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