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