1 
2 
3 #define EX_UTILS_NO_USE_INPUT 1
4 #define EX_UTILS_NO_USE_IO_FD 1
5 #define EX_UTILS_NO_USE_OPEN  1
6 #define EX_UTILS_NO_USE_INIT  1
7 #define EX_UTILS_NO_USE_EXIT  1
8 #include "ex_utils.h"
9 
10 #include <stdlib.h>
11 #include <sys/socket.h>
12 #include <getopt.h>
13 #include <netinet/in.h>
14 #include <netinet/tcp.h>
15 #include <arpa/inet.h>
16 #include <netdb.h>
17 #include <sys/time.h>
18 #include <time.h>
19 #include <signal.h>
20 
21 #include <socket_poll.h>
22 #include <timer_q.h>
23 
24 #define WAIT_SET_RECV_FLAG 1 /* work around ? */
25 
26 #define CL_PACKET_MAX 0xFFFF
27 #define CL_MAX_CONNECT 128
28 
29 /* made up ... 8bits */
30 #define CL_MSG_CLIENT_NONE INT_MAX
31 #define CL_MSG_CLIENT_ZERO (INT_MAX - 1)
32 #define CL_MAX_WAIT_SEND 16
33 
34 
35 #include "app.h"
36 #include "evnt.h"
37 
38 #define EX_UTILS_NO_FUNCS  1
39 #include "ex_utils.h"
40 
41 #include "mk.h"
42 
43 MALLOC_CHECK_DECL();
44 
45 #include "opt.h"
46 
47 struct con
48 {
49  struct Evnt ev[1];
50 };
51 
52 static int io_r_fd = STDIN_FILENO;
53 unsigned int io_ind_r = 0; /* socket poll */
54 static Vstr_base *io_r = NULL;
55 static int io_w_fd = STDOUT_FILENO;
56 unsigned int io_ind_w = 0; /* socket poll */
57 static Vstr_base *io_w = NULL;
58 
59 static Timer_q_base *cl_timeout_base = NULL;
60 static Timer_q_base *cl_timer_connect_base = NULL;
61 
62 static int server_clients_count = 0; /* current number of clients */
63 
64 static int server_clients = 1;
65 static unsigned int server_timeout = (2 * 60); /* 2 minutes */
66 
67 static const char *server_filename = NULL;
68 
69 static Vlg *vlg = NULL;
70 
ui_out(void)71 static void ui_out(void)
72 {
73   if (!io_ind_w)
74     return;
75 
76   SOCKET_POLL_INDICATOR(io_ind_w)->events |=  POLLOUT;
77 }
78 
cl_recv(struct Evnt * evnt)79 static int cl_recv(struct Evnt *evnt)
80 {
81   int ret = evnt_cb_func_recv(evnt);
82 
83   if (!ret)
84     goto malloc_bad;
85 
86   while (evnt->io_r->len)
87   {
88     size_t pos = 0;
89     size_t len = 0;
90     size_t ns1 = 0;
91     size_t vpos = 0;
92     size_t vlen = 0;
93     size_t nse2 = 0;
94     int done = FALSE;
95 
96     if (!(ns1 = vstr_parse_netstr(evnt->io_r, 1, evnt->io_r->len, &pos, &len)))
97     {
98       if (!(SOCKET_POLL_INDICATOR(evnt->ind)->events & POLLIN))
99         return (FALSE);
100       return (TRUE);
101     }
102 
103     while ((nse2 = vstr_parse_netstr(evnt->io_r, pos, len, &vpos, &vlen)))
104     {
105       if (!done && !vlen && (nse2 == len))
106       {
107         len = 0;
108         evnt_got_pkt(evnt);
109         break;
110       }
111 
112       if (done)
113         app_cstr_buf(io_w, " ");
114 
115       app_vstr(io_w, evnt->io_r, vpos, vlen, VSTR_TYPE_ADD_DEF);
116 
117       if (!done)
118         app_cstr_buf(io_w, ":");
119 
120       done = TRUE;
121 
122       len -= nse2; pos += nse2;
123     }
124     if (done)
125       app_cstr_buf(io_w, "\n");
126 
127     ui_out();
128 
129     if (len)
130       VLG_WARN_RET(FALSE, (vlg, "invalid entry\n"));
131 
132   /*
133   if (io_w->conf->malloc_bad)
134     goto malloc_bad;
135   */
136 
137     vstr_del(evnt->io_r, 1, ns1);
138   }
139 
140   return (TRUE);
141 
142  malloc_bad:
143   evnt->io_r->conf->malloc_bad = FALSE;
144   evnt->io_w->conf->malloc_bad = FALSE;
145   return (FALSE);
146 }
147 
148 #define UI_CMD(x)                                                       \
149     else if (vstr_cmp_case_cstr_eq(io_r, 1, len, x "\n")) do            \
150     {                                                                   \
151       size_t ns1 = 0;                                                   \
152       Vstr_base *out = con->io_w;                                       \
153                                                                         \
154       if (!(ns1 = vstr_add_netstr_beg(out, out->len)) ||                \
155           !vstr_add_cstr_ptr(out, out->len, x) ||                       \
156           !vstr_add_netstr_end(out, ns1, out->len) ||                   \
157           !evnt_send_add(con, FALSE, 0))                                \
158       {                                                                 \
159         evnt_close(con);                                                \
160         return;                                                         \
161       }                                                                 \
162       evnt_put_pkt(con);                                                \
163     } while (FALSE)
164 
165 static void cl_connect(void); /* fwd ref */
ui_parse(void)166 static void ui_parse(void)
167 {
168   size_t len = 0;
169   unsigned int count = 64;
170   struct Evnt *con = NULL;
171 
172   vlg_dbg3(vlg, "ui_parse %zu\n", io_r->len);
173 
174   if (!io_r->len)
175     return; /* don't start more connections for nothing */
176 
177   if (!(con = evnt_find_least_used()))
178   {
179     cl_connect();
180     return;
181   }
182 
183   while ((len = vstr_srch_chr_fwd(io_r, 1, io_r->len, '\n')) && --count)
184   {
185     size_t line_len = len;
186 
187     if (0) { /* not reached */ }
188     UI_CMD("CLOSE");
189     UI_CMD("DBG");
190     UI_CMD("UNDBG");
191     UI_CMD("LIST");
192     UI_CMD("STATUS");
193 
194     vlg_dbg3(vlg, "bad input\n");
195     /* ignore everything else */
196 
197     vstr_del(io_r, 1, line_len);
198   }
199   vlg_dbg3(vlg, "io_r left = %zu\n", io_r->len);
200 }
201 #undef UI_CMD
202 
cl_cb_func_connect(struct Evnt * evnt)203 static int cl_cb_func_connect(struct Evnt *evnt)
204 {
205   (void)evnt;
206 
207   vlg_dbg3(vlg, "connect\n");
208 
209   ui_parse();
210 
211   return (TRUE);
212 }
213 
cl_cb_func_recv(struct Evnt * evnt)214 static int cl_cb_func_recv(struct Evnt *evnt)
215 {
216   return (cl_recv(evnt));
217 }
218 
cl_cb_func_free(struct Evnt * evnt)219 static void cl_cb_func_free(struct Evnt *evnt)
220 {
221   struct con *con = (struct con *)evnt;
222 
223   F(con);
224 
225   --server_clients_count;
226 }
227 
cl_make(const char * server_fname)228 static struct con *cl_make(const char *server_fname)
229 {
230   struct con *ret = MK(sizeof(struct con));
231 
232   if (!ret)
233     errno = ENOMEM, err(EXIT_FAILURE, "%s", __func__);
234   if (!evnt_make_con_local(ret->ev, server_fname))
235     err(EXIT_FAILURE, "%s", __func__);
236 
237   ret->ev->cbs->cb_func_connect = cl_cb_func_connect;
238   ret->ev->cbs->cb_func_recv    = cl_cb_func_recv;
239   ret->ev->cbs->cb_func_free    = cl_cb_func_free;
240 
241   ++server_clients_count;
242 
243   if (ret->ev->flag_q_none)
244     cl_cb_func_connect(ret->ev);
245 
246   return (ret);
247 }
248 
cl_connect(void)249 static void cl_connect(void)
250 {
251   struct con *con = cl_make(server_filename);
252   struct timeval tv;
253 
254   if (server_timeout)
255   {
256     gettimeofday(&tv, NULL);
257 
258     TIMER_Q_TIMEVAL_ADD_SECS(&tv, 0, rand() % server_timeout);
259 
260     if (!(con->ev->tm_o = timer_q_add_node(cl_timeout_base, con, &tv,
261                                            TIMER_Q_FLAG_NODE_DEFAULT)))
262       errno = ENOMEM, err(EXIT_FAILURE, "%s", __func__);
263   }
264 }
265 
cl_scan_io_fds(unsigned int ready)266 static unsigned int cl_scan_io_fds(unsigned int ready)
267 {
268   const int bad_poll_flags = (POLLERR | /* POLLHUP | */ POLLNVAL);
269 
270   vlg_dbg2(vlg, "BEG ready = %u\n", ready);
271   if (io_ind_r &&
272       SOCKET_POLL_INDICATOR(io_ind_r)->revents & bad_poll_flags)
273   {
274     --ready;
275 
276     close(SOCKET_POLL_INDICATOR(io_ind_r)->fd);
277     vlg_dbg2(vlg, "ERROR-POLL-IO_R(%d):\n",
278              SOCKET_POLL_INDICATOR(io_ind_r)->revents);
279     socket_poll_del(io_ind_r);
280     io_ind_r = 0;
281   }
282   if (SOCKET_POLL_INDICATOR(io_ind_w)->revents & bad_poll_flags)
283   {
284     --ready;
285 
286     close(SOCKET_POLL_INDICATOR(io_ind_w)->fd);
287     vlg_dbg2(vlg, "ERROR-POLL-IO_W(%d):\n",
288              SOCKET_POLL_INDICATOR(io_ind_w)->revents);
289     socket_poll_del(io_ind_w);
290     io_ind_w = 0;
291   }
292   if (io_ind_r && (SOCKET_POLL_INDICATOR(io_ind_r)->revents & POLLIN))
293   {
294     unsigned int ern;
295 
296     --ready;
297     while (vstr_sc_read_iov_fd(io_r, io_r->len, io_r_fd, 4, 32, &ern))
298     { /* do nothing */ }
299 
300     switch (ern)
301     {
302       case VSTR_TYPE_SC_READ_FD_ERR_EOF:
303         close(SOCKET_POLL_INDICATOR(io_ind_r)->fd);
304         SOCKET_POLL_INDICATOR(io_ind_r)->fd = -1;
305         socket_poll_del(io_ind_r);
306         io_ind_r = 0;
307         errno = EAGAIN;
308       case VSTR_TYPE_SC_READ_FD_ERR_READ_ERRNO:
309         if (errno != EAGAIN)
310           break;
311       case VSTR_TYPE_SC_READ_FD_ERR_NONE:
312         ui_parse();
313       default:
314         break;
315     }
316     vlg_dbg2(vlg, "READ UI\n");
317   }
318   else if (io_ind_w)
319     ui_parse();
320 
321   if (io_ind_w && (SOCKET_POLL_INDICATOR(io_ind_w)->revents & POLLOUT))
322   {
323     unsigned int ern;
324 
325     --ready;
326 
327     while (io_w->len && vstr_sc_write_fd(io_w, 1, io_w->len, io_w_fd, &ern))
328     { /* do nothing */ }
329 
330     if (!io_w->len)
331       SOCKET_POLL_INDICATOR(io_ind_w)->events &= ~POLLOUT;
332     vlg_dbg2(vlg, "WRITE UI\n");
333   }
334 
335   return (ready);
336 }
337 
usage(const char * program_name,int ret,const char * prefix)338 static void usage(const char *program_name, int ret, const char *prefix)
339 {
340   Vstr_base *out = vstr_make_base(NULL);
341 
342   if (!out)
343     errno = ENOMEM, err(EXIT_FAILURE, "usage");
344 
345   vstr_add_fmt(out, 0, "%s\n"
346           " Format: %s [-chmtwV] <server name>\n"
347           " --help -h         - Print this message.\n"
348           " --debug -d        - Enable debug info.\n"
349           " --clients -c      - Number of connections to make.\n"
350           " --nagle -n        - Enable/disable nagle TCP option.\n"
351           " --timeout -t      - Timeout (usecs) between each message.\n"
352           " --version -V      - Print the version string.\n",
353           prefix, program_name);
354 
355   if (io_put_all(out, ret ? STDERR_FILENO : STDOUT_FILENO) == IO_FAIL)
356     err(EXIT_FAILURE, "write");
357 
358   exit (ret);
359 }
360 
361 
cl_cmd_line(int argc,char * argv[])362 static void cl_cmd_line(int argc, char *argv[])
363 {
364   char optchar = 0;
365   const char *program_name = NULL;
366   struct option long_options[] =
367   {
368    {"help", no_argument, NULL, 'h'},
369    {"clients", required_argument, NULL, 'c'},
370    {"debug", required_argument, NULL, 'd'},
371    {"execute", required_argument, NULL, 'e'},
372    {"host", required_argument, NULL, 'H'},
373    {"port", required_argument, NULL, 'P'},
374    {"nagle", optional_argument, NULL, 'n'},
375    {"timeout", required_argument, NULL, 't'},
376    {"version", no_argument, NULL, 'V'},
377    {NULL, 0, NULL, 0}
378   };
379   Vstr_base *out = vstr_make_base(NULL);
380 
381   if (!out)
382     errno = ENOMEM, err(EXIT_FAILURE, "command line");
383 
384   program_name = opt_program_name(argv[0], "cntl");
385 
386   while ((optchar = getopt_long(argc, argv, "c:de:hH:nP:Rt:V",
387                                 long_options, NULL)) != -1)
388   {
389     switch (optchar)
390     {
391       case '?': usage(program_name, EXIT_FAILURE, "");
392       case 'h': usage(program_name, EXIT_SUCCESS, "");
393 
394       case 'V':
395           vstr_add_fmt(out, 0,"\
396 %s version 1.0.0, compiled on %s.\n\
397 Written by James Antill\n\
398 \n\
399 Uses Vstr string library.\n\
400 ",
401                        program_name, __DATE__);
402 
403         if (io_put_all(out, STDOUT_FILENO) == IO_FAIL)
404           err(EXIT_FAILURE, "write");
405 
406         exit (EXIT_SUCCESS);
407 
408       case 'c': server_clients      = atoi(optarg); break;
409       case 't': server_timeout      = atoi(optarg); break;
410 
411       case 'd': vlg_debug(vlg);                     break;
412 
413       case 'e':
414         /* use cmd line instead of stdin */
415         io_r_fd = -1;
416         app_cstr_buf(io_r, optarg); app_cstr_buf(io_r, "\n");
417         break;
418 
419       case 'n':
420         if (!optarg)
421         { evnt_opt_nagle = !evnt_opt_nagle; }
422         else if (!strcasecmp("true", optarg))   evnt_opt_nagle = TRUE;
423         else if (!strcasecmp("1", optarg))      evnt_opt_nagle = TRUE;
424         else if (!strcasecmp("false", optarg))  evnt_opt_nagle = FALSE;
425         else if (!strcasecmp("0", optarg))      evnt_opt_nagle = FALSE;
426         break;
427 
428       default:
429         abort();
430     }
431   }
432   vstr_free_base(out); out = NULL;
433 
434   argc -= optind;
435   argv += optind;
436 
437   if (argc != 1)
438     usage(program_name, EXIT_FAILURE, "");
439 
440   server_filename = argv[0];
441 }
442 
cl_timer_cli(int type,void * data)443 static void cl_timer_cli(int type, void *data)
444 {
445   struct con *con = data;
446   struct timeval tv;
447   unsigned long diff = 0;
448 
449   if (!con)
450     return;
451 
452   ASSERT(evnt_fd(con->ev) != -1);
453 
454   if (type == TIMER_Q_TYPE_CALL_RUN_ALL)
455     return;
456 
457   con->ev->tm_o = NULL;
458 
459   if (type == TIMER_Q_TYPE_CALL_DEL)
460     return;
461 
462   gettimeofday(&tv, NULL);
463   diff = timer_q_timeval_udiff_secs(&tv, &con->ev->mtime);
464   if (diff > server_timeout)
465   {
466     vlg_dbg2(vlg, "timeout = %p (%lu, %lu)\n",
467              con, diff, (unsigned long)server_timeout);
468     close(SOCKET_POLL_INDICATOR(con->ev->ind)->fd);
469     return;
470   }
471 
472   if (type == TIMER_Q_TYPE_CALL_RUN_ALL)
473     return;
474 
475   TIMER_Q_TIMEVAL_ADD_SECS(&tv, (server_timeout - diff) + 1, 0);
476   if (!(con->ev->tm_o = timer_q_add_node(cl_timeout_base, con, &tv,
477                                          TIMER_Q_FLAG_NODE_DEFAULT)))
478     errno = ENOMEM, err(EXIT_FAILURE, "%s", __func__);
479 }
480 
cl_timer_con(int type,void * data)481 static void cl_timer_con(int type, void *data)
482 {
483   int count = 0;
484 
485   if (type == TIMER_Q_TYPE_CALL_DEL)
486     return;
487 
488   while ((server_clients_count < server_clients) && (count < CL_MAX_CONNECT))
489   {
490     cl_connect();
491     ++count;
492   }
493 
494   if (type == TIMER_Q_TYPE_CALL_RUN_ALL)
495     return;
496 
497   if (server_clients_count < server_clients)
498   {
499     struct timeval tv;
500 
501     gettimeofday(&tv, NULL);
502     TIMER_Q_TIMEVAL_ADD_SECS(&tv, 1, 0);
503     if (!timer_q_add_node(cl_timer_connect_base, NULL, &tv,
504                           TIMER_Q_FLAG_NODE_DEFAULT))
505       errno = ENOMEM, err(EXIT_FAILURE, "%s", __func__);
506   }
507 }
508 
cl_init(void)509 static void cl_init(void)
510 {
511   cl_timeout_base       = timer_q_add_base(cl_timer_cli,
512                                            TIMER_Q_FLAG_BASE_DEFAULT);
513   cl_timer_connect_base = timer_q_add_base(cl_timer_con,
514                                            TIMER_Q_FLAG_BASE_DEFAULT);
515   if (!cl_timeout_base)
516     errno = ENOMEM, err(EXIT_FAILURE, "%s", __func__);
517   if (!cl_timer_connect_base)
518     errno = ENOMEM, err(EXIT_FAILURE, "%s", __func__);
519 
520   /* FIXME: massive hack 1.0.5 is broken */
521   timer_q_cntl_base(cl_timeout_base,
522                     TIMER_Q_CNTL_BASE_SET_FLAG_INSERT_FROM_END, FALSE);
523   timer_q_cntl_base(cl_timer_connect_base,
524                     TIMER_Q_CNTL_BASE_SET_FLAG_INSERT_FROM_END, FALSE);
525 
526   vlg_init();
527 
528   if (!(vlg = vlg_make()))
529     errno = ENOMEM, err(EXIT_FAILURE, "init");
530 
531   evnt_logger(vlg);
532 }
533 
cl_beg(void)534 static void cl_beg(void)
535 {
536   int count = 0;
537 
538   vlg_dbg3(vlg, "cl_beg\n");
539 
540   if (io_r_fd != -1)
541   {
542     vlg_dbg3(vlg, "cl_beg io_r beg\n");
543     evnt_fd__set_nonblock(io_r_fd,  TRUE);
544     if (!(io_ind_r = socket_poll_add(io_r_fd)))
545       errno = ENOMEM, err(EXIT_FAILURE, "%s", __func__);
546     SOCKET_POLL_INDICATOR(io_ind_r)->events |= POLLIN;
547   }
548 
549   evnt_fd__set_nonblock(io_w_fd, TRUE);
550   if (!(io_ind_w = socket_poll_add(io_w_fd)))
551     errno = ENOMEM, err(EXIT_FAILURE, "%s", __func__);
552 
553   while ((server_clients_count < server_clients) && (count < CL_MAX_CONNECT))
554   {
555     cl_connect();
556     ++count;
557   }
558 
559   if (server_clients_count < server_clients)
560   {
561     struct timeval tv;
562 
563     gettimeofday(&tv, NULL);
564     TIMER_Q_TIMEVAL_ADD_SECS(&tv, 1, 0);
565     if (!timer_q_add_node(cl_timer_connect_base, NULL, &tv,
566                           TIMER_Q_FLAG_NODE_DEFAULT))
567       errno = ENOMEM, err(EXIT_FAILURE, "%s", __func__);
568   }
569 }
570 
cl_signals(void)571 static void cl_signals(void)
572 {
573   struct sigaction sa;
574 
575   if (sigemptyset(&sa.sa_mask) == -1)
576     err(EXIT_FAILURE, "%s", __func__);
577 
578   /* don't use SA_RESTART ... */
579   sa.sa_flags = 0;
580   /* ignore it... we don't have a use for it */
581   sa.sa_handler = SIG_IGN;
582 
583   if (sigaction(SIGPIPE, &sa, NULL) == -1)
584     err(EXIT_FAILURE, "%s", __func__);
585 }
586 
main(int argc,char * argv[])587 int main(int argc, char *argv[])
588 {
589   if (!vstr_init())
590     errno = ENOMEM, err(EXIT_FAILURE, "%s", __func__);
591 
592   vstr_cntl_conf(NULL, VSTR_CNTL_CONF_SET_FMT_CHAR_ESC, '$');
593   vstr_sc_fmt_add_all(NULL);
594 
595   if (!(io_r = vstr_make_base(NULL))) /* used in cmd line */
596     errno = ENOMEM, err(EXIT_FAILURE, "%s", __func__);
597   if (!(io_w = vstr_make_base(NULL)))
598     errno = ENOMEM, err(EXIT_FAILURE, "%s", __func__);
599 
600   if (!socket_poll_init(0, SOCKET_POLL_TYPE_MAP_DIRECT))
601     errno = ENOMEM, err(EXIT_FAILURE, "%s", __func__);
602 
603   srand(getpid() ^ time(NULL)); /* doesn't need to be secure... just different
604                                  * for different runs */
605 
606   cl_signals();
607 
608   cl_init();
609 
610   cl_cmd_line(argc, argv);
611 
612   cl_beg();
613 
614   while (io_ind_w && (io_w->len || evnt_waiting() || io_ind_r || io_r->len))
615   {
616     int ready = evnt_poll();
617     struct timeval tv;
618 
619     if ((ready == -1) && (errno != EINTR))
620       err(EXIT_FAILURE, "%s", __func__);
621     if (ready == -1)
622       continue;
623 
624     evnt_out_dbg3("1");
625     ready = cl_scan_io_fds(ready);
626     evnt_out_dbg3("2");
627     evnt_scan_fds(ready, CL_MAX_WAIT_SEND);
628     evnt_out_dbg3("3");
629     evnt_scan_send_fds();
630     evnt_out_dbg3("4");
631 
632     gettimeofday(&tv, NULL);
633     timer_q_run_norm(&tv);
634 
635     evnt_out_dbg3("5");
636     evnt_scan_send_fds();
637     evnt_out_dbg3("6");
638   }
639   evnt_out_dbg3("E");
640 
641   vstr_free_base(io_r);
642   vstr_free_base(io_w);
643 
644   timer_q_del_base(cl_timeout_base);
645   timer_q_del_base(cl_timer_connect_base);
646 
647   evnt_close_all();
648 
649   vlg_free(vlg);
650 
651   vlg_exit();
652 
653   vstr_exit();
654 
655   MALLOC_CHECK_EMPTY();
656 
657   exit (EXIT_SUCCESS);
658 }
659 
660