session: separate local session logic
[vpp.git] / src / vnet / session / application_worker.c
1 /*
2  * Copyright (c) 2019 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/session/application.h>
17 #include <vnet/session/application_interface.h>
18 #include <vnet/session/application_local.h>
19 #include <vnet/session/session.h>
20
21 /**
22  * Pool of workers associated to apps
23  */
24 static app_worker_t *app_workers;
25
26 app_worker_t *
27 app_worker_alloc (application_t * app)
28 {
29   app_worker_t *app_wrk;
30   pool_get (app_workers, app_wrk);
31   clib_memset (app_wrk, 0, sizeof (*app_wrk));
32   app_wrk->wrk_index = app_wrk - app_workers;
33   app_wrk->app_index = app->app_index;
34   app_wrk->wrk_map_index = ~0;
35   app_wrk->connects_seg_manager = APP_INVALID_SEGMENT_MANAGER_INDEX;
36   app_wrk->first_segment_manager = APP_INVALID_SEGMENT_MANAGER_INDEX;
37   app_wrk->local_segment_manager = APP_INVALID_SEGMENT_MANAGER_INDEX;
38   APP_DBG ("New app %v worker %u", app_get_name (app), app_wrk->wrk_index);
39   return app_wrk;
40 }
41
42 app_worker_t *
43 app_worker_get (u32 wrk_index)
44 {
45   return pool_elt_at_index (app_workers, wrk_index);
46 }
47
48 app_worker_t *
49 app_worker_get_if_valid (u32 wrk_index)
50 {
51   if (pool_is_free_index (app_workers, wrk_index))
52     return 0;
53   return pool_elt_at_index (app_workers, wrk_index);
54 }
55
56 void
57 app_worker_free (app_worker_t * app_wrk)
58 {
59   application_t *app = application_get (app_wrk->app_index);
60   vnet_unlisten_args_t _a, *a = &_a;
61   u64 handle, *handles = 0;
62   segment_manager_t *sm;
63   u32 sm_index;
64   int i;
65   app_listener_t *al;
66   session_t *ls;
67
68   /*
69    *  Listener cleanup
70    */
71
72   /* *INDENT-OFF* */
73   hash_foreach (handle, sm_index, app_wrk->listeners_table, ({
74     ls = listen_session_get_from_handle (handle);
75     al = app_listener_get (app, ls->al_index);
76     vec_add1 (handles, app_listener_handle (al));
77     sm = segment_manager_get (sm_index);
78     sm->app_wrk_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
79   }));
80   /* *INDENT-ON* */
81
82   for (i = 0; i < vec_len (handles); i++)
83     {
84       a->app_index = app->app_index;
85       a->wrk_map_index = app_wrk->wrk_map_index;
86       a->handle = handles[i];
87       /* seg manager is removed when unbind completes */
88       (void) vnet_unlisten (a);
89     }
90
91   /*
92    * Connects segment manager cleanup
93    */
94
95   if (app_wrk->connects_seg_manager != APP_INVALID_SEGMENT_MANAGER_INDEX)
96     {
97       sm = segment_manager_get (app_wrk->connects_seg_manager);
98       sm->app_wrk_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
99       sm->first_is_protected = 0;
100       segment_manager_init_del (sm);
101     }
102
103   /* If first segment manager is used by a listener */
104   if (app_wrk->first_segment_manager != APP_INVALID_SEGMENT_MANAGER_INDEX
105       && app_wrk->first_segment_manager != app_wrk->connects_seg_manager)
106     {
107       sm = segment_manager_get (app_wrk->first_segment_manager);
108       sm->first_is_protected = 0;
109       sm->app_wrk_index = SEGMENT_MANAGER_INVALID_APP_INDEX;
110       /* .. and has no fifos, e.g. it might be used for redirected sessions,
111        * remove it */
112       if (!segment_manager_has_fifos (sm))
113         segment_manager_del (sm);
114     }
115
116   /*
117    * Local sessions
118    */
119   app_worker_local_sessions_free (app_wrk);
120
121   pool_put (app_workers, app_wrk);
122   if (CLIB_DEBUG)
123     clib_memset (app_wrk, 0xfe, sizeof (*app_wrk));
124 }
125
126 application_t *
127 app_worker_get_app (u32 wrk_index)
128 {
129   app_worker_t *app_wrk;
130   app_wrk = app_worker_get_if_valid (wrk_index);
131   if (!app_wrk)
132     return 0;
133   return application_get_if_valid (app_wrk->app_index);
134 }
135
136 static segment_manager_t *
137 app_worker_alloc_segment_manager (app_worker_t * app_wrk)
138 {
139   segment_manager_t *sm = 0;
140
141   /* If the first segment manager is not in use, don't allocate a new one */
142   if (app_wrk->first_segment_manager != APP_INVALID_SEGMENT_MANAGER_INDEX
143       && app_wrk->first_segment_manager_in_use == 0)
144     {
145       sm = segment_manager_get (app_wrk->first_segment_manager);
146       app_wrk->first_segment_manager_in_use = 1;
147       return sm;
148     }
149
150   sm = segment_manager_new ();
151   sm->app_wrk_index = app_wrk->wrk_index;
152
153   return sm;
154 }
155
156 static int
157 app_worker_alloc_session_fifos (segment_manager_t * sm, session_t * s)
158 {
159   svm_fifo_t *rx_fifo = 0, *tx_fifo = 0;
160   u32 fifo_segment_index;
161   int rv;
162
163   if ((rv = segment_manager_alloc_session_fifos (sm, &rx_fifo, &tx_fifo,
164                                                  &fifo_segment_index)))
165     return rv;
166
167   rx_fifo->master_session_index = s->session_index;
168   rx_fifo->master_thread_index = s->thread_index;
169
170   tx_fifo->master_session_index = s->session_index;
171   tx_fifo->master_thread_index = s->thread_index;
172
173   s->rx_fifo = rx_fifo;
174   s->tx_fifo = tx_fifo;
175   s->svm_segment_index = fifo_segment_index;
176   return 0;
177 }
178
179 int
180 app_worker_start_listen (app_worker_t * app_wrk,
181                          app_listener_t * app_listener)
182 {
183   segment_manager_t *sm;
184   session_t *ls;
185
186   if (clib_bitmap_get (app_listener->workers, app_wrk->wrk_map_index))
187     return VNET_API_ERROR_ADDRESS_IN_USE;
188
189   app_listener->workers = clib_bitmap_set (app_listener->workers,
190                                            app_wrk->wrk_map_index, 1);
191
192   if (app_listener->session_index == SESSION_INVALID_INDEX)
193     return 0;
194
195   ls = session_get (app_listener->session_index, 0);
196
197   /* Allocate segment manager. All sessions derived out of a listen session
198    * have fifos allocated by the same segment manager. */
199   if (!(sm = app_worker_alloc_segment_manager (app_wrk)))
200     return -1;
201
202   /* Keep track of the segment manager for the listener or this worker */
203   hash_set (app_wrk->listeners_table, listen_session_get_handle (ls),
204             segment_manager_index (sm));
205
206   if (session_transport_service_type (ls) == TRANSPORT_SERVICE_CL)
207     {
208       if (!ls->rx_fifo && app_worker_alloc_session_fifos (sm, ls))
209         return -1;
210     }
211   return 0;
212 }
213
214 int
215 app_worker_stop_listen (app_worker_t * app_wrk, app_listener_t * al)
216 {
217   session_handle_t handle;
218   segment_manager_t *sm;
219   uword *sm_indexp;
220
221   if (!clib_bitmap_get (al->workers, app_wrk->wrk_map_index))
222     return 0;
223
224   if (al->session_index != SESSION_INVALID_INDEX)
225     {
226       session_t *ls;
227
228       ls = listen_session_get (al->session_index);
229       handle = listen_session_get_handle (ls);
230
231       sm_indexp = hash_get (app_wrk->listeners_table, handle);
232       if (PREDICT_FALSE (!sm_indexp))
233         {
234           clib_warning ("listener handle was removed %llu!", handle);
235           return -1;
236         }
237
238       sm = segment_manager_get (*sm_indexp);
239       if (app_wrk->first_segment_manager == *sm_indexp)
240         {
241           /* Delete sessions but don't remove segment manager */
242           app_wrk->first_segment_manager_in_use = 0;
243           segment_manager_del_sessions (sm);
244         }
245       else
246         {
247           segment_manager_init_del (sm);
248         }
249       hash_unset (app_wrk->listeners_table, handle);
250     }
251
252   if (al->local_index != SESSION_INVALID_INDEX)
253     {
254       local_session_t *ll, *ls;
255       application_t *app;
256
257       app = application_get (app_wrk->app_index);
258       ll = application_get_local_listen_session (app, al->local_index);
259
260       /* *INDENT-OFF* */
261       pool_foreach (ls, app_wrk->local_sessions, ({
262         if (ls->listener_index == ll->session_index)
263           app_worker_local_session_disconnect (app_wrk->wrk_index, ls);
264       }));
265       /* *INDENT-ON* */
266     }
267
268   clib_bitmap_set_no_check (al->workers, app_wrk->wrk_map_index, 0);
269   if (clib_bitmap_is_zero (al->workers))
270     app_listener_cleanup (al);
271
272   return 0;
273 }
274
275 int
276 app_worker_init_accepted (session_t * s)
277 {
278   app_worker_t *app_wrk;
279   segment_manager_t *sm;
280   session_t *listener;
281
282   listener = listen_session_get (s->listener_index);
283   app_wrk = application_listener_select_worker (listener);
284   s->app_wrk_index = app_wrk->wrk_index;
285   sm = app_worker_get_listen_segment_manager (app_wrk, listener);
286   return app_worker_alloc_session_fifos (sm, s);
287 }
288
289 int
290 app_worker_accept_notify (app_worker_t * app_wrk, session_t * s)
291 {
292   application_t *app = application_get (app_wrk->app_index);
293   return app->cb_fns.session_accept_callback (s);
294 }
295
296 int
297 app_worker_init_connected (app_worker_t * app_wrk, session_t * s)
298 {
299   application_t *app = application_get (app_wrk->app_index);
300   segment_manager_t *sm;
301
302   /* Allocate fifos for session, unless the app is a builtin proxy */
303   if (!application_is_builtin_proxy (app))
304     {
305       sm = app_worker_get_connect_segment_manager (app_wrk);
306       if (app_worker_alloc_session_fifos (sm, s))
307         return -1;
308     }
309   return 0;
310 }
311
312 int
313 app_worker_connect_notify (app_worker_t * app_wrk, session_t * s, u32 opaque)
314 {
315   application_t *app = application_get (app_wrk->app_index);
316   return app->cb_fns.session_connected_callback (app_wrk->wrk_index, opaque,
317                                                  s, s == 0 /* is_fail */ );
318 }
319
320 int
321 app_worker_own_session (app_worker_t * app_wrk, session_t * s)
322 {
323   segment_manager_t *sm;
324   svm_fifo_t *rxf, *txf;
325
326   if (s->session_state == SESSION_STATE_LISTENING)
327     return application_change_listener_owner (s, app_wrk);
328
329   s->app_wrk_index = app_wrk->wrk_index;
330
331   rxf = s->rx_fifo;
332   txf = s->tx_fifo;
333
334   if (!rxf || !txf)
335     return 0;
336
337   s->rx_fifo = 0;
338   s->tx_fifo = 0;
339
340   sm = app_worker_get_or_alloc_connect_segment_manager (app_wrk);
341   if (app_worker_alloc_session_fifos (sm, s))
342     return -1;
343
344   if (!svm_fifo_is_empty (rxf))
345     {
346       clib_memcpy_fast (s->rx_fifo->data, rxf->data, rxf->nitems);
347       s->rx_fifo->head = rxf->head;
348       s->rx_fifo->tail = rxf->tail;
349       s->rx_fifo->cursize = rxf->cursize;
350     }
351
352   if (!svm_fifo_is_empty (txf))
353     {
354       clib_memcpy_fast (s->tx_fifo->data, txf->data, txf->nitems);
355       s->tx_fifo->head = txf->head;
356       s->tx_fifo->tail = txf->tail;
357       s->tx_fifo->cursize = txf->cursize;
358     }
359
360   segment_manager_dealloc_fifos (rxf->segment_index, rxf, txf);
361
362   return 0;
363 }
364
365 int
366 app_worker_connect_session (app_worker_t * app, session_endpoint_t * sep,
367                             u32 api_context)
368 {
369   int rv;
370
371   /* Make sure we have a segment manager for connects */
372   app_worker_alloc_connects_segment_manager (app);
373
374   if ((rv = session_open (app->wrk_index, sep, api_context)))
375     return rv;
376
377   return 0;
378 }
379
380 int
381 app_worker_alloc_connects_segment_manager (app_worker_t * app_wrk)
382 {
383   segment_manager_t *sm;
384
385   if (app_wrk->connects_seg_manager == APP_INVALID_SEGMENT_MANAGER_INDEX)
386     {
387       sm = app_worker_alloc_segment_manager (app_wrk);
388       if (sm == 0)
389         return -1;
390       app_wrk->connects_seg_manager = segment_manager_index (sm);
391     }
392   return 0;
393 }
394
395 segment_manager_t *
396 app_worker_get_connect_segment_manager (app_worker_t * app)
397 {
398   ASSERT (app->connects_seg_manager != (u32) ~ 0);
399   return segment_manager_get (app->connects_seg_manager);
400 }
401
402 segment_manager_t *
403 app_worker_get_or_alloc_connect_segment_manager (app_worker_t * app_wrk)
404 {
405   if (app_wrk->connects_seg_manager == (u32) ~ 0)
406     app_worker_alloc_connects_segment_manager (app_wrk);
407   return segment_manager_get (app_wrk->connects_seg_manager);
408 }
409
410 segment_manager_t *
411 app_worker_get_listen_segment_manager (app_worker_t * app,
412                                        session_t * listener)
413 {
414   uword *smp;
415   smp = hash_get (app->listeners_table, listen_session_get_handle (listener));
416   ASSERT (smp != 0);
417   return segment_manager_get (*smp);
418 }
419
420 session_t *
421 app_worker_first_listener (app_worker_t * app_wrk, u8 fib_proto,
422                            u8 transport_proto)
423 {
424   session_t *listener;
425   u64 handle;
426   u32 sm_index;
427   u8 sst;
428
429   sst = session_type_from_proto_and_ip (transport_proto,
430                                         fib_proto == FIB_PROTOCOL_IP4);
431
432   /* *INDENT-OFF* */
433    hash_foreach (handle, sm_index, app_wrk->listeners_table, ({
434      listener = listen_session_get_from_handle (handle);
435      if (listener->session_type == sst
436          && listener->enqueue_epoch != SESSION_PROXY_LISTENER_INDEX)
437        return listener;
438    }));
439   /* *INDENT-ON* */
440
441   return 0;
442 }
443
444 session_t *
445 app_worker_proxy_listener (app_worker_t * app_wrk, u8 fib_proto,
446                            u8 transport_proto)
447 {
448   session_t *listener;
449   u64 handle;
450   u32 sm_index;
451   u8 sst;
452
453   sst = session_type_from_proto_and_ip (transport_proto,
454                                         fib_proto == FIB_PROTOCOL_IP4);
455
456   /* *INDENT-OFF* */
457    hash_foreach (handle, sm_index, app_wrk->listeners_table, ({
458      listener = listen_session_get_from_handle (handle);
459      if (listener->session_type == sst
460          && listener->enqueue_epoch == SESSION_PROXY_LISTENER_INDEX)
461        return listener;
462    }));
463   /* *INDENT-ON* */
464
465   return 0;
466 }
467
468 /**
469  * Send an API message to the external app, to map new segment
470  */
471 int
472 app_worker_add_segment_notify (u32 app_wrk_index, u64 segment_handle)
473 {
474   app_worker_t *app_wrk = app_worker_get (app_wrk_index);
475   application_t *app = application_get (app_wrk->app_index);
476   return app->cb_fns.add_segment_callback (app_wrk->api_client_index,
477                                            segment_handle);
478 }
479
480 u8
481 app_worker_application_is_builtin (app_worker_t * app_wrk)
482 {
483   return app_wrk->app_is_builtin;
484 }
485
486 static inline int
487 app_enqueue_evt (svm_msg_q_t * mq, svm_msg_q_msg_t * msg, u8 lock)
488 {
489   if (PREDICT_FALSE (svm_msg_q_is_full (mq)))
490     {
491       clib_warning ("evt q full");
492       svm_msg_q_free_msg (mq, msg);
493       if (lock)
494         svm_msg_q_unlock (mq);
495       return -1;
496     }
497
498   if (lock)
499     {
500       svm_msg_q_add_and_unlock (mq, msg);
501       return 0;
502     }
503
504   /* Even when not locking the ring, we must wait for queue mutex */
505   if (svm_msg_q_add (mq, msg, SVM_Q_WAIT))
506     {
507       clib_warning ("msg q add returned");
508       return -1;
509     }
510   return 0;
511 }
512
513 static inline int
514 app_send_io_evt_rx (app_worker_t * app_wrk, session_t * s, u8 lock)
515 {
516   session_event_t *evt;
517   svm_msg_q_msg_t msg;
518   svm_msg_q_t *mq;
519
520   if (PREDICT_FALSE (s->session_state != SESSION_STATE_READY
521                      && s->session_state != SESSION_STATE_LISTENING))
522     {
523       /* Session is closed so app will never clean up. Flush rx fifo */
524       if (s->session_state == SESSION_STATE_CLOSED)
525         svm_fifo_dequeue_drop_all (s->rx_fifo);
526       return 0;
527     }
528
529   if (app_worker_application_is_builtin (app_wrk))
530     {
531       application_t *app = application_get (app_wrk->app_index);
532       return app->cb_fns.builtin_app_rx_callback (s);
533     }
534
535   if (svm_fifo_has_event (s->rx_fifo) || svm_fifo_is_empty (s->rx_fifo))
536     return 0;
537
538   mq = app_wrk->event_queue;
539   if (lock)
540     svm_msg_q_lock (mq);
541
542   if (PREDICT_FALSE (svm_msg_q_ring_is_full (mq, SESSION_MQ_IO_EVT_RING)))
543     {
544       clib_warning ("evt q rings full");
545       if (lock)
546         svm_msg_q_unlock (mq);
547       return -1;
548     }
549
550   msg = svm_msg_q_alloc_msg_w_ring (mq, SESSION_MQ_IO_EVT_RING);
551   ASSERT (!svm_msg_q_msg_is_invalid (&msg));
552
553   evt = (session_event_t *) svm_msg_q_msg_data (mq, &msg);
554   evt->fifo = s->rx_fifo;
555   evt->event_type = FIFO_EVENT_APP_RX;
556
557   (void) svm_fifo_set_event (s->rx_fifo);
558
559   if (app_enqueue_evt (mq, &msg, lock))
560     return -1;
561   return 0;
562 }
563
564 static inline int
565 app_send_io_evt_tx (app_worker_t * app_wrk, session_t * s, u8 lock)
566 {
567   svm_msg_q_t *mq;
568   session_event_t *evt;
569   svm_msg_q_msg_t msg;
570
571   if (app_worker_application_is_builtin (app_wrk))
572     return 0;
573
574   mq = app_wrk->event_queue;
575   if (lock)
576     svm_msg_q_lock (mq);
577
578   if (PREDICT_FALSE (svm_msg_q_ring_is_full (mq, SESSION_MQ_IO_EVT_RING)))
579     {
580       clib_warning ("evt q rings full");
581       if (lock)
582         svm_msg_q_unlock (mq);
583       return -1;
584     }
585
586   msg = svm_msg_q_alloc_msg_w_ring (mq, SESSION_MQ_IO_EVT_RING);
587   ASSERT (!svm_msg_q_msg_is_invalid (&msg));
588
589   evt = (session_event_t *) svm_msg_q_msg_data (mq, &msg);
590   evt->event_type = FIFO_EVENT_APP_TX;
591   evt->fifo = s->tx_fifo;
592
593   return app_enqueue_evt (mq, &msg, lock);
594 }
595
596 /* *INDENT-OFF* */
597 typedef int (app_send_evt_handler_fn) (app_worker_t *app,
598                                        session_t *s,
599                                        u8 lock);
600 static app_send_evt_handler_fn * const app_send_evt_handler_fns[3] = {
601     app_send_io_evt_rx,
602     0,
603     app_send_io_evt_tx,
604 };
605 /* *INDENT-ON* */
606
607 /**
608  * Send event to application
609  *
610  * Logic from queue perspective is non-blocking. If there's
611  * not enough space to enqueue a message, we return.
612  */
613 int
614 app_worker_send_event (app_worker_t * app, session_t * s, u8 evt_type)
615 {
616   ASSERT (app && evt_type <= FIFO_EVENT_APP_TX);
617   return app_send_evt_handler_fns[evt_type] (app, s, 0 /* lock */ );
618 }
619
620 /**
621  * Send event to application
622  *
623  * Logic from queue perspective is blocking. However, if queue is full,
624  * we return.
625  */
626 int
627 app_worker_lock_and_send_event (app_worker_t * app, session_t * s,
628                                 u8 evt_type)
629 {
630   return app_send_evt_handler_fns[evt_type] (app, s, 1 /* lock */ );
631 }
632
633 segment_manager_t *
634 app_worker_get_local_segment_manager (app_worker_t * app_worker)
635 {
636   return segment_manager_get (app_worker->local_segment_manager);
637 }
638
639 segment_manager_t *
640 app_worker_get_local_segment_manager_w_session (app_worker_t * app_wrk,
641                                                 local_session_t * ls)
642 {
643   session_t *listener;
644   if (application_local_session_listener_has_transport (ls))
645     {
646       listener = listen_session_get (ls->listener_index);
647       return app_worker_get_listen_segment_manager (app_wrk, listener);
648     }
649   return segment_manager_get (app_wrk->local_segment_manager);
650 }
651
652 u8 *
653 format_app_worker_listener (u8 * s, va_list * args)
654 {
655   app_worker_t *app_wrk = va_arg (*args, app_worker_t *);
656   u64 handle = va_arg (*args, u64);
657   u32 sm_index = va_arg (*args, u32);
658   int verbose = va_arg (*args, int);
659   session_t *listener;
660   const u8 *app_name;
661   u8 *str;
662
663   if (!app_wrk)
664     {
665       if (verbose)
666         s = format (s, "%-40s%-25s%=10s%-15s%-15s%-10s", "Connection", "App",
667                     "Wrk", "API Client", "ListenerID", "SegManager");
668       else
669         s = format (s, "%-40s%-25s%=10s", "Connection", "App", "Wrk");
670
671       return s;
672     }
673
674   app_name = application_name_from_index (app_wrk->app_index);
675   listener = listen_session_get_from_handle (handle);
676   str = format (0, "%U", format_stream_session, listener, verbose);
677
678   if (verbose)
679     {
680       char buf[32];
681       sprintf (buf, "%u(%u)", app_wrk->wrk_map_index, app_wrk->wrk_index);
682       s = format (s, "%-40s%-25s%=10s%-15u%-15u%-10u", str, app_name,
683                   buf, app_wrk->api_client_index, handle, sm_index);
684     }
685   else
686     s = format (s, "%-40s%-25s%=10u", str, app_name, app_wrk->wrk_map_index);
687
688   return s;
689 }
690
691 u8 *
692 format_app_worker (u8 * s, va_list * args)
693 {
694   app_worker_t *app_wrk = va_arg (*args, app_worker_t *);
695   u32 indent = 1;
696
697   s = format (s, "%U wrk-index %u app-index %u map-index %u "
698               "api-client-index %d\n", format_white_space, indent,
699               app_wrk->wrk_index, app_wrk->app_index, app_wrk->wrk_map_index,
700               app_wrk->api_client_index);
701   return s;
702 }
703
704 void
705 app_worker_format_connects (app_worker_t * app_wrk, int verbose)
706 {
707   svm_fifo_segment_private_t *fifo_segment;
708   vlib_main_t *vm = vlib_get_main ();
709   segment_manager_t *sm;
710   const u8 *app_name;
711   u8 *s = 0;
712
713   /* Header */
714   if (!app_wrk)
715     {
716       if (verbose)
717         vlib_cli_output (vm, "%-40s%-20s%-15s%-10s", "Connection", "App",
718                          "API Client", "SegManager");
719       else
720         vlib_cli_output (vm, "%-40s%-20s", "Connection", "App");
721       return;
722     }
723
724   if (app_wrk->connects_seg_manager == (u32) ~ 0)
725     return;
726
727   app_name = application_name_from_index (app_wrk->app_index);
728
729   /* Across all fifo segments */
730   sm = segment_manager_get (app_wrk->connects_seg_manager);
731
732   /* *INDENT-OFF* */
733   segment_manager_foreach_segment_w_lock (fifo_segment, sm, ({
734     svm_fifo_t *fifo;
735     u8 *str;
736
737     fifo = svm_fifo_segment_get_fifo_list (fifo_segment);
738     while (fifo)
739       {
740         u32 session_index, thread_index;
741         session_t *session;
742
743         session_index = fifo->master_session_index;
744         thread_index = fifo->master_thread_index;
745
746         session = session_get (session_index, thread_index);
747         str = format (0, "%U", format_stream_session, session, verbose);
748
749         if (verbose)
750           s = format (s, "%-40s%-20s%-15u%-10u", str, app_name,
751                       app_wrk->api_client_index, app_wrk->connects_seg_manager);
752         else
753           s = format (s, "%-40s%-20s", str, app_name);
754
755         vlib_cli_output (vm, "%v", s);
756         vec_reset_length (s);
757         vec_free (str);
758
759         fifo = fifo->next;
760       }
761     vec_free (s);
762   }));
763   /* *INDENT-ON* */
764 }
765
766 /*
767  * fd.io coding-style-patch-verification: ON
768  *
769  * Local Variables:
770  * eval: (c-set-style "gnu")
771  * End:
772  */