2 * Copyright (c) 2015-2020 Red Hat, Inc.
6 * Author: Jan Friesse (jfriesse@redhat.com)
8 * This software licensed under BSD license, the text of which follows:
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
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.
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.
35 #include <sys/types.h>
49 #include "dynar-str.h"
50 #include "dynar-simple-lex.h"
51 #include "process-list.h"
53 static void process_list_free_argv(size_t no_params
, char **argv
);
55 static void process_list_entry_free(struct process_list_entry
*entry
);
57 static char **process_list_parse_command(const char *command
, size_t *no_params
);
59 static int process_list_entry_exec(const struct process_list
*plist
,
60 struct process_list_entry
*entry
);
63 process_list_init(struct process_list
*plist
, size_t max_list_entries
, int use_execvp
,
64 process_list_notify_fn_t notify_fn
, void *notify_fn_user_data
)
67 memset(plist
, 0, sizeof(*plist
));
69 plist
->max_list_entries
= max_list_entries
;
70 plist
->allocated_list_entries
= 0;
71 plist
->use_execvp
= use_execvp
;
72 plist
->notify_fn
= notify_fn
;
73 plist
->notify_fn_user_data
= notify_fn_user_data
;
75 TAILQ_INIT(&plist
->active_list
);
76 TAILQ_INIT(&plist
->to_kill_list
);
80 process_list_free_argv(size_t no_params
, char **argv
)
84 for (zi
= 0; zi
< no_params
; zi
++) {
91 process_list_entry_free(struct process_list_entry
*entry
)
94 process_list_free_argv(entry
->exec_argc
, entry
->exec_argv
);
100 process_list_parse_command(const char *command
, size_t *no_params
)
102 struct dynar command_dstr
;
103 struct dynar_simple_lex lex
;
111 dynar_init(&command_dstr
, strlen(command
) + 1);
112 if (dynar_str_cpy(&command_dstr
, command
) != 0) {
116 dynar_simple_lex_init(&lex
, &command_dstr
, DYNAR_SIMPLE_LEX_TYPE_QUOTE
);
121 token
= dynar_simple_lex_token_next(&lex
);
126 if (strcmp(dynar_data(token
), "") == 0) {
133 if (*no_params
< 1) {
137 dynar_simple_lex_destroy(&lex
);
139 res_argv
= malloc(sizeof(char *) * (*no_params
+ 1));
140 if (res_argv
== NULL
) {
143 memset(res_argv
, 0, sizeof(char *) * (*no_params
+ 1));
145 dynar_simple_lex_init(&lex
, &command_dstr
, DYNAR_SIMPLE_LEX_TYPE_QUOTE
);
150 token
= dynar_simple_lex_token_next(&lex
);
152 process_list_free_argv(*no_params
, res_argv
);
157 if (strcmp(dynar_data(token
), "") == 0) {
160 res_argv
[zi
] = strdup(dynar_data(token
));
161 if (res_argv
[zi
] == NULL
) {
162 process_list_free_argv(*no_params
, res_argv
);
170 if (zi
!= *no_params
) {
172 * If this happens it means something is seriously broken (memory corrupted)
174 process_list_free_argv(*no_params
, res_argv
);
180 dynar_simple_lex_destroy(&lex
);
181 dynar_destroy(&command_dstr
);
185 struct process_list_entry
*
186 process_list_add(struct process_list
*plist
, const char *name
, const char *command
)
188 struct process_list_entry
*entry
;
190 if (plist
->allocated_list_entries
+ 1 > plist
->max_list_entries
) {
197 entry
= malloc(sizeof(*entry
));
202 memset(entry
, 0, sizeof(*entry
));
203 entry
->name
= strdup(name
);
204 if (entry
->name
== NULL
) {
205 process_list_entry_free(entry
);
210 entry
->state
= PROCESS_LIST_ENTRY_STATE_INITIALIZED
;
211 entry
->exec_argv
= process_list_parse_command(command
, &entry
->exec_argc
);
212 if (entry
->exec_argv
== NULL
) {
213 process_list_entry_free(entry
);
218 plist
->allocated_list_entries
++;
219 TAILQ_INSERT_TAIL(&plist
->active_list
, entry
, entries
);
225 process_list_free(struct process_list
*plist
)
227 struct process_list_entry
*entry
;
228 struct process_list_entry
*entry_next
;
230 entry
= TAILQ_FIRST(&plist
->active_list
);
232 while (entry
!= NULL
) {
233 entry_next
= TAILQ_NEXT(entry
, entries
);
235 process_list_entry_free(entry
);
240 entry
= TAILQ_FIRST(&plist
->to_kill_list
);
242 while (entry
!= NULL
) {
243 entry_next
= TAILQ_NEXT(entry
, entries
);
245 process_list_entry_free(entry
);
250 plist
->allocated_list_entries
= 0;
252 TAILQ_INIT(&plist
->active_list
);
253 TAILQ_INIT(&plist
->to_kill_list
);
257 process_list_entry_exec_helper_set_stdfd(void)
261 devnull
= open("/dev/null", O_RDWR
);
263 err(EXIT_FAILURE
, "Can't open /dev/null");
266 if (dup2(devnull
, 0) < 0 || dup2(devnull
, 1) < 0 || dup2(devnull
, 2) < 0) {
268 err(EXIT_FAILURE
, "Can't dup2 stdin/out/err to /dev/null");
275 process_list_entry_exec(const struct process_list
*plist
, struct process_list_entry
*entry
)
279 if (entry
->state
!= PROCESS_LIST_ENTRY_STATE_INITIALIZED
) {
286 } else if (pid
== 0) {
287 process_list_entry_exec_helper_set_stdfd();
289 if (!plist
->use_execvp
) {
290 execv(entry
->exec_argv
[0], entry
->exec_argv
);
292 execvp(entry
->exec_argv
[0], entry
->exec_argv
);
296 * Exec returned -> exec failed
298 err(EXIT_FAILURE
, "Can't execute command %s (%s)", entry
->name
, entry
->exec_argv
[0]);
301 entry
->state
= PROCESS_LIST_ENTRY_STATE_RUNNING
;
303 if (plist
->notify_fn
!= NULL
) {
304 plist
->notify_fn(PROCESS_LIST_NOTIFY_REASON_EXECUTED
, entry
,
305 plist
->notify_fn_user_data
);
313 process_list_exec_initialized(struct process_list
*plist
)
315 struct process_list_entry
*entry
;
317 TAILQ_FOREACH(entry
, &plist
->active_list
, entries
) {
318 if (entry
->state
== PROCESS_LIST_ENTRY_STATE_INITIALIZED
) {
319 if (process_list_entry_exec(plist
, entry
) != 0) {
329 process_list_entry_waitpid(const struct process_list
*plist
, struct process_list_entry
*entry
)
334 if (entry
->state
== PROCESS_LIST_ENTRY_STATE_INITIALIZED
||
335 entry
->state
== PROCESS_LIST_ENTRY_STATE_FINISHED
) {
339 wpid_res
= waitpid(entry
->pid
, &status
, WNOHANG
);
340 if (wpid_res
== -1) {
351 entry
->exit_status
= status
;
353 if (entry
->state
== PROCESS_LIST_ENTRY_STATE_RUNNING
) {
354 if (plist
->notify_fn
!= NULL
) {
355 plist
->notify_fn(PROCESS_LIST_NOTIFY_REASON_FINISHED
, entry
,
356 plist
->notify_fn_user_data
);
360 entry
->state
= PROCESS_LIST_ENTRY_STATE_FINISHED
;
366 process_list_waitpid(struct process_list
*plist
)
368 struct process_list_entry
*entry
;
369 struct process_list_entry
*entry_next
;
371 TAILQ_FOREACH(entry
, &plist
->active_list
, entries
) {
372 if (process_list_entry_waitpid(plist
, entry
) != 0) {
377 entry
= TAILQ_FIRST(&plist
->to_kill_list
);
379 while (entry
!= NULL
) {
380 entry_next
= TAILQ_NEXT(entry
, entries
);
382 if (process_list_entry_waitpid(plist
, entry
) != 0) {
386 if (entry
->state
== PROCESS_LIST_ENTRY_STATE_FINISHED
) {
388 * Process finished -> remove it from list
390 TAILQ_REMOVE(&plist
->to_kill_list
, entry
, entries
);
391 process_list_entry_free(entry
);
392 plist
->allocated_list_entries
--;
402 process_list_get_no_running(struct process_list
*plist
)
404 struct process_list_entry
*entry
;
409 TAILQ_FOREACH(entry
, &plist
->active_list
, entries
) {
410 if (entry
->state
== PROCESS_LIST_ENTRY_STATE_RUNNING
) {
419 * -1 = Not all processes finished
420 * 0 = All processes finished successfully
421 * 1 - All processes finished but some of them not successfully
424 process_list_get_summary_result(struct process_list
*plist
)
426 struct process_list_entry
*entry
;
431 TAILQ_FOREACH(entry
, &plist
->active_list
, entries
) {
432 if (entry
->state
!= PROCESS_LIST_ENTRY_STATE_FINISHED
) {
436 if (!WIFEXITED(entry
->exit_status
) || WEXITSTATUS(entry
->exit_status
) != 0) {
445 * 0 = All processes finished successfully
446 * 1 = Some process finished and failed
447 * -1 = Not all processed finished and none of finished failed
450 process_list_get_summary_result_short(struct process_list
*plist
)
452 struct process_list_entry
*entry
;
457 TAILQ_FOREACH(entry
, &plist
->active_list
, entries
) {
458 if (entry
->state
== PROCESS_LIST_ENTRY_STATE_FINISHED
) {
459 if (!WIFEXITED(entry
->exit_status
) || WEXITSTATUS(entry
->exit_status
) != 0) {
471 process_list_move_entry_to_kill_list(struct process_list
*plist
, struct process_list_entry
*entry
)
474 TAILQ_REMOVE(&plist
->active_list
, entry
, entries
);
475 TAILQ_INSERT_TAIL(&plist
->to_kill_list
, entry
, entries
);
479 process_list_move_active_entries_to_kill_list(struct process_list
*plist
)
481 struct process_list_entry
*entry
;
482 struct process_list_entry
*entry_next
;
484 entry
= TAILQ_FIRST(&plist
->active_list
);
486 while (entry
!= NULL
) {
487 entry_next
= TAILQ_NEXT(entry
, entries
);
489 if (entry
->state
== PROCESS_LIST_ENTRY_STATE_INITIALIZED
||
490 entry
->state
== PROCESS_LIST_ENTRY_STATE_FINISHED
) {
491 TAILQ_REMOVE(&plist
->active_list
, entry
, entries
);
492 process_list_entry_free(entry
);
493 plist
->allocated_list_entries
--;
495 process_list_move_entry_to_kill_list(plist
, entry
);
503 process_list_process_kill_list_entry(struct process_list
*plist
, struct process_list_entry
*entry
)
506 enum process_list_entry_state new_state
;
510 new_state
= PROCESS_LIST_ENTRY_STATE_INITIALIZED
;
512 switch (entry
->state
) {
513 case PROCESS_LIST_ENTRY_STATE_INITIALIZED
:
515 * This shouldn't happen. If it does, process_list_move_active_entries_to_kill_list
516 * doesn't work as expected or there is some kind of memory corruption.
518 assert(entry
->state
!= PROCESS_LIST_ENTRY_STATE_INITIALIZED
);
520 case PROCESS_LIST_ENTRY_STATE_FINISHED
:
522 * This shouldn't happen. If it does, process_list_waitpid
523 * doesn't work as expected or there is some kind of memory corruption.
525 assert(entry
->state
!= PROCESS_LIST_ENTRY_STATE_FINISHED
);
527 case PROCESS_LIST_ENTRY_STATE_RUNNING
:
528 sig_to_send
= SIGTERM
;
529 new_state
= PROCESS_LIST_ENTRY_STATE_SIGTERM_SENT
;
531 case PROCESS_LIST_ENTRY_STATE_SIGTERM_SENT
:
532 sig_to_send
= SIGKILL
;
533 new_state
= PROCESS_LIST_ENTRY_STATE_SIGKILL_SENT
;
535 case PROCESS_LIST_ENTRY_STATE_SIGKILL_SENT
:
536 sig_to_send
= SIGKILL
;
537 new_state
= PROCESS_LIST_ENTRY_STATE_SIGKILL_SENT
;
543 if (kill(entry
->pid
, sig_to_send
) == -1) {
544 if (errno
== EPERM
|| errno
== EINVAL
) {
549 entry
->state
= new_state
;
555 process_list_process_kill_list(struct process_list
*plist
)
557 struct process_list_entry
*entry
;
559 if (process_list_waitpid(plist
) != 0) {
563 TAILQ_FOREACH(entry
, &plist
->to_kill_list
, entries
) {
564 if (process_list_process_kill_list_entry(plist
, entry
) != 0) {
573 process_list_get_kill_list_items(struct process_list
*plist
)
575 struct process_list_entry
*entry
;
580 TAILQ_FOREACH(entry
, &plist
->to_kill_list
, entries
) {
588 process_list_killall(struct process_list
*plist
, uint32_t timeout
)
590 uint32_t action_timeout
;
593 process_list_move_active_entries_to_kill_list(plist
);
595 action_timeout
= timeout
/ 10;
596 if (action_timeout
< 1) {
600 for (i
= 0; i
< 10; i
++) {
602 * Make sure all process got signal (quick phase)
604 if (process_list_process_kill_list(plist
) != 0) {
609 for (i
= 0; i
< 10 && process_list_get_kill_list_items(plist
) > 0; i
++) {
610 if (process_list_process_kill_list(plist
) != 0) {
614 poll(NULL
, 0, action_timeout
);
617 if (process_list_get_kill_list_items(plist
) > 0) {