1
+/*
2
+ * test-simple-ipc.c: verify that the Inter-Process Communication works.
3
+ */
4
+
5
+#include "test-tool.h"
6
+#include "cache.h"
7
+#include "strbuf.h"
8
+#include "simple-ipc.h"
9
+#include "parse-options.h"
10
+#include "thread-utils.h"
11
+#include "strvec.h"
12
+
13
+#ifndef SUPPORTS_SIMPLE_IPC
14
+int cmd__simple_ipc(int argc, const char **argv)
15
+{
16
+ die("simple IPC not available on this platform");
17
+}
18
+#else
19
+
20
+/*
21
+ * The test daemon defines an "application callback" that supports a
22
+ * series of commands (see `test_app_cb()`).
23
+ *
24
+ * Unknown commands are caught here and we send an error message back
25
+ * to the client process.
26
+ */
27
+static int app__unhandled_command(const char *command,
28
+ ipc_server_reply_cb *reply_cb,
29
+ struct ipc_server_reply_data *reply_data)
30
+{
31
+ struct strbuf buf = STRBUF_INIT;
32
+ int ret;
33
+
34
+ strbuf_addf(&buf, "unhandled command: %s", command);
35
+ ret = reply_cb(reply_data, buf.buf, buf.len);
36
+ strbuf_release(&buf);
37
+
38
+ return ret;
39
+}
40
+
41
+/*
42
+ * Reply with a single very large buffer. This is to ensure that
43
+ * long response are properly handled -- whether the chunking occurs
44
+ * in the kernel or in the (probably pkt-line) layer.
45
+ */
46
+#define BIG_ROWS (10000)
47
+static int app__big_command(ipc_server_reply_cb *reply_cb,
48
+ struct ipc_server_reply_data *reply_data)
49
+{
50
+ struct strbuf buf = STRBUF_INIT;
51
+ int row;
52
+ int ret;
53
+
54
+ for (row = 0; row < BIG_ROWS; row++)
55
+ strbuf_addf(&buf, "big: %.75d\n", row);
56
+
57
+ ret = reply_cb(reply_data, buf.buf, buf.len);
58
+ strbuf_release(&buf);
59
+
60
+ return ret;
61
+}
62
+
63
+/*
64
+ * Reply with a series of lines. This is to ensure that we can incrementally
65
+ * compute the response and chunk it to the client.
66
+ */
67
+#define CHUNK_ROWS (10000)
68
+static int app__chunk_command(ipc_server_reply_cb *reply_cb,
69
+ struct ipc_server_reply_data *reply_data)
70
+{
71
+ struct strbuf buf = STRBUF_INIT;
72
+ int row;
73
+ int ret;
74
+
75
+ for (row = 0; row < CHUNK_ROWS; row++) {
76
+ strbuf_setlen(&buf, 0);
77
+ strbuf_addf(&buf, "big: %.75d\n", row);
78
+ ret = reply_cb(reply_data, buf.buf, buf.len);
79
+ }
80
+
81
+ strbuf_release(&buf);
82
+
83
+ return ret;
84
+}
85
+
86
+/*
87
+ * Slowly reply with a series of lines. This is to model an expensive to
88
+ * compute chunked response (which might happen if this callback is running
89
+ * in a thread and is fighting for a lock with other threads).
90
+ */
91
+#define SLOW_ROWS (1000)
92
+#define SLOW_DELAY_MS (10)
93
+static int app__slow_command(ipc_server_reply_cb *reply_cb,
94
+ struct ipc_server_reply_data *reply_data)
95
+{
96
+ struct strbuf buf = STRBUF_INIT;
97
+ int row;
98
+ int ret;
99
+
100
+ for (row = 0; row < SLOW_ROWS; row++) {
101
+ strbuf_setlen(&buf, 0);
102
+ strbuf_addf(&buf, "big: %.75d\n", row);
103
+ ret = reply_cb(reply_data, buf.buf, buf.len);
104
+ sleep_millisec(SLOW_DELAY_MS);
105
+ }
106
+
107
+ strbuf_release(&buf);
108
+
109
+ return ret;
110
+}
111
+
112
+/*
113
+ * The client sent a command followed by a (possibly very) large buffer.
114
+ */
115
+static int app__sendbytes_command(const char *received,
116
+ ipc_server_reply_cb *reply_cb,
117
+ struct ipc_server_reply_data *reply_data)
118
+{
119
+ struct strbuf buf_resp = STRBUF_INIT;
120
+ const char *p = "?";
121
+ int len_ballast = 0;
122
+ int k;
123
+ int errs = 0;
124
+ int ret;
125
+
126
+ if (skip_prefix(received, "sendbytes ", &p))
127
+ len_ballast = strlen(p);
128
+
129
+ /*
130
+ * Verify that the ballast is n copies of a single letter.
131
+ * And that the multi-threaded IO layer didn't cross the streams.
132
+ */
133
+ for (k = 1; k < len_ballast; k++)
134
+ if (p[k] != p[0])
135
+ errs++;
136
+
137
+ if (errs)
138
+ strbuf_addf(&buf_resp, "errs:%d\n", errs);
139
+ else
140
+ strbuf_addf(&buf_resp, "rcvd:%c%08d\n", p[0], len_ballast);
141
+
142
+ ret = reply_cb(reply_data, buf_resp.buf, buf_resp.len);
143
+
144
+ strbuf_release(&buf_resp);
145
+
146
+ return ret;
147
+}
148
+
149
+/*
150
+ * An arbitrary fixed address to verify that the application instance
151
+ * data is handled properly.
152
+ */
153
+static int my_app_data = 42;
154
+
155
+static ipc_server_application_cb test_app_cb;
156
+
157
+/*
158
+ * This is the "application callback" that sits on top of the
159
+ * "ipc-server". It completely defines the set of commands supported
160
+ * by this application.
161
+ */
162
+static int test_app_cb(void *application_data,
163
+ const char *command,
164
+ ipc_server_reply_cb *reply_cb,
165
+ struct ipc_server_reply_data *reply_data)
166
+{
167
+ /*
168
+ * Verify that we received the application-data that we passed
169
+ * when we started the ipc-server. (We have several layers of
170
+ * callbacks calling callbacks and it's easy to get things mixed
171
+ * up (especially when some are "void*").)
172
+ */
173
+ if (application_data != (void*)&my_app_data)
174
+ BUG("application_cb: application_data pointer wrong");
175
+
176
+ if (!strcmp(command, "quit")) {
177
+ /*
178
+ * The client sent a "quit" command. This is an async
179
+ * request for the server to shutdown.
180
+ *
181
+ * We DO NOT send the client a response message
182
+ * (because we have nothing to say and the other
183
+ * server threads have not yet stopped).
184
+ *
185
+ * Tell the ipc-server layer to start shutting down.
186
+ * This includes: stop listening for new connections
187
+ * on the socket/pipe and telling all worker threads
188
+ * to finish/drain their outgoing responses to other
189
+ * clients.
190
+ *
191
+ * This DOES NOT force an immediate sync shutdown.
192
+ */
193
+ return SIMPLE_IPC_QUIT;
194
+ }
195
+
196
+ if (!strcmp(command, "ping")) {
197
+ const char *answer = "pong";
198
+ return reply_cb(reply_data, answer, strlen(answer));
199
+ }
200
+
201
+ if (!strcmp(command, "big"))
202
+ return app__big_command(reply_cb, reply_data);
203
+
204
+ if (!strcmp(command, "chunk"))
205
+ return app__chunk_command(reply_cb, reply_data);
206
+
207
+ if (!strcmp(command, "slow"))
208
+ return app__slow_command(reply_cb, reply_data);
209
+
210
+ if (starts_with(command, "sendbytes "))
211
+ return app__sendbytes_command(command, reply_cb, reply_data);
212
+
213
+ return app__unhandled_command(command, reply_cb, reply_data);
214
+}
215
+
216
+struct cl_args
217
+{
218
+ const char *subcommand;
219
+ const char *path;
220
+ const char *token;
221
+
222
+ int nr_threads;
223
+ int max_wait_sec;
224
+ int bytecount;
225
+ int batchsize;
226
+
227
+ char bytevalue;
228
+};
229
+
230
+static struct cl_args cl_args = {
231
+ .subcommand = NULL,
232
+ .path = "ipc-test",
233
+ .token = NULL,
234
+
235
+ .nr_threads = 5,
236
+ .max_wait_sec = 60,
237
+ .bytecount = 1024,
238
+ .batchsize = 10,
239
+
240
+ .bytevalue = 'x',
241
+};
242
+
243
+/*
244
+ * This process will run as a simple-ipc server and listen for IPC commands
245
+ * from client processes.
246
+ */
247
+static int daemon__run_server(void)
248
+{
249
+ int ret;
250
+
251
+ struct ipc_server_opts opts = {
252
+ .nr_threads = cl_args.nr_threads,
253
+ };
254
+
255
+ /*
256
+ * Synchronously run the ipc-server. We don't need any application
257
+ * instance data, so pass an arbitrary pointer (that we'll later
258
+ * verify made the round trip).
259
+ */
260
+ ret = ipc_server_run(cl_args.path, &opts, test_app_cb, (void*)&my_app_data);
261
+ if (ret == -2)
262
+ error(_("socket/pipe already in use: '%s'"), cl_args.path);
263
+ else if (ret == -1)
264
+ error_errno(_("could not start server on: '%s'"), cl_args.path);
265
+
266
+ return ret;
267
+}
268
+
269
+#ifndef GIT_WINDOWS_NATIVE
270
+/*
271
+ * This is adapted from `daemonize()`. Use `fork()` to directly create and
272
+ * run the daemon in a child process.
273
+ */
274
+static int spawn_server(pid_t *pid)
275
+{
276
+ struct ipc_server_opts opts = {
277
+ .nr_threads = cl_args.nr_threads,
278
+ };
279
+
280
+ *pid = fork();
281
+
282
+ switch (*pid) {
283
+ case 0:
284
+ if (setsid() == -1)
285
+ error_errno(_("setsid failed"));
286
+ close(0);
287
+ close(1);
288
+ close(2);
289
+ sanitize_stdfds();
290
+
291
+ return ipc_server_run(cl_args.path, &opts, test_app_cb,
292
+ (void*)&my_app_data);
293
+
294
+ case -1:
295
+ return error_errno(_("could not spawn daemon in the background"));
296
+
297
+ default:
298
+ return 0;
299
+ }
300
+}
301
+#else
302
+/*
303
+ * Conceptually like `daemonize()` but different because Windows does not
304
+ * have `fork(2)`. Spawn a normal Windows child process but without the
305
+ * limitations of `start_command()` and `finish_command()`.
306
+ */
307
+static int spawn_server(pid_t *pid)
308
+{
309
+ char test_tool_exe[MAX_PATH];
310
+ struct strvec args = STRVEC_INIT;
311
+ int in, out;
312
+
313
+ GetModuleFileNameA(NULL, test_tool_exe, MAX_PATH);
314
+
315
+ in = open("/dev/null", O_RDONLY);
316
+ out = open("/dev/null", O_WRONLY);
317
+
318
+ strvec_push(&args, test_tool_exe);
319
+ strvec_push(&args, "simple-ipc");
320
+ strvec_push(&args, "run-daemon");
321
+ strvec_pushf(&args, "--name=%s", cl_args.path);
322
+ strvec_pushf(&args, "--threads=%d", cl_args.nr_threads);
323
+
324
+ *pid = mingw_spawnvpe(args.v[0], args.v, NULL, NULL, in, out, out);
325
+ close(in);
326
+ close(out);
327
+
328
+ strvec_clear(&args);
329
+
330
+ if (*pid < 0)
331
+ return error(_("could not spawn daemon in the background"));
332
+
333
+ return 0;
334
+}
335
+#endif
336
+
337
+/*
338
+ * This is adapted from `wait_or_whine()`. Watch the child process and
339
+ * let it get started and begin listening for requests on the socket
340
+ * before reporting our success.
341
+ */
342
+static int wait_for_server_startup(pid_t pid_child)
343
+{
344
+ int status;
345
+ pid_t pid_seen;
346
+ enum ipc_active_state s;
347
+ time_t time_limit, now;
348
+
349
+ time(&time_limit);
350
+ time_limit += cl_args.max_wait_sec;
351
+
352
+ for (;;) {
353
+ pid_seen = waitpid(pid_child, &status, WNOHANG);
354
+
355
+ if (pid_seen == -1)
356
+ return error_errno(_("waitpid failed"));
357
+
358
+ else if (pid_seen == 0) {
359
+ /*
360
+ * The child is still running (this should be
361
+ * the normal case). Try to connect to it on
362
+ * the socket and see if it is ready for
363
+ * business.
364
+ *
365
+ * If there is another daemon already running,
366
+ * our child will fail to start (possibly
367
+ * after a timeout on the lock), but we don't
368
+ * care (who responds) if the socket is live.
369
+ */
370
+ s = ipc_get_active_state(cl_args.path);
371
+ if (s == IPC_STATE__LISTENING)
372
+ return 0;
373
+
374
+ time(&now);
375
+ if (now > time_limit)
376
+ return error(_("daemon not online yet"));
377
+
378
+ continue;
379
+ }
380
+
381
+ else if (pid_seen == pid_child) {
382
+ /*
383
+ * The new child daemon process shutdown while
384
+ * it was starting up, so it is not listening
385
+ * on the socket.
386
+ *
387
+ * Try to ping the socket in the odd chance
388
+ * that another daemon started (or was already
389
+ * running) while our child was starting.
390
+ *
391
+ * Again, we don't care who services the socket.
392
+ */
393
+ s = ipc_get_active_state(cl_args.path);
394
+ if (s == IPC_STATE__LISTENING)
395
+ return 0;
396
+
397
+ /*
398
+ * We don't care about the WEXITSTATUS() nor
399
+ * any of the WIF*(status) values because
400
+ * `cmd__simple_ipc()` does the `!!result`
401
+ * trick on all function return values.
402
+ *
403
+ * So it is sufficient to just report the
404
+ * early shutdown as an error.
405
+ */
406
+ return error(_("daemon failed to start"));
407
+ }
408
+
409
+ else
410
+ return error(_("waitpid is confused"));
411
+ }
412
+}
413
+
414
+/*
415
+ * This process will start a simple-ipc server in a background process and
416
+ * wait for it to become ready. This is like `daemonize()` but gives us
417
+ * more control and better error reporting (and makes it easier to write
418
+ * unit tests).
419
+ */
420
+static int daemon__start_server(void)
421
+{
422
+ pid_t pid_child;
423
+ int ret;
424
+
425
+ /*
426
+ * Run the actual daemon in a background process.
427
+ */
428
+ ret = spawn_server(&pid_child);
429
+ if (pid_child <= 0)
430
+ return ret;
431
+
432
+ /*
433
+ * Let the parent wait for the child process to get started
434
+ * and begin listening for requests on the socket.
435
+ */
436
+ ret = wait_for_server_startup(pid_child);
437
+
438
+ return ret;
439
+}
440
+
441
+/*
442
+ * This process will run a quick probe to see if a simple-ipc server
443
+ * is active on this path.
444
+ *
445
+ * Returns 0 if the server is alive.
446
+ */
447
+static int client__probe_server(void)
448
+{
449
+ enum ipc_active_state s;
450
+
451
+ s = ipc_get_active_state(cl_args.path);
452
+ switch (s) {
453
+ case IPC_STATE__LISTENING:
454
+ return 0;
455
+
456
+ case IPC_STATE__NOT_LISTENING:
457
+ return error("no server listening at '%s'", cl_args.path);
458
+
459
+ case IPC_STATE__PATH_NOT_FOUND:
460
+ return error("path not found '%s'", cl_args.path);
461
+
462
+ case IPC_STATE__INVALID_PATH:
463
+ return error("invalid pipe/socket name '%s'", cl_args.path);
464
+
465
+ case IPC_STATE__OTHER_ERROR:
466
+ default:
467
+ return error("other error for '%s'", cl_args.path);
468
+ }
469
+}
470
+
471
+/*
472
+ * Send an IPC command token to an already-running server daemon and
473
+ * print the response.
474
+ *
475
+ * This is a simple 1 word command/token that `test_app_cb()` (in the
476
+ * daemon process) will understand.
477
+ */
478
+static int client__send_ipc(void)
479
+{
480
+ const char *command = "(no-command)";
481
+ struct strbuf buf = STRBUF_INIT;
482
+ struct ipc_client_connect_options options
483
+ = IPC_CLIENT_CONNECT_OPTIONS_INIT;
484
+
485
+ if (cl_args.token && *cl_args.token)
486
+ command = cl_args.token;
487
+
488
+ options.wait_if_busy = 1;
489
+ options.wait_if_not_found = 0;
490
+
491
+ if (!ipc_client_send_command(cl_args.path, &options, command, &buf)) {
492
+ if (buf.len) {
493
+ printf("%s\n", buf.buf);
494
+ fflush(stdout);
495
+ }
496
+ strbuf_release(&buf);
497
+
498
+ return 0;
499
+ }
500
+
501
+ return error("failed to send '%s' to '%s'", command, cl_args.path);
502
+}
503
+
504
+/*
505
+ * Send an IPC command to an already-running server and ask it to
506
+ * shutdown. "send quit" is an async request and queues a shutdown
507
+ * event in the server, so we spin and wait here for it to actually
508
+ * shutdown to make the unit tests a little easier to write.
509
+ */
510
+static int client__stop_server(void)
511
+{
512
+ int ret;
513
+ time_t time_limit, now;
514
+ enum ipc_active_state s;
515
+
516
+ time(&time_limit);
517
+ time_limit += cl_args.max_wait_sec;
518
+
519
+ cl_args.token = "quit";
520
+
521
+ ret = client__send_ipc();
522
+ if (ret)
523
+ return ret;
524
+
525
+ for (;;) {
526
+ sleep_millisec(100);
527
+
528
+ s = ipc_get_active_state(cl_args.path);
529
+
530
+ if (s != IPC_STATE__LISTENING) {
531
+ /*
532
+ * The socket/pipe is gone and/or has stopped
533
+ * responding. Lets assume that the daemon
534
+ * process has exited too.
535
+ */
536
+ return 0;
537
+ }
538
+
539
+ time(&now);
540
+ if (now > time_limit)
541
+ return error(_("daemon has not shutdown yet"));
542
+ }
543
+}
544
+
545
+/*
546
+ * Send an IPC command followed by ballast to confirm that a large
547
+ * message can be sent and that the kernel or pkt-line layers will
548
+ * properly chunk it and that the daemon receives the entire message.
549
+ */
550
+static int do_sendbytes(int bytecount, char byte, const char *path,
551
+ const struct ipc_client_connect_options *options)
552
+{
553
+ struct strbuf buf_send = STRBUF_INIT;
554
+ struct strbuf buf_resp = STRBUF_INIT;
555
+
556
+ strbuf_addstr(&buf_send, "sendbytes ");
557
+ strbuf_addchars(&buf_send, byte, bytecount);
558
+
559
+ if (!ipc_client_send_command(path, options, buf_send.buf, &buf_resp)) {
560
+ strbuf_rtrim(&buf_resp);
561
+ printf("sent:%c%08d %s\n", byte, bytecount, buf_resp.buf);
562
+ fflush(stdout);
563
+ strbuf_release(&buf_send);
564
+ strbuf_release(&buf_resp);
565
+
566
+ return 0;
567
+ }
568
+
569
+ return error("client failed to sendbytes(%d, '%c') to '%s'",
570
+ bytecount, byte, path);
571
+}
572
+
573
+/*
574
+ * Send an IPC command with ballast to an already-running server daemon.
575
+ */
576
+static int client__sendbytes(void)
577
+{
578
+ struct ipc_client_connect_options options
579
+ = IPC_CLIENT_CONNECT_OPTIONS_INIT;
580
+
581
+ options.wait_if_busy = 1;
582
+ options.wait_if_not_found = 0;
583
+ options.uds_disallow_chdir = 0;
584
+
585
+ return do_sendbytes(cl_args.bytecount, cl_args.bytevalue, cl_args.path,
586
+ &options);
587
+}
588
+
589
+struct multiple_thread_data {
590
+ pthread_t pthread_id;
591
+ struct multiple_thread_data *next;
592
+ const char *path;
593
+ int bytecount;
594
+ int batchsize;
595
+ int sum_errors;
596
+ int sum_good;
597
+ char letter;
598
+};
599
+
600
+static void *multiple_thread_proc(void *_multiple_thread_data)
601
+{
602
+ struct multiple_thread_data *d = _multiple_thread_data;
603
+ int k;
604
+ struct ipc_client_connect_options options
605
+ = IPC_CLIENT_CONNECT_OPTIONS_INIT;
606
+
607
+ options.wait_if_busy = 1;
608
+ options.wait_if_not_found = 0;
609
+ /*
610
+ * A multi-threaded client should not be randomly calling chdir().
611
+ * The test will pass without this restriction because the test is
612
+ * not otherwise accessing the filesystem, but it makes us honest.
613
+ */
614
+ options.uds_disallow_chdir = 1;
615
+
616
+ trace2_thread_start("multiple");
617
+
618
+ for (k = 0; k < d->batchsize; k++) {
619
+ if (do_sendbytes(d->bytecount + k, d->letter, d->path, &options))
620
+ d->sum_errors++;
621
+ else
622
+ d->sum_good++;
623
+ }
624
+
625
+ trace2_thread_exit();
626
+ return NULL;
627
+}
628
+
629
+/*
630
+ * Start a client-side thread pool. Each thread sends a series of
631
+ * IPC requests. Each request is on a new connection to the server.
632
+ */
633
+static int client__multiple(void)
634
+{
635
+ struct multiple_thread_data *list = NULL;
636
+ int k;
637
+ int sum_join_errors = 0;
638
+ int sum_thread_errors = 0;
639
+ int sum_good = 0;
640
+
641
+ for (k = 0; k < cl_args.nr_threads; k++) {
642
+ struct multiple_thread_data *d = xcalloc(1, sizeof(*d));
643
+ d->next = list;
644
+ d->path = cl_args.path;
645
+ d->bytecount = cl_args.bytecount + cl_args.batchsize*(k/26);
646
+ d->batchsize = cl_args.batchsize;
647
+ d->sum_errors = 0;
648
+ d->sum_good = 0;
649
+ d->letter = 'A' + (k % 26);
650
+
651
+ if (pthread_create(&d->pthread_id, NULL, multiple_thread_proc, d)) {
652
+ warning("failed to create thread[%d] skipping remainder", k);
653
+ free(d);
654
+ break;
655
+ }
656
+
657
+ list = d;
658
+ }
659
+
660
+ while (list) {
661
+ struct multiple_thread_data *d = list;
662
+
663
+ if (pthread_join(d->pthread_id, NULL))
664
+ sum_join_errors++;
665
+
666
+ sum_thread_errors += d->sum_errors;
667
+ sum_good += d->sum_good;
668
+
669
+ list = d->next;
670
+ free(d);
671
+ }
672
+
673
+ printf("client (good %d) (join %d), (errors %d)\n",
674
+ sum_good, sum_join_errors, sum_thread_errors);
675
+
676
+ return (sum_join_errors + sum_thread_errors) ? 1 : 0;
677
+}
678
+
679
+int cmd__simple_ipc(int argc, const char **argv)
680
+{
681
+ const char * const simple_ipc_usage[] = {
682
+ N_("test-helper simple-ipc is-active [<name>] [<options>]"),
683
+ N_("test-helper simple-ipc run-daemon [<name>] [<threads>]"),
684
+ N_("test-helper simple-ipc start-daemon [<name>] [<threads>] [<max-wait>]"),
685
+ N_("test-helper simple-ipc stop-daemon [<name>] [<max-wait>]"),
686
+ N_("test-helper simple-ipc send [<name>] [<token>]"),
687
+ N_("test-helper simple-ipc sendbytes [<name>] [<bytecount>] [<byte>]"),
688
+ N_("test-helper simple-ipc multiple [<name>] [<threads>] [<bytecount>] [<batchsize>]"),
689
+ NULL
690
+ };
691
+
692
+ const char *bytevalue = NULL;
693
+
694
+ struct option options[] = {
695
+#ifndef GIT_WINDOWS_NATIVE
696
+ OPT_STRING(0, "name", &cl_args.path, N_("name"), N_("name or pathname of unix domain socket")),
697
+#else
698
+ OPT_STRING(0, "name", &cl_args.path, N_("name"), N_("named-pipe name")),
699
+#endif
700
+ OPT_INTEGER(0, "threads", &cl_args.nr_threads, N_("number of threads in server thread pool")),
701
+ OPT_INTEGER(0, "max-wait", &cl_args.max_wait_sec, N_("seconds to wait for daemon to start or stop")),
702
+
703
+ OPT_INTEGER(0, "bytecount", &cl_args.bytecount, N_("number of bytes")),
704
+ OPT_INTEGER(0, "batchsize", &cl_args.batchsize, N_("number of requests per thread")),
705
+
706
+ OPT_STRING(0, "byte", &bytevalue, N_("byte"), N_("ballast character")),
707
+ OPT_STRING(0, "token", &cl_args.token, N_("token"), N_("command token to send to the server")),
708
+
709
+ OPT_END()
710
+ };
711
+
712
+ if (argc < 2)
713
+ usage_with_options(simple_ipc_usage, options);
714
+
715
+ if (argc == 2 && !strcmp(argv[1], "-h"))
716
+ usage_with_options(simple_ipc_usage, options);
717
+
718
+ if (argc == 2 && !strcmp(argv[1], "SUPPORTS_SIMPLE_IPC"))
719
+ return 0;
720
+
721
+ cl_args.subcommand = argv[1];
722
+
723
+ argc--;
724
+ argv++;
725
+
726
+ argc = parse_options(argc, argv, NULL, options, simple_ipc_usage, 0);
727
+
728
+ if (cl_args.nr_threads < 1)
729
+ cl_args.nr_threads = 1;
730
+ if (cl_args.max_wait_sec < 0)
731
+ cl_args.max_wait_sec = 0;
732
+ if (cl_args.bytecount < 1)
733
+ cl_args.bytecount = 1;
734
+ if (cl_args.batchsize < 1)
735
+ cl_args.batchsize = 1;
736
+
737
+ if (bytevalue && *bytevalue)
738
+ cl_args.bytevalue = bytevalue[0];
739
+
740
+ /*
741
+ * Use '!!' on all dispatch functions to map from `error()` style
742
+ * (returns -1) style to `test_must_fail` style (expects 1). This
743
+ * makes shell error messages less confusing.
744
+ */
745
+
746
+ if (!strcmp(cl_args.subcommand, "is-active"))
747
+ return !!client__probe_server();
748
+
749
+ if (!strcmp(cl_args.subcommand, "run-daemon"))
750
+ return !!daemon__run_server();
751
+
752
+ if (!strcmp(cl_args.subcommand, "start-daemon"))
753
+ return !!daemon__start_server();
754
+
755
+ /*
756
+ * Client commands follow. Ensure a server is running before
757
+ * sending any data. This might be overkill, but then again
758
+ * this is a test harness.
759
+ */
760
+
761
+ if (!strcmp(cl_args.subcommand, "stop-daemon")) {
762
+ if (client__probe_server())
763
+ return 1;
764
+ return !!client__stop_server();
765
+ }
766
+
767
+ if (!strcmp(cl_args.subcommand, "send")) {
768
+ if (client__probe_server())
769
+ return 1;
770
+ return !!client__send_ipc();
771
+ }
772
+
773
+ if (!strcmp(cl_args.subcommand, "sendbytes")) {
774
+ if (client__probe_server())
775
+ return 1;
776
+ return !!client__sendbytes();
777
+ }
778
+
779
+ if (!strcmp(cl_args.subcommand, "multiple")) {
780
+ if (client__probe_server())
781
+ return 1;
782
+ return !!client__multiple();
783
+ }
784
+
785
+ die("Unhandled subcommand: '%s'", cl_args.subcommand);
786
+}
787
+#endif