api: API trace improvements
[vpp.git] / src / vlibmemory / socket_client.c
1 /*
2  *------------------------------------------------------------------
3  * socket_client.c - API message handling over sockets, client code.
4  *
5  * Copyright (c) 2017 Cisco and/or its affiliates.
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at:
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *------------------------------------------------------------------
18  */
19
20 #include <stdio.h>
21 #define __USE_GNU
22 #define _GNU_SOURCE
23 #include <sys/socket.h>
24
25 #include <svm/ssvm.h>
26 #include <vlibmemory/socket_client.h>
27 #include <vlibmemory/memory_client.h>
28
29 #include <vlibmemory/vl_memory_msg_enum.h>
30
31 #define vl_typedefs             /* define message structures */
32 #include <vlibmemory/vl_memory_api_h.h>
33 #undef vl_typedefs
34
35 #define vl_endianfun            /* define message structures */
36 #include <vlibmemory/vl_memory_api_h.h>
37 #undef vl_endianfun
38
39 /* instantiate all the print functions we know about */
40 #define vl_print(handle, ...) clib_warning (__VA_ARGS__)
41 #define vl_printfun
42 #include <vlibmemory/vl_memory_api_h.h>
43 #undef vl_printfun
44
45 socket_client_main_t socket_client_main;
46 __thread socket_client_main_t *socket_client_ctx = &socket_client_main;
47
48 /* Debug aid */
49 u32 vl (void *p) __attribute__ ((weak));
50
51 u32
52 vl (void *p)
53 {
54   return vec_len (p);
55 }
56
57 static socket_client_main_t *
58 vl_socket_client_ctx_push (socket_client_main_t * ctx)
59 {
60   socket_client_main_t *old = socket_client_ctx;
61   socket_client_ctx = ctx;
62   return old;
63 }
64
65 static void
66 vl_socket_client_ctx_pop (socket_client_main_t * old_ctx)
67 {
68   socket_client_ctx = old_ctx;
69 }
70
71 static int
72 vl_socket_client_read_internal (socket_client_main_t * scm, int wait)
73 {
74   u32 data_len = 0, msg_size;
75   int n, current_rx_index;
76   msgbuf_t *mbp = 0;
77   f64 timeout;
78
79   if (scm->socket_fd == 0)
80     return -1;
81
82   if (wait)
83     timeout = clib_time_now (&scm->clib_time) + wait;
84
85   while (1)
86     {
87       while (vec_len (scm->socket_rx_buffer) < sizeof (*mbp))
88         {
89           current_rx_index = vec_len (scm->socket_rx_buffer);
90           vec_validate (scm->socket_rx_buffer, current_rx_index
91                         + scm->socket_buffer_size - 1);
92           _vec_len (scm->socket_rx_buffer) = current_rx_index;
93           n = read (scm->socket_fd, scm->socket_rx_buffer + current_rx_index,
94                     scm->socket_buffer_size);
95           if (n < 0)
96             {
97               if (errno == EAGAIN)
98                 continue;
99
100               clib_unix_warning ("socket_read");
101               return -1;
102             }
103           _vec_len (scm->socket_rx_buffer) += n;
104         }
105
106 #if CLIB_DEBUG > 1
107       if (n > 0)
108         clib_warning ("read %d bytes", n);
109 #endif
110
111       mbp = (msgbuf_t *) (scm->socket_rx_buffer);
112       data_len = ntohl (mbp->data_len);
113       current_rx_index = vec_len (scm->socket_rx_buffer);
114       vec_validate (scm->socket_rx_buffer, current_rx_index + data_len);
115       _vec_len (scm->socket_rx_buffer) = current_rx_index;
116       mbp = (msgbuf_t *) (scm->socket_rx_buffer);
117       msg_size = data_len + sizeof (*mbp);
118
119       while (vec_len (scm->socket_rx_buffer) < msg_size)
120         {
121           n = read (scm->socket_fd,
122                     scm->socket_rx_buffer + vec_len (scm->socket_rx_buffer),
123                     msg_size - vec_len (scm->socket_rx_buffer));
124           if (n < 0)
125             {
126               if (errno == EAGAIN)
127                 continue;
128
129               clib_unix_warning ("socket_read");
130               return -1;
131             }
132           _vec_len (scm->socket_rx_buffer) += n;
133         }
134
135       if (vec_len (scm->socket_rx_buffer) >= data_len + sizeof (*mbp))
136         {
137           vl_msg_api_socket_handler ((void *) (mbp->data));
138
139           if (vec_len (scm->socket_rx_buffer) == data_len + sizeof (*mbp))
140             _vec_len (scm->socket_rx_buffer) = 0;
141           else
142             vec_delete (scm->socket_rx_buffer, data_len + sizeof (*mbp), 0);
143           mbp = 0;
144
145           /* Quit if we're out of data, and not expecting a ping reply */
146           if (vec_len (scm->socket_rx_buffer) == 0
147               && scm->control_pings_outstanding == 0)
148             break;
149         }
150       if (wait && clib_time_now (&scm->clib_time) >= timeout)
151         return -1;
152     }
153   return 0;
154 }
155
156 int
157 vl_socket_client_read (int wait)
158 {
159   return vl_socket_client_read_internal (socket_client_ctx, wait);
160 }
161
162 int
163 vl_socket_client_read2 (socket_client_main_t * scm, int wait)
164 {
165   socket_client_main_t *old_ctx;
166   int rv;
167
168   old_ctx = vl_socket_client_ctx_push (scm);
169   rv = vl_socket_client_read_internal (scm, wait);
170   vl_socket_client_ctx_pop (old_ctx);
171   return rv;
172 }
173
174 static int
175 vl_socket_client_write_internal (socket_client_main_t * scm)
176 {
177   int n;
178
179   msgbuf_t msgbuf = {
180     .q = 0,
181     .gc_mark_timestamp = 0,
182     .data_len = htonl (scm->socket_tx_nbytes),
183   };
184
185   n = write (scm->socket_fd, &msgbuf, sizeof (msgbuf));
186   if (n < sizeof (msgbuf))
187     {
188       clib_unix_warning ("socket write (msgbuf)");
189       return -1;
190     }
191
192   n = write (scm->socket_fd, scm->socket_tx_buffer, scm->socket_tx_nbytes);
193   if (n < scm->socket_tx_nbytes)
194     {
195       clib_unix_warning ("socket write (msg)");
196       return -1;
197     }
198
199   return n;
200 }
201
202 int
203 vl_socket_client_write (void)
204 {
205   return vl_socket_client_write_internal (socket_client_ctx);
206 }
207
208 int
209 vl_socket_client_write2 (socket_client_main_t * scm)
210 {
211   socket_client_main_t *old_ctx;
212   int rv;
213
214   old_ctx = vl_socket_client_ctx_push (scm);
215   rv = vl_socket_client_write_internal (scm);
216   vl_socket_client_ctx_pop (old_ctx);
217   return rv;
218 }
219
220 void *
221 vl_socket_client_msg_alloc2 (socket_client_main_t * scm, int nbytes)
222 {
223   scm->socket_tx_nbytes = nbytes;
224   return ((void *) scm->socket_tx_buffer);
225 }
226
227 void *
228 vl_socket_client_msg_alloc (int nbytes)
229 {
230   return vl_socket_client_msg_alloc2 (socket_client_ctx, nbytes);
231 }
232
233 void
234 vl_socket_client_disconnect2 (socket_client_main_t * scm)
235 {
236   if (vl_mem_client_is_connected ())
237     {
238       vl_client_disconnect_from_vlib_no_unmap ();
239       ssvm_delete_memfd (&scm->memfd_segment);
240     }
241   if (scm->socket_fd && (close (scm->socket_fd) < 0))
242     clib_unix_warning ("close");
243   scm->socket_fd = 0;
244 }
245
246 void
247 vl_socket_client_disconnect (void)
248 {
249   vl_socket_client_disconnect2 (socket_client_ctx);
250 }
251
252 void
253 vl_socket_client_enable_disable2 (socket_client_main_t * scm, int enable)
254 {
255   scm->socket_enable = enable;
256 }
257
258 void
259 vl_socket_client_enable_disable (int enable)
260 {
261   vl_socket_client_enable_disable2 (socket_client_ctx, enable);
262 }
263
264 static clib_error_t *
265 vl_sock_api_recv_fd_msg_internal (socket_client_main_t * scm, int fds[],
266                                   int n_fds, u32 wait)
267 {
268   char msgbuf[16];
269   char ctl[CMSG_SPACE (sizeof (int) * n_fds)
270            + CMSG_SPACE (sizeof (struct ucred))];
271   struct msghdr mh = { 0 };
272   struct iovec iov[1];
273   ssize_t size = 0;
274   struct ucred *cr = 0;
275   struct cmsghdr *cmsg;
276   pid_t pid __attribute__ ((unused));
277   uid_t uid __attribute__ ((unused));
278   gid_t gid __attribute__ ((unused));
279   int socket_fd;
280   f64 timeout;
281
282   socket_fd = scm->client_socket.fd;
283
284   iov[0].iov_base = msgbuf;
285   iov[0].iov_len = 5;
286   mh.msg_iov = iov;
287   mh.msg_iovlen = 1;
288   mh.msg_control = ctl;
289   mh.msg_controllen = sizeof (ctl);
290
291   clib_memset (ctl, 0, sizeof (ctl));
292
293   if (wait != ~0)
294     {
295       timeout = clib_time_now (&scm->clib_time) + wait;
296       while (size != 5 && clib_time_now (&scm->clib_time) < timeout)
297         size = recvmsg (socket_fd, &mh, MSG_DONTWAIT);
298     }
299   else
300     size = recvmsg (socket_fd, &mh, 0);
301
302   if (size != 5)
303     {
304       return (size == 0) ? clib_error_return (0, "disconnected") :
305         clib_error_return_unix (0, "recvmsg: malformed message (fd %d)",
306                                 socket_fd);
307     }
308
309   cmsg = CMSG_FIRSTHDR (&mh);
310   while (cmsg)
311     {
312       if (cmsg->cmsg_level == SOL_SOCKET)
313         {
314           if (cmsg->cmsg_type == SCM_CREDENTIALS)
315             {
316               cr = (struct ucred *) CMSG_DATA (cmsg);
317               uid = cr->uid;
318               gid = cr->gid;
319               pid = cr->pid;
320             }
321           else if (cmsg->cmsg_type == SCM_RIGHTS)
322             {
323               clib_memcpy_fast (fds, CMSG_DATA (cmsg), sizeof (int) * n_fds);
324             }
325         }
326       cmsg = CMSG_NXTHDR (&mh, cmsg);
327     }
328   return 0;
329 }
330
331 clib_error_t *
332 vl_sock_api_recv_fd_msg (int socket_fd, int fds[], int n_fds, u32 wait)
333 {
334   return vl_sock_api_recv_fd_msg_internal (socket_client_ctx, fds, n_fds,
335                                            wait);
336 }
337
338 clib_error_t *
339 vl_sock_api_recv_fd_msg2 (socket_client_main_t * scm, int socket_fd,
340                           int fds[], int n_fds, u32 wait)
341 {
342   socket_client_main_t *old_ctx;
343   clib_error_t *error;
344
345   old_ctx = vl_socket_client_ctx_push (scm);
346   error = vl_sock_api_recv_fd_msg_internal (scm, fds, n_fds, wait);
347   vl_socket_client_ctx_pop (old_ctx);
348   return error;
349 }
350
351 static void vl_api_sock_init_shm_reply_t_handler
352   (vl_api_sock_init_shm_reply_t * mp)
353 {
354   socket_client_main_t *scm = socket_client_ctx;
355   ssvm_private_t *memfd = &scm->memfd_segment;
356   i32 retval = ntohl (mp->retval);
357   api_main_t *am = vlibapi_get_main ();
358   clib_error_t *error;
359   int my_fd = -1;
360   u8 *new_name;
361
362   if (retval)
363     {
364       clib_warning ("failed to init shmem");
365       return;
366     }
367
368   /*
369    * Check the socket for the magic fd
370    */
371   error = vl_sock_api_recv_fd_msg (scm->socket_fd, &my_fd, 1, 5);
372   if (error)
373     {
374       clib_error_report (error);
375       retval = -99;
376       return;
377     }
378
379   clib_memset (memfd, 0, sizeof (*memfd));
380   memfd->fd = my_fd;
381
382   /* Note: this closes memfd.fd */
383   retval = ssvm_client_init_memfd (memfd);
384   if (retval)
385     clib_warning ("WARNING: segment map returned %d", retval);
386
387   /*
388    * Pivot to the memory client segment that vpp just created
389    */
390   am->vlib_rp = (void *) (memfd->requested_va + MMAP_PAGESIZE);
391   am->shmem_hdr = (void *) am->vlib_rp->user_ctx;
392
393   new_name = format (0, "%v[shm]%c", scm->name, 0);
394   vl_client_install_client_message_handlers ();
395   if (scm->want_shm_pthread)
396     {
397       vl_client_connect_to_vlib_no_map ("pvt", (char *) new_name,
398                                         32 /* input_queue_length */ );
399     }
400   else
401     {
402       vl_client_connect_to_vlib_no_rx_pthread_no_map ("pvt",
403                                                       (char *) new_name, 32
404                                                       /* input_queue_length */
405         );
406     }
407   vl_socket_client_enable_disable (0);
408   vec_free (new_name);
409 }
410
411 static void
412 vl_api_sockclnt_create_reply_t_handler (vl_api_sockclnt_create_reply_t * mp)
413 {
414   socket_client_main_t *scm = socket_client_ctx;
415   if (!mp->response)
416     {
417       scm->socket_enable = 1;
418       scm->client_index = clib_net_to_host_u32 (mp->index);
419     }
420 }
421
422 #define foreach_sock_client_api_msg                             \
423 _(SOCKCLNT_CREATE_REPLY, sockclnt_create_reply)                 \
424 _(SOCK_INIT_SHM_REPLY, sock_init_shm_reply)                     \
425
426 static void
427 noop_handler (void *notused)
428 {
429 }
430
431 void
432 vl_sock_client_install_message_handlers (void)
433 {
434
435 #define _(N, n)                                                               \
436   vl_msg_api_set_handlers (VL_API_##N, #n, vl_api_##n##_t_handler,            \
437                            noop_handler, vl_api_##n##_t_endian,               \
438                            vl_api_##n##_t_print, sizeof (vl_api_##n##_t), 0,  \
439                            vl_api_##n##_t_print_json, vl_api_##n##_t_tojson,  \
440                            vl_api_##n##_t_fromjson);
441   foreach_sock_client_api_msg;
442 #undef _
443 }
444
445 int
446 vl_socket_client_connect_internal (socket_client_main_t * scm,
447                                    char *socket_path, char *client_name,
448                                    u32 socket_buffer_size)
449 {
450   vl_api_sockclnt_create_t *mp;
451   clib_socket_t *sock;
452   clib_error_t *error;
453
454   /* Already connected? */
455   if (scm->socket_fd)
456     return (-2);
457
458   /* bogus call? */
459   if (socket_path == 0 || client_name == 0)
460     return (-3);
461
462   sock = &scm->client_socket;
463   sock->config = socket_path;
464   sock->flags = CLIB_SOCKET_F_IS_CLIENT;
465
466   if ((error = clib_socket_init (sock)))
467     {
468       clib_error_report (error);
469       return (-1);
470     }
471
472   vl_sock_client_install_message_handlers ();
473
474   scm->socket_fd = sock->fd;
475   scm->socket_buffer_size = socket_buffer_size ? socket_buffer_size :
476     SOCKET_CLIENT_DEFAULT_BUFFER_SIZE;
477   vec_validate (scm->socket_tx_buffer, scm->socket_buffer_size - 1);
478   vec_validate (scm->socket_rx_buffer, scm->socket_buffer_size - 1);
479   _vec_len (scm->socket_rx_buffer) = 0;
480   _vec_len (scm->socket_tx_buffer) = 0;
481   scm->name = format (0, "%s", client_name);
482
483   mp = vl_socket_client_msg_alloc2 (scm, sizeof (*mp));
484   mp->_vl_msg_id = htons (VL_API_SOCKCLNT_CREATE);
485   strncpy ((char *) mp->name, client_name, sizeof (mp->name) - 1);
486   mp->name[sizeof (mp->name) - 1] = 0;
487   mp->context = 0xfeedface;
488
489   clib_time_init (&scm->clib_time);
490
491   if (vl_socket_client_write_internal (scm) <= 0)
492     return (-1);
493
494   if (vl_socket_client_read_internal (scm, 5))
495     return (-1);
496
497   return (0);
498 }
499
500 int
501 vl_socket_client_connect (char *socket_path, char *client_name,
502                           u32 socket_buffer_size)
503 {
504   return vl_socket_client_connect_internal (socket_client_ctx, socket_path,
505                                             client_name, socket_buffer_size);
506 }
507
508 int
509 vl_socket_client_connect2 (socket_client_main_t * scm, char *socket_path,
510                            char *client_name, u32 socket_buffer_size)
511 {
512   socket_client_main_t *old_ctx;
513   int rv;
514
515   old_ctx = vl_socket_client_ctx_push (scm);
516   rv = vl_socket_client_connect_internal (socket_client_ctx, socket_path,
517                                           client_name, socket_buffer_size);
518   vl_socket_client_ctx_pop (old_ctx);
519   return rv;
520 }
521
522 int
523 vl_socket_client_init_shm_internal (socket_client_main_t * scm,
524                                     vl_api_shm_elem_config_t * config,
525                                     int want_pthread)
526 {
527   vl_api_sock_init_shm_t *mp;
528   int rv, i;
529   u64 *cfg;
530
531   scm->want_shm_pthread = want_pthread;
532
533   mp = vl_socket_client_msg_alloc2 (scm, sizeof (*mp) +
534                                     vec_len (config) * sizeof (u64));
535   clib_memset (mp, 0, sizeof (*mp));
536   mp->_vl_msg_id = clib_host_to_net_u16 (VL_API_SOCK_INIT_SHM);
537   mp->client_index = clib_host_to_net_u32 (scm->client_index);
538   mp->requested_size = 64 << 20;
539
540   if (config)
541     {
542       for (i = 0; i < vec_len (config); i++)
543         {
544           cfg = (u64 *) & config[i];
545           mp->configs[i] = *cfg;
546         }
547       mp->nitems = vec_len (config);
548     }
549   rv = vl_socket_client_write_internal (scm);
550   if (rv <= 0)
551     return rv;
552
553   if (vl_socket_client_read_internal (scm, 1))
554     return -1;
555
556   return 0;
557 }
558
559 int
560 vl_socket_client_init_shm (vl_api_shm_elem_config_t * config,
561                            int want_pthread)
562 {
563   return vl_socket_client_init_shm_internal (socket_client_ctx, config,
564                                              want_pthread);
565 }
566
567 int
568 vl_socket_client_init_shm2 (socket_client_main_t * scm,
569                             vl_api_shm_elem_config_t * config,
570                             int want_pthread)
571 {
572   socket_client_main_t *old_ctx;
573   int rv;
574
575   old_ctx = vl_socket_client_ctx_push (scm);
576   rv = vl_socket_client_init_shm_internal (socket_client_ctx, config,
577                                            want_pthread);
578   vl_socket_client_ctx_pop (old_ctx);
579   return rv;
580 }
581
582 clib_error_t *
583 vl_socket_client_recv_fd_msg2 (socket_client_main_t * scm, int fds[],
584                                int n_fds, u32 wait)
585 {
586   if (!scm->socket_fd)
587     return clib_error_return (0, "no socket");
588   return vl_sock_api_recv_fd_msg_internal (scm, fds, n_fds, wait);
589 }
590
591 clib_error_t *
592 vl_socket_client_recv_fd_msg (int fds[], int n_fds, u32 wait)
593 {
594   return vl_socket_client_recv_fd_msg2 (socket_client_ctx, fds, n_fds, wait);
595 }
596
597 /*
598  * fd.io coding-style-patch-verification: ON
599  *
600  * Local Variables:
601  * eval: (c-set-style "gnu")
602  * End:
603  */