1ZPROC(3) CZMQ Manual ZPROC(3)
2
3
4
6 zproc - Class for process configuration and status
7
9 // This is a draft class, and may change without notice. It is disabled in
10 // stable builds by default. If you use this in applications, please ask
11 // for it to be pushed to stable state. Use --enable-drafts to enable.
12 #ifdef CZMQ_BUILD_DRAFT_API
13 // *** Draft method, for development use, may change without warning ***
14 // Create a new zproc.
15 // NOTE: On Windows and with libzmq3 and libzmq2 this function
16 // returns NULL. Code needs to be ported there.
17 CZMQ_EXPORT zproc_t *
18 zproc_new (void);
19
20 // *** Draft method, for development use, may change without warning ***
21 // Destroy zproc, wait until process ends.
22 CZMQ_EXPORT void
23 zproc_destroy (zproc_t **self_p);
24
25 // *** Draft method, for development use, may change without warning ***
26 // Return command line arguments (the first item is the executable) or
27 // NULL if not set.
28 // Caller owns return value and must destroy it when done.
29 CZMQ_EXPORT zlist_t *
30 zproc_args (zproc_t *self);
31
32 // *** Draft method, for development use, may change without warning ***
33 // Setup the command line arguments, the first item must be an (absolute) filename
34 // to run.
35 CZMQ_EXPORT void
36 zproc_set_args (zproc_t *self, zlist_t **arguments);
37
38 // *** Draft method, for development use, may change without warning ***
39 // Setup the command line arguments, the first item must be an (absolute) filename
40 // to run. Variadic function, must be NULL terminated.
41 CZMQ_EXPORT void
42 zproc_set_argsx (zproc_t *self, const char *arguments, ...);
43
44 // *** Draft method, for development use, may change without warning ***
45 // Setup the environment variables for the process.
46 CZMQ_EXPORT void
47 zproc_set_env (zproc_t *self, zhash_t **arguments);
48
49 // *** Draft method, for development use, may change without warning ***
50 // Connects process stdin with a readable ('>', connect) zeromq socket. If
51 // socket argument is NULL, zproc creates own managed pair of inproc
52 // sockets. The writable one is then accessbile via zproc_stdin method.
53 CZMQ_EXPORT void
54 zproc_set_stdin (zproc_t *self, void *socket);
55
56 // *** Draft method, for development use, may change without warning ***
57 // Connects process stdout with a writable ('@', bind) zeromq socket. If
58 // socket argument is NULL, zproc creates own managed pair of inproc
59 // sockets. The readable one is then accessbile via zproc_stdout method.
60 CZMQ_EXPORT void
61 zproc_set_stdout (zproc_t *self, void *socket);
62
63 // *** Draft method, for development use, may change without warning ***
64 // Connects process stderr with a writable ('@', bind) zeromq socket. If
65 // socket argument is NULL, zproc creates own managed pair of inproc
66 // sockets. The readable one is then accessbile via zproc_stderr method.
67 CZMQ_EXPORT void
68 zproc_set_stderr (zproc_t *self, void *socket);
69
70 // *** Draft method, for development use, may change without warning ***
71 // Return subprocess stdin writable socket. NULL for
72 // not initialized or external sockets.
73 CZMQ_EXPORT void *
74 zproc_stdin (zproc_t *self);
75
76 // *** Draft method, for development use, may change without warning ***
77 // Return subprocess stdout readable socket. NULL for
78 // not initialized or external sockets.
79 CZMQ_EXPORT void *
80 zproc_stdout (zproc_t *self);
81
82 // *** Draft method, for development use, may change without warning ***
83 // Return subprocess stderr readable socket. NULL for
84 // not initialized or external sockets.
85 CZMQ_EXPORT void *
86 zproc_stderr (zproc_t *self);
87
88 // *** Draft method, for development use, may change without warning ***
89 // Starts the process, return just before execve/CreateProcess.
90 CZMQ_EXPORT int
91 zproc_run (zproc_t *self);
92
93 // *** Draft method, for development use, may change without warning ***
94 // process exit code
95 CZMQ_EXPORT int
96 zproc_returncode (zproc_t *self);
97
98 // *** Draft method, for development use, may change without warning ***
99 // PID of the process
100 CZMQ_EXPORT int
101 zproc_pid (zproc_t *self);
102
103 // *** Draft method, for development use, may change without warning ***
104 // return true if process is running, false if not yet started or finished
105 CZMQ_EXPORT bool
106 zproc_running (zproc_t *self);
107
108 // *** Draft method, for development use, may change without warning ***
109 // The timeout should be zero or greater, or -1 to wait indefinitely.
110 // wait or poll process status, return return code
111 CZMQ_EXPORT int
112 zproc_wait (zproc_t *self, int timeout);
113
114 // *** Draft method, for development use, may change without warning ***
115 // send SIGTERM signal to the subprocess, wait for grace period and
116 // eventually send SIGKILL
117 CZMQ_EXPORT void
118 zproc_shutdown (zproc_t *self, int timeout);
119
120 // *** Draft method, for development use, may change without warning ***
121 // return internal actor, useful for the polling if process died
122 CZMQ_EXPORT void *
123 zproc_actor (zproc_t *self);
124
125 // *** Draft method, for development use, may change without warning ***
126 // send a signal to the subprocess
127 CZMQ_EXPORT void
128 zproc_kill (zproc_t *self, int signal);
129
130 // *** Draft method, for development use, may change without warning ***
131 // set verbose mode
132 CZMQ_EXPORT void
133 zproc_set_verbose (zproc_t *self, bool verbose);
134
135 // *** Draft method, for development use, may change without warning ***
136 // Self test of this class.
137 CZMQ_EXPORT void
138 zproc_test (bool verbose);
139
140 #endif // CZMQ_BUILD_DRAFT_API
141 Please add '@interface' section in './../src/zproc.c'.
142
144 zproc - process configuration and status, plus unix pipes on steroids
145
146 Warning
147 zproc class have several limitations atm * is tested on zmq4 on
148 Linux and OSX. * does not work on Windows, where you get empty
149 stubs for most of the methods * does not work on libzmq3 and
150 libzmq2. We have experienced stalls and timeouts when running tests
151 against such old version
152
153 Note: zproc is not yet stable, so there are no guarantees regarding API
154 stability. Some methods can have weird semantics or strange API.
155
156 Class zproc run an external process and to use ZeroMQ sockets to
157 communicate with it. In other words standard input and outputs MAY be
158 connected with appropriate zeromq socket and data flow is managed by
159 zproc itself. This makes zproc the best in class way how to run and
160 manage sub processes.
161
162 Data are sent and received as zframes (zframe_t), so zproc does not try
163 to interpret content of the messages in any way. See test example on
164 how to use it.
165
166 +----------------------------------------+
167 | /bin/cat cat /etc/passwd |
168 | stdin | stdout | stderr |
169 |------||--------||---------------||-----|
170 | fd1 fd2 fd3 |
171 | ^ v v |
172 |zmq://stdin |zmq://stdout |zmq://stderr |
173 | [zproc supervisor] |
174 +----------------------------------------+
175
176 ----------> zeromq magic here <-----------
177
178 +----------------------------------------+
179 |zmq://stdin |zmq://stdout |zmq://stderr |
180 | |
181 | consumer |
182 | |
183 | |
184 +----------------------------------------+
185
186 Please add @discuss section in ./../src/zproc.c.
187
189 From zproc_test method.
190
191 // variable file contains path to zsp executable:
192 // char *file = "path/to/zsp";
193
194 #if defined (__WINDOWS__)
195 printf ("Very limited (on Windows) ");
196 {
197 zsys_init ();
198 zproc_t *self = zproc_new ();
199 assert (self);
200
201 zproc_set_verbose (self, verbose);
202 zproc_set_argsx (self, file, "-v", NULL);
203 zproc_run (self);
204 zclock_sleep (100); // to let actor start the process
205 assert (zproc_pid (self));
206
207 zproc_kill (self, SIGTERM);
208 assert (zproc_returncode (self) == 255);
209 zproc_destroy (&self);
210 }
211 printf ("OK\n");
212 return;
213 #endif
214 {
215 // Test case #1: run command, wait until it ends and get the (stdandard) output
216 zproc_t *self = zproc_new ();
217 assert (self);
218 zproc_set_verbose (self, verbose);
219
220 // join stdout of the process to zeromq socket
221 // all data will be readable from zproc_stdout socket
222 assert (!zproc_stdout (self));
223 zproc_set_stdout (self, NULL);
224 assert (zproc_stdout (self));
225
226 zproc_set_argsx (self, file, "--help", NULL);
227
228 if (verbose)
229 zsys_debug("zproc_test() : launching helper '%s' --help", file );
230
231 int r = zproc_run (self);
232 assert (r == 0);
233 zframe_t *frame;
234 zsock_brecv (zproc_stdout (self), "f", &frame);
235 assert (frame);
236 assert (zframe_data (frame));
237 // TODO: real test
238 if (verbose)
239 zframe_print (frame, "1:");
240 zframe_destroy (&frame);
241 r = zproc_wait (self, -1);
242 assert (r == 0);
243 zproc_destroy (&self);
244 }
245
246 {
247 // Test case#2: run zsp helper with a content written on stdin, check if it was passed to stdout
248 zproc_t *self = zproc_new ();
249 assert (self);
250 zproc_set_verbose (self, verbose);
251 // forward input from stdin to stderr
252 zproc_set_argsx (self, file, "--stdin", "--stderr", NULL);
253 // FIXME: there is a BUG in zproc somewhere, you can't gen an output from both stdout/stderr
254 //zproc_set_argsx (self, file, "--stdin", "--stdout", "--stderr", NULL);
255 zproc_set_stdin (self, NULL);
256 // FIXME: the bug
257 //zproc_set_stdout (self, NULL);
258 zproc_set_stderr (self, NULL);
259
260 // send data to stdin
261 int r = zproc_run (self);
262 assert (r == 0);
263 zframe_t *frame = zframe_new ("Lorem ipsum\0\0", strlen ("Lorem ipsum")+2);
264 assert (frame);
265 zsock_bsend (zproc_stdin (self), "f", frame);
266 zframe_destroy (&frame);
267
268 // FIXME: the bug
269 //zproc_set_stdout (self, NULL);
270 // read data from stdout
271 /*
272 zsys_debug ("BAF1");
273 zsock_brecv (zproc_stdout (self), "f", &frame);
274 zsys_debug ("BAF2");
275 assert (frame);
276 assert (zframe_data (frame));
277 if (verbose)
278 zframe_print (frame, "2.stdout:");
279 assert (!strncmp ((char*) zframe_data (frame), "Lorem ipsum", 11));
280 */
281
282 // read data from stderr
283 zsock_brecv (zproc_stderr (self), "f", &frame);
284 assert (frame);
285 assert (zframe_data (frame));
286 if (verbose)
287 zframe_print (frame, "2.stderr:");
288 assert (!strncmp ((char*) zframe_data (frame), "Lorem ipsum", 11));
289 zproc_kill (self, SIGTERM);
290 zproc_wait (self, -1);
291 zframe_destroy (&frame);
292 zproc_destroy (&self);
293 }
294
295 {
296 // Test case#3: run non existing binary
297 zproc_t *self = zproc_new ();
298 assert (self);
299 zproc_set_verbose (self, verbose);
300 // forward input from stdin to stderr
301 zproc_set_argsx (self, "/not/existing/file", NULL);
302
303 int r = zproc_run (self);
304 assert (r == -1);
305 zproc_destroy (&self);
306 }
307
308 {
309 // Test case #4: child abort itself
310 zproc_t *self = zproc_new ();
311 assert (self);
312 zproc_set_verbose (self, verbose);
313 zproc_set_argsx (self, file, "--verbose", "--abrt", NULL);
314 zproc_set_stdout (self, NULL);
315 zproc_set_stderr (self, NULL);
316 zproc_set_stdin (self, NULL);
317
318 int r = zproc_run (self);
319 zclock_sleep (100); // to let actor start the process
320 assert (r != -1);
321 zclock_sleep (100);
322 zframe_t *frame;
323 zsock_brecv (zproc_stdout (self), "f", &frame);
324 assert (zframe_is (frame));
325 assert (zframe_size (frame) > 0);
326 zframe_destroy (&frame);
327 zproc_wait (self, -1);
328 assert (zproc_returncode (self) == -SIGABRT);
329 zproc_destroy (&self);
330 }
331
332 {
333 // Test case #5: use never ending subprocess and poller to read data from it
334 // Create new zproc instance
335 zproc_t *self = zproc_new ();
336 assert (self);
337 zproc_set_verbose (self, verbose);
338 // join stdout of the process to zeromq socket
339 // all data will be readable from zproc_stdout socket
340 zproc_set_stdout (self, NULL);
341
342 zlist_t *args = zlist_new ();
343 zlist_autofree (args);
344 zlist_append (args, file);
345 zlist_append (args, "--stdout");
346 zproc_set_args (self, &args);
347
348 zhash_t *env = zhash_new ();
349 zhash_autofree (env);
350 zhash_insert (env, "ZSP_MESSAGE", "czmq is great\n");
351 zproc_set_env (self, &env);
352
353 // execute the binary. It runs in own actor, which monitor the process and
354 // pass data accross pipes and zeromq sockets
355 if (verbose)
356 zsys_debug("zproc_test() : launching helper '%s'", file );
357 zproc_run (self);
358 zpoller_t *poller = zpoller_new (zproc_stdout (self), NULL);
359
360 // kill the binary, it never ends, but the test must:
361 // termination also flushes the output streams so we can
362 // read them entirely; note that other process runs in
363 // parallel to this thread
364 if (verbose)
365 zsys_debug("zproc_test() : sleeping 4000 msec to gather some output from helper");
366 zclock_sleep (4000);
367 zproc_kill (self, SIGTERM);
368 zproc_wait (self, -1);
369
370 // read the content from zproc_stdout - use zpoller and a loop
371 bool stdout_read = false;
372 int64_t zproc_timeout_msec = 10000;
373 int64_t zproc_test_start_msec = zclock_mono();
374 int64_t zproc_test_elapsed_msec = 0;
375
376 while (!zsys_interrupted) {
377 void *which = zpoller_wait (poller, 800);
378 zproc_test_elapsed_msec = zclock_mono() - zproc_test_start_msec;
379
380 if (!which) {
381 if (stdout_read) {
382 if (verbose)
383 zsys_debug("zproc_test() : did not get stdout from helper, but we already have some (%" PRIi64 " msec remaining to retry)", (zproc_timeout_msec - zproc_test_elapsed_msec) );
384 break;
385 }
386 if (zproc_timeout_msec > zproc_test_elapsed_msec) {
387 if (verbose)
388 zsys_debug("zproc_test() : did not get stdout from helper, %" PRIi64 " msec remaining to retry", (zproc_timeout_msec - zproc_test_elapsed_msec) );
389 continue;
390 }
391 // ...else : we've slept a lot and got no response; kill the helper
392 if (verbose)
393 zsys_debug("zproc_test() : did not get stdout from helper, patience expired (%" PRIi64 " msec remaining to retry)", (zproc_timeout_msec - zproc_test_elapsed_msec) );
394 break;
395 }
396
397 if (which == zproc_stdout (self)) {
398 // it suffices for us to have read something
399 // we only check the first frame, others may start with the
400 // expected key string broken mid-way due to alignment etc.,
401 // but we drain the whole incoming queue of stdout frames.
402 zframe_t *frame;
403 zsock_brecv (zproc_stdout (self), "f", &frame);
404 assert (frame);
405 assert (zframe_data (frame));
406 if (!stdout_read) {
407 if (verbose)
408 zsys_debug("zproc_test() : got stdout from helper, %" PRIi64 " msec was remaining to retry", (zproc_timeout_msec - zproc_test_elapsed_msec));
409 assert (!strncmp(
410 "czmq is great\n",
411 (char*) zframe_data (frame),
412 14));
413 stdout_read = true;
414 }
415
416 if (verbose)
417 zframe_print (frame, "zproc_test");
418
419 zframe_destroy (&frame);
420 continue;
421 }
422
423 // should not get there
424 if (verbose)
425 zsys_debug("zproc_test() : reached the unreachable point (unexpected zpoller result), %" PRIi64 " msec was remaining to retry", (zproc_timeout_msec - zproc_test_elapsed_msec) );
426 assert (false);
427 }
428
429 assert (stdout_read);
430 zpoller_destroy (&poller);
431 zproc_destroy (&self);
432 }
433 {
434 // testcase #6 wait for process that hangs, kill it
435 zproc_t *self = zproc_new ();
436 assert (self);
437 zproc_set_verbose (self, verbose);
438
439 zproc_set_argsx (self, file, NULL);
440
441 if (verbose)
442 zsys_debug("zproc_test() : launching helper '%s'", file);
443
444 int r = zproc_run (self);
445 assert (r == 0);
446 r = zproc_wait (self, 1000);
447 assert (r == ZPROC_RUNNING);
448 assert (zproc_running (self));
449 zproc_shutdown (self, 1000);
450 assert (!zproc_running (self));
451 zproc_destroy (&self);
452 }
453 {
454 // testcase #7 wait for process that exits earlier
455 zproc_t *self = zproc_new ();
456 assert (self);
457 zproc_set_verbose (self, verbose);
458
459 zproc_set_argsx (self, file, "--quit", "1", NULL);
460
461 if (verbose)
462 zsys_debug("zproc_test() : launching helper '%s' --quit 1", file);
463
464 int r = zproc_run (self);
465 assert (r == 0);
466 int t = zclock_mono ();
467 r = zproc_wait (self, 8000);
468 assert (r == 0);
469 t = zclock_mono () - t;
470 assert (t < 2000);
471 zproc_destroy (&self);
472 }
473
474
476 The czmq manual was written by the authors in the AUTHORS file.
477
479 Main web site:
480
481 Report bugs to the email <zeromq-dev@lists.zeromq.org[1]>
482
484 Copyright (c) the Contributors as noted in the AUTHORS file. This file
485 is part of CZMQ, the high-level C binding for 0MQ:
486 http://czmq.zeromq.org. This Source Code Form is subject to the terms
487 of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
488 distributed with this file, You can obtain one at
489 http://mozilla.org/MPL/2.0/. LICENSE included with the czmq
490 distribution.
491
493 1. zeromq-dev@lists.zeromq.org
494 mailto:zeromq-dev@lists.zeromq.org
495
496
497
498CZMQ 4.2.1 07/19/2023 ZPROC(3)