1 /*
2 * Copyright (c) 2015-2018 Red Hat, Inc.
3 *
4 * All rights reserved.
5 *
6 * Author: Jan Friesse (jfriesse@redhat.com)
7 *
8 * This software licensed under BSD license, the text of which follows:
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * - Neither the name of the Red Hat, Inc. nor the names of its
19 * contributors may be used to endorse or promote products derived from this
20 * software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <sys/types.h>
36 #include <sys/stat.h>
37
38 #include <stdio.h>
39 #include <assert.h>
40 #include <string.h>
41 #include <poll.h>
42 #include <signal.h>
43 #include <stdlib.h>
44 #include <limits.h>
45 #include <unistd.h>
46
47 #include "process-list.h"
48
49 /*
50 * 10 min timeout
51 */
52 #define WAIT_FOR_NO_RUNNING_REPEATS 6000
53 #define WAIT_FOR_NO_RUNNING_TIMEOUT 600000
54 /*
55 * 1 sec timeout
56 */
57 /*
58 #define WAIT_FOR_NO_RUNNING_REPEATS 100
59 #define WAIT_FOR_NO_RUNNING_TIMEOUT 1000
60 */
61
62 static int no_executed;
63 static int no_finished;
64 static volatile sig_atomic_t sigusr1_received;
65 static volatile sig_atomic_t sigusr2_received;
66
67 static void
signal_usr1_handler(int sig)68 signal_usr1_handler(int sig)
69 {
70
71 sigusr1_received = 1;
72 }
73
74 static void
signal_usr2_handler(int sig)75 signal_usr2_handler(int sig)
76 {
77
78 sigusr2_received = 1;
79 }
80
81 static void
signal_handlers_register(void)82 signal_handlers_register(void)
83 {
84 struct sigaction act;
85
86 act.sa_handler = SIG_DFL;
87 sigemptyset(&act.sa_mask);
88 act.sa_flags = SA_RESTART;
89
90 sigaction(SIGCHLD, &act, NULL);
91
92 act.sa_handler = SIG_IGN;
93 sigemptyset(&act.sa_mask);
94 act.sa_flags = SA_RESTART;
95
96 sigaction(SIGPIPE, &act, NULL);
97
98 act.sa_handler = signal_usr1_handler;
99 sigemptyset(&act.sa_mask);
100 act.sa_flags = SA_RESTART;
101
102 sigaction(SIGUSR1, &act, NULL);
103
104 act.sa_handler = signal_usr2_handler;
105 sigemptyset(&act.sa_mask);
106 act.sa_flags = SA_RESTART;
107
108 sigaction(SIGUSR2, &act, NULL);
109 }
110
111 static void
plist_notify(enum process_list_notify_reason reason,const struct process_list_entry * entry,void * user_data)112 plist_notify(enum process_list_notify_reason reason, const struct process_list_entry *entry,
113 void *user_data)
114 {
115
116 assert(user_data == (void *)0x42);
117
118 switch (reason) {
119 case PROCESS_LIST_NOTIFY_REASON_EXECUTED:
120 no_executed++;
121 break;
122 case PROCESS_LIST_NOTIFY_REASON_FINISHED:
123 no_finished++;
124 break;
125 }
126 }
127
128 static char *
find_exec_path(const char * exec)129 find_exec_path(const char *exec)
130 {
131 struct stat stat_buf;
132 char *res_path;
133 int res;
134
135 assert((res_path = malloc(PATH_MAX)) != NULL);
136 memset(res_path, 0, PATH_MAX);
137
138 res = snprintf(res_path, PATH_MAX, "/bin/%s", exec);
139 assert(res > 0 && res < PATH_MAX);
140 if (stat(res_path, &stat_buf) == 0 && (stat_buf.st_mode & S_IXUSR)) {
141 return (res_path);
142 }
143
144 res = snprintf(res_path, PATH_MAX, "/usr/bin/%s", exec);
145 assert(res > 0 && res < PATH_MAX);
146 if (stat(res_path, &stat_buf) == 0 && (stat_buf.st_mode & S_IXUSR)) {
147 return (res_path);
148 }
149
150 return (NULL);
151 }
152
153 static int
wait_for_no_running(struct process_list * plist,int no_running,int no_in_kill_list)154 wait_for_no_running(struct process_list *plist, int no_running, int no_in_kill_list)
155 {
156 int timeout;
157 int no_repeats;
158 int i;
159
160 no_repeats = WAIT_FOR_NO_RUNNING_REPEATS;
161 timeout = WAIT_FOR_NO_RUNNING_TIMEOUT / no_repeats;
162
163 for (i = 0; i < no_repeats; i++) {
164 assert(process_list_waitpid(plist) == 0);
165 if (process_list_get_no_running(plist) == no_running &&
166 process_list_get_kill_list_items(plist) == no_in_kill_list) {
167 return (0);
168 }
169
170 poll(NULL, 0, timeout);
171 }
172
173 return (-1);
174 }
175
176 static int
wait_for_sigusrs_received(void)177 wait_for_sigusrs_received(void)
178 {
179 int timeout;
180 int no_repeats;
181 int i;
182
183 no_repeats = WAIT_FOR_NO_RUNNING_REPEATS;
184 timeout = WAIT_FOR_NO_RUNNING_TIMEOUT / no_repeats;
185
186 for (i = 0; i < no_repeats; i++) {
187 if (sigusr1_received && sigusr2_received) {
188 return (0);
189 }
190
191 poll(NULL, 0, timeout);
192 }
193
194 return (-1);
195 }
196
197
198 int
main(void)199 main(void)
200 {
201 struct process_list plist;
202 struct process_list_entry *plist_entry;
203 char *true_path, *false_path;
204 char ignore_sigint_cmd[PATH_MAX];
205 char ignore_sigintterm_cmd[PATH_MAX];
206
207 assert(snprintf(ignore_sigint_cmd, PATH_MAX,
208 "bash -c \"trap 'echo trap' SIGINT;kill -USR1 %ld;while true;do sleep 1;done\"",
209 (long int)getpid()) < PATH_MAX);
210
211 assert(snprintf(ignore_sigintterm_cmd, PATH_MAX,
212 "bash -c \"trap 'echo trap' SIGINT SIGTERM;kill -USR2 %ld;while true;do sleep 1;done\"",
213 (long int)getpid()) < PATH_MAX);
214
215 assert((true_path = find_exec_path("true")) != NULL);
216 assert((false_path = find_exec_path("false")) != NULL);
217
218 signal_handlers_register();
219
220 process_list_init(&plist, 10, 1, plist_notify, (void *)0x42);
221 plist_entry = process_list_add(&plist, "test name", "command");
222 assert(plist_entry != NULL);
223 assert(strcmp(plist_entry->name, "test name") == 0);
224 assert(plist_entry->state == PROCESS_LIST_ENTRY_STATE_INITIALIZED);
225 assert(plist_entry->exec_argc == 1);
226 assert(plist_entry->exec_argv[0] != NULL && strcmp(plist_entry->exec_argv[0], "command") == 0);
227 assert(plist_entry->exec_argv[1] == NULL);
228
229 plist_entry = process_list_add(&plist, "test name", "/bin/ping -c \"host wit\\\"h space\" notaspace");
230 assert(plist_entry != NULL);
231 assert(strcmp(plist_entry->name, "test name") == 0);
232 assert(plist_entry->state == PROCESS_LIST_ENTRY_STATE_INITIALIZED);
233 assert(plist_entry->exec_argc == 4);
234 assert(plist_entry->exec_argv[0] != NULL && strcmp(plist_entry->exec_argv[0], "/bin/ping") == 0);
235 assert(plist_entry->exec_argv[1] != NULL && strcmp(plist_entry->exec_argv[1], "-c") == 0);
236 assert(plist_entry->exec_argv[2] != NULL && strcmp(plist_entry->exec_argv[2], "host wit\"h space") == 0);
237 assert(plist_entry->exec_argv[3] != NULL && strcmp(plist_entry->exec_argv[3], "notaspace") == 0);
238 assert(plist_entry->exec_argv[4] == NULL);
239
240 process_list_free(&plist);
241
242 /*
243 * Test no process
244 */
245 no_executed = 0;
246 no_finished = 0;
247 assert(process_list_exec_initialized(&plist) == 0);
248 assert(no_executed == 0);
249 assert(process_list_get_no_running(&plist) == 0);
250
251 assert(wait_for_no_running(&plist, 0, 0) == 0);
252
253 assert(no_finished == 0);
254 assert(process_list_get_summary_result(&plist) == 0);
255 assert(process_list_get_summary_result_short(&plist) == 0);
256
257 process_list_free(&plist);
258
259 /*
260 * Test two processes. /bin/true and /bin/false. Accumulated result should be fail
261 */
262 plist_entry = process_list_add(&plist, "true", true_path);
263 assert(plist_entry != NULL);
264
265 plist_entry = process_list_add(&plist, "false", false_path);
266 assert(plist_entry != NULL);
267
268 no_executed = 0;
269 no_finished = 0;
270 assert(process_list_exec_initialized(&plist) == 0);
271 assert(no_executed == 2);
272 assert(process_list_get_no_running(&plist) == 2);
273
274 /*
275 * Wait to exit
276 */
277 assert(wait_for_no_running(&plist, 0, 0) == 0);
278
279 assert(process_list_waitpid(&plist) == 0);
280 assert(process_list_get_no_running(&plist) == 0);
281 assert(no_finished == 2);
282 assert(process_list_get_summary_result(&plist) == 1);
283 assert(process_list_get_summary_result_short(&plist) == 1);
284
285 process_list_free(&plist);
286
287 /*
288 * Test two processes. /bin/true and one non-existing. Accumulated result should be fail
289 */
290 plist_entry = process_list_add(&plist, "true", true_path);
291 assert(plist_entry != NULL);
292
293 plist_entry = process_list_add(&plist, "false", "/nonexistingdir/nonexistingfile");
294 assert(plist_entry != NULL);
295
296 no_executed = 0;
297 no_finished = 0;
298 assert(process_list_exec_initialized(&plist) == 0);
299 assert(no_executed == 2);
300 assert(process_list_get_no_running(&plist) == 2);
301
302 /*
303 * Wait to exit
304 */
305 assert(wait_for_no_running(&plist, 0, 0) == 0);
306
307 assert(no_finished == 2);
308 assert(process_list_get_summary_result(&plist) == 1);
309 assert(process_list_get_summary_result_short(&plist) == 1);
310
311 process_list_free(&plist);
312
313 /*
314 * Test three processes /bin/true. Accumulated result should be success.
315 */
316 plist_entry = process_list_add(&plist, "true", true_path);
317 assert(plist_entry != NULL);
318
319 plist_entry = process_list_add(&plist, "true2", true_path);
320 assert(plist_entry != NULL);
321
322 plist_entry = process_list_add(&plist, "true3", true_path);
323 assert(plist_entry != NULL);
324
325 no_executed = 0;
326 no_finished = 0;
327 assert(process_list_exec_initialized(&plist) == 0);
328 assert(no_executed == 3);
329 assert(process_list_get_no_running(&plist) == 3);
330
331 /*
332 * Wait to exit
333 */
334 assert(wait_for_no_running(&plist, 0, 0) == 0);
335
336 assert(no_finished == 3);
337 assert(process_list_get_summary_result(&plist) == 0);
338 assert(process_list_get_summary_result_short(&plist) == 0);
339
340 process_list_free(&plist);
341
342 /*
343 * Test two processes. /bin/true and cat. Cat blocks so test kill list
344 */
345 plist_entry = process_list_add(&plist, "true", true_path);
346 assert(plist_entry != NULL);
347
348 plist_entry = process_list_add(&plist, "cat", "/bin/cat /dev/zero");
349 assert(plist_entry != NULL);
350
351 no_executed = 0;
352 no_finished = 0;
353 assert(process_list_exec_initialized(&plist) == 0);
354 assert(no_executed == 2);
355 assert(process_list_get_no_running(&plist) == 2);
356
357 assert(wait_for_no_running(&plist, 1, 0) == 0);
358
359 assert(no_finished == 1);
360 assert(process_list_get_summary_result(&plist) == -1);
361 assert(process_list_get_summary_result_short(&plist) == -1);
362
363 process_list_move_active_entries_to_kill_list(&plist);
364 assert(process_list_process_kill_list(&plist) == 0);
365 /*
366 * There should be 0 running and 0 in kill list
367 */
368 assert(wait_for_no_running(&plist, 0, 0) == 0);
369
370 assert(process_list_get_kill_list_items(&plist) == 0);
371
372 assert(process_list_process_kill_list(&plist) == 0);
373
374 process_list_free(&plist);
375
376 /*
377 * Test two bash proceses. One ignores INT and second ignores INT and TERM.
378 */
379 sigusr1_received = 0;
380 plist_entry = process_list_add(&plist, "ignoresig1", ignore_sigint_cmd);
381 assert(plist_entry != NULL);
382
383 sigusr2_received = 0;
384 plist_entry = process_list_add(&plist, "ignoresig2", ignore_sigintterm_cmd);
385 assert(plist_entry != NULL);
386
387 no_executed = 0;
388 no_finished = 0;
389 assert(process_list_exec_initialized(&plist) == 0);
390 assert(no_executed == 2);
391 assert(process_list_get_no_running(&plist) == 2);
392 assert(wait_for_sigusrs_received() == 0);
393
394 /*
395 * Wait some time. 2 processes should be running
396 */
397 poll(NULL, 0, 500);
398 assert(process_list_waitpid(&plist) == 0);
399
400 assert(process_list_get_no_running(&plist) == 2);
401 assert(no_finished == 0);
402 assert(process_list_get_summary_result(&plist) == -1);
403 assert(process_list_get_summary_result_short(&plist) == -1);
404
405 process_list_move_active_entries_to_kill_list(&plist);
406 assert(wait_for_no_running(&plist, 0, 2) == 0);
407 assert(process_list_process_kill_list(&plist) == 0);
408 assert(wait_for_no_running(&plist, 0, 1) == 0);
409
410 assert(process_list_process_kill_list(&plist) == 0);
411 assert(wait_for_no_running(&plist, 0, 0) == 0);
412
413 process_list_free(&plist);
414
415 /*
416 * Test 3 processes. Test if entries are properly deallocated
417 */
418 process_list_init(&plist, 3, 1, plist_notify, (void *)0x42);
419 plist_entry = process_list_add(&plist, "true", true_path);
420 assert(plist_entry != NULL);
421
422 plist_entry = process_list_add(&plist, "true2", true_path);
423 assert(plist_entry != NULL);
424
425 plist_entry = process_list_add(&plist, "true3", true_path);
426 assert(plist_entry != NULL);
427
428 /*
429 * Insert fails
430 */
431 plist_entry = process_list_add(&plist, "true4", true_path);
432 assert(plist_entry == NULL);
433
434 no_executed = 0;
435 no_finished = 0;
436 assert(process_list_exec_initialized(&plist) == 0);
437 assert(no_executed == 3);
438 assert(process_list_get_no_running(&plist) == 3);
439
440 /*
441 * Wait to exit
442 */
443 assert(wait_for_no_running(&plist, 0, 0) == 0);
444
445 assert(no_finished == 3);
446 assert(process_list_get_summary_result(&plist) == 0);
447 assert(process_list_get_summary_result_short(&plist) == 0);
448
449 process_list_move_active_entries_to_kill_list(&plist);
450
451 plist_entry = process_list_add(&plist, "true", true_path);
452 assert(plist_entry != NULL);
453
454 sigusr1_received = 0;
455 plist_entry = process_list_add(&plist, "ignoresig1", ignore_sigint_cmd);
456 assert(plist_entry != NULL);
457
458 sigusr2_received = 0;
459 plist_entry = process_list_add(&plist, "ignoresig2", ignore_sigintterm_cmd);
460 assert(plist_entry != NULL);
461
462 plist_entry = process_list_add(&plist, "true4", true_path);
463 assert(plist_entry == NULL);
464
465 no_executed = 0;
466 no_finished = 0;
467 assert(process_list_exec_initialized(&plist) == 0);
468 assert(no_executed == 3);
469 assert(process_list_get_no_running(&plist) == 3);
470 assert(wait_for_sigusrs_received() == 0);
471
472 assert(wait_for_no_running(&plist, 2, 0) == 0);
473
474 assert(process_list_get_no_running(&plist) == 2);
475 assert(no_finished == 1);
476 assert(process_list_get_summary_result(&plist) == -1);
477 assert(process_list_get_summary_result_short(&plist) == -1);
478
479 plist_entry = process_list_add(&plist, "true4", true_path);
480 assert(plist_entry == NULL);
481
482 process_list_move_active_entries_to_kill_list(&plist);
483
484 plist_entry = process_list_add(&plist, "true4", true_path);
485 assert(plist_entry != NULL);
486
487 plist_entry = process_list_add(&plist, "true5", true_path);
488 assert(plist_entry == NULL);
489
490 assert(process_list_process_kill_list(&plist) == 0);
491 assert(wait_for_no_running(&plist, 0, 1) == 0);
492
493 assert(process_list_process_kill_list(&plist) == 0);
494 assert(wait_for_no_running(&plist, 0, 0) == 0);
495
496 process_list_move_active_entries_to_kill_list(&plist);
497 assert(process_list_get_summary_result(&plist) == 0);
498 assert(process_list_get_summary_result_short(&plist) == 0);
499
500 plist_entry = process_list_add(&plist, "true", true_path);
501 assert(plist_entry != NULL);
502
503 plist_entry = process_list_add(&plist, "true2", true_path);
504 assert(plist_entry != NULL);
505
506 plist_entry = process_list_add(&plist, "true3", true_path);
507 assert(plist_entry != NULL);
508
509 plist_entry = process_list_add(&plist, "true4", true_path);
510 assert(plist_entry == NULL);
511
512 process_list_free(&plist);
513
514 /*
515 * Test 3 processes and difference between summary and short-circuit summary
516 */
517 process_list_init(&plist, 3, 1, plist_notify, (void *)0x42);
518 plist_entry = process_list_add(&plist, "true", true_path);
519 assert(plist_entry != NULL);
520
521 plist_entry = process_list_add(&plist, "false", false_path);
522 assert(plist_entry != NULL);
523
524 plist_entry = process_list_add(&plist, "loop", "bash -c \"while true;do sleep 1;done\"");
525 assert(plist_entry != NULL);
526
527 plist_entry = process_list_add(&plist, "true4", true_path);
528 assert(plist_entry == NULL);
529
530 no_executed = 0;
531 no_finished = 0;
532 assert(process_list_exec_initialized(&plist) == 0);
533 assert(no_executed == 3);
534 assert(process_list_get_no_running(&plist) == 3);
535
536 /*
537 * Wait to exit
538 */
539 assert(wait_for_no_running(&plist, 1, 0) == 0);
540
541 assert(no_finished == 2);
542 assert(process_list_get_summary_result(&plist) == -1);
543 assert(process_list_get_summary_result_short(&plist) == 1);
544
545 process_list_move_active_entries_to_kill_list(&plist);
546 assert(process_list_process_kill_list(&plist) == 0);
547 assert(wait_for_no_running(&plist, 0, 0) == 0);
548
549 process_list_free(&plist);
550
551 /*
552 * Test process_list_killall by running two bash proceses.
553 * One ignores INT and second ignores INT and TERM. Waiting for maximum of 2 sec
554 */
555 sigusr1_received = 0;
556 plist_entry = process_list_add(&plist, "ignoresig1", ignore_sigint_cmd);
557 assert(plist_entry != NULL);
558
559 sigusr2_received = 0;
560 plist_entry = process_list_add(&plist, "ignoresig2", ignore_sigintterm_cmd);
561 assert(plist_entry != NULL);
562
563 no_executed = 0;
564 no_finished = 0;
565 assert(process_list_exec_initialized(&plist) == 0);
566 assert(no_executed == 2);
567 assert(process_list_get_no_running(&plist) == 2);
568 assert(wait_for_sigusrs_received() == 0);
569
570 /*
571 * Ensure processes are running after pause
572 */
573 poll(NULL, 0, 500);
574 assert(process_list_waitpid(&plist) == 0);
575
576 assert(process_list_get_no_running(&plist) == 2);
577 assert(no_finished == 0);
578 assert(process_list_get_summary_result(&plist) == -1);
579 assert(process_list_get_summary_result_short(&plist) == -1);
580
581 assert(process_list_killall(&plist, 2000) == 0);
582 assert(process_list_get_kill_list_items(&plist) == 0);
583
584 process_list_free(&plist);
585
586 /*
587 * Empty killall exits with sucess result
588 */
589 assert(process_list_killall(&plist, 2000) == 0);
590
591 process_list_free(&plist);
592
593 return (0);
594 }
595