]> git.proxmox.com Git - mirror_corosync-qdevice.git/blob - qdevices/process-list.c
qdevice: Use EXIT_SUCCESS and EXIT_FAILURE codes
[mirror_corosync-qdevice.git] / qdevices / process-list.c
1 /*
2 * Copyright (c) 2015-2020 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 #include <fcntl.h>
38 #include <sys/wait.h>
39
40 #include <string.h>
41 #include <stdlib.h>
42 #include <assert.h>
43 #include <err.h>
44 #include <errno.h>
45 #include <poll.h>
46 #include <unistd.h>
47
48 #include "dynar.h"
49 #include "dynar-str.h"
50 #include "dynar-simple-lex.h"
51 #include "process-list.h"
52
53 static void process_list_free_argv(size_t no_params, char **argv);
54
55 static void process_list_entry_free(struct process_list_entry *entry);
56
57 static char **process_list_parse_command(const char *command, size_t *no_params);
58
59 static int process_list_entry_exec(const struct process_list *plist,
60 struct process_list_entry *entry);
61
62 void
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)
65 {
66
67 memset(plist, 0, sizeof(*plist));
68
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;
74
75 TAILQ_INIT(&plist->active_list);
76 TAILQ_INIT(&plist->to_kill_list);
77 }
78
79 static void
80 process_list_free_argv(size_t no_params, char **argv)
81 {
82 size_t zi;
83
84 for (zi = 0; zi < no_params; zi++) {
85 free(argv[zi]);
86 }
87 free(argv);
88 }
89
90 static void
91 process_list_entry_free(struct process_list_entry *entry)
92 {
93
94 process_list_free_argv(entry->exec_argc, entry->exec_argv);
95 free(entry->name);
96 free(entry);
97 }
98
99 static char **
100 process_list_parse_command(const char *command, size_t *no_params)
101 {
102 struct dynar command_dstr;
103 struct dynar_simple_lex lex;
104 struct dynar *token;
105 int finished;
106 char **res_argv;
107 size_t zi;
108
109 res_argv = NULL;
110
111 dynar_init(&command_dstr, strlen(command) + 1);
112 if (dynar_str_cpy(&command_dstr, command) != 0) {
113 return (NULL);
114 }
115
116 dynar_simple_lex_init(&lex, &command_dstr, DYNAR_SIMPLE_LEX_TYPE_QUOTE);
117 *no_params = 0;
118 finished = 0;
119
120 while (!finished) {
121 token = dynar_simple_lex_token_next(&lex);
122 if (token == NULL) {
123 goto exit_res;
124 }
125
126 if (strcmp(dynar_data(token), "") == 0) {
127 finished = 1;
128 } else {
129 (*no_params)++;
130 }
131 }
132
133 if (*no_params < 1) {
134 goto exit_res;
135 }
136
137 dynar_simple_lex_destroy(&lex);
138
139 res_argv = malloc(sizeof(char *) * (*no_params + 1));
140 if (res_argv == NULL) {
141 goto exit_res;
142 }
143 memset(res_argv, 0, sizeof(char *) * (*no_params + 1));
144
145 dynar_simple_lex_init(&lex, &command_dstr, DYNAR_SIMPLE_LEX_TYPE_QUOTE);
146
147 finished = 0;
148 zi = 0;
149 while (!finished) {
150 token = dynar_simple_lex_token_next(&lex);
151 if (token == NULL) {
152 process_list_free_argv(*no_params, res_argv);
153 res_argv = NULL;
154 goto exit_res;
155 }
156
157 if (strcmp(dynar_data(token), "") == 0) {
158 finished = 1;
159 } else {
160 res_argv[zi] = strdup(dynar_data(token));
161 if (res_argv[zi] == NULL) {
162 process_list_free_argv(*no_params, res_argv);
163 res_argv = NULL;
164 goto exit_res;
165 }
166 zi++;
167 }
168 }
169
170 if (zi != *no_params) {
171 /*
172 * If this happens it means something is seriously broken (memory corrupted)
173 */
174 process_list_free_argv(*no_params, res_argv);
175 res_argv = NULL;
176 goto exit_res;
177 }
178
179 exit_res:
180 dynar_simple_lex_destroy(&lex);
181 dynar_destroy(&command_dstr);
182 return (res_argv);
183 }
184
185 struct process_list_entry *
186 process_list_add(struct process_list *plist, const char *name, const char *command)
187 {
188 struct process_list_entry *entry;
189
190 if (plist->allocated_list_entries + 1 > plist->max_list_entries) {
191 return (NULL);
192 }
193
194 /*
195 * Alloc new entry
196 */
197 entry = malloc(sizeof(*entry));
198 if (entry == NULL) {
199 return (NULL);
200 }
201
202 memset(entry, 0, sizeof(*entry));
203 entry->name = strdup(name);
204 if (entry->name == NULL) {
205 process_list_entry_free(entry);
206
207 return (NULL);
208 }
209
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);
214
215 return (NULL);
216 }
217
218 plist->allocated_list_entries++;
219 TAILQ_INSERT_TAIL(&plist->active_list, entry, entries);
220
221 return (entry);
222 }
223
224 void
225 process_list_free(struct process_list *plist)
226 {
227 struct process_list_entry *entry;
228 struct process_list_entry *entry_next;
229
230 entry = TAILQ_FIRST(&plist->active_list);
231
232 while (entry != NULL) {
233 entry_next = TAILQ_NEXT(entry, entries);
234
235 process_list_entry_free(entry);
236
237 entry = entry_next;
238 }
239
240 entry = TAILQ_FIRST(&plist->to_kill_list);
241
242 while (entry != NULL) {
243 entry_next = TAILQ_NEXT(entry, entries);
244
245 process_list_entry_free(entry);
246
247 entry = entry_next;
248 }
249
250 plist->allocated_list_entries = 0;
251
252 TAILQ_INIT(&plist->active_list);
253 TAILQ_INIT(&plist->to_kill_list);
254 }
255
256 static void
257 process_list_entry_exec_helper_set_stdfd(void)
258 {
259 int devnull;
260
261 devnull = open("/dev/null", O_RDWR);
262 if (devnull == -1) {
263 err(EXIT_FAILURE, "Can't open /dev/null");
264 }
265
266 if (dup2(devnull, 0) < 0 || dup2(devnull, 1) < 0 || dup2(devnull, 2) < 0) {
267 close(devnull);
268 err(EXIT_FAILURE, "Can't dup2 stdin/out/err to /dev/null");
269 }
270
271 close(devnull);
272 }
273
274 static int
275 process_list_entry_exec(const struct process_list *plist, struct process_list_entry *entry)
276 {
277 pid_t pid;
278
279 if (entry->state != PROCESS_LIST_ENTRY_STATE_INITIALIZED) {
280 return (-1);
281 }
282
283 pid = fork();
284 if (pid == -1) {
285 return (-1);
286 } else if (pid == 0) {
287 process_list_entry_exec_helper_set_stdfd();
288
289 if (!plist->use_execvp) {
290 execv(entry->exec_argv[0], entry->exec_argv);
291 } else {
292 execvp(entry->exec_argv[0], entry->exec_argv);
293 }
294
295 /*
296 * Exec returned -> exec failed
297 */
298 err(EXIT_FAILURE, "Can't execute command %s (%s)", entry->name, entry->exec_argv[0]);
299 } else {
300 entry->pid = pid;
301 entry->state = PROCESS_LIST_ENTRY_STATE_RUNNING;
302
303 if (plist->notify_fn != NULL) {
304 plist->notify_fn(PROCESS_LIST_NOTIFY_REASON_EXECUTED, entry,
305 plist->notify_fn_user_data);
306 }
307 }
308
309 return (0);
310 }
311
312 int
313 process_list_exec_initialized(struct process_list *plist)
314 {
315 struct process_list_entry *entry;
316
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) {
320 return (-1);
321 }
322 }
323 }
324
325 return (0);
326 }
327
328 static int
329 process_list_entry_waitpid(const struct process_list *plist, struct process_list_entry *entry)
330 {
331 pid_t wpid_res;
332 int status;
333
334 if (entry->state == PROCESS_LIST_ENTRY_STATE_INITIALIZED ||
335 entry->state == PROCESS_LIST_ENTRY_STATE_FINISHED) {
336 return (0);
337 }
338
339 wpid_res = waitpid(entry->pid, &status, WNOHANG);
340 if (wpid_res == -1) {
341 return (-1);
342 }
343
344 if (wpid_res == 0) {
345 /*
346 * No change
347 */
348 return (0);
349 }
350
351 entry->exit_status = status;
352
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);
357 }
358 }
359
360 entry->state = PROCESS_LIST_ENTRY_STATE_FINISHED;
361
362 return (0);
363 }
364
365 int
366 process_list_waitpid(struct process_list *plist)
367 {
368 struct process_list_entry *entry;
369 struct process_list_entry *entry_next;
370
371 TAILQ_FOREACH(entry, &plist->active_list, entries) {
372 if (process_list_entry_waitpid(plist, entry) != 0) {
373 return (-1);
374 }
375 }
376
377 entry = TAILQ_FIRST(&plist->to_kill_list);
378
379 while (entry != NULL) {
380 entry_next = TAILQ_NEXT(entry, entries);
381
382 if (process_list_entry_waitpid(plist, entry) != 0) {
383 return (-1);
384 }
385
386 if (entry->state == PROCESS_LIST_ENTRY_STATE_FINISHED) {
387 /*
388 * Process finished -> remove it from list
389 */
390 TAILQ_REMOVE(&plist->to_kill_list, entry, entries);
391 process_list_entry_free(entry);
392 plist->allocated_list_entries--;
393 }
394
395 entry = entry_next;
396 }
397
398 return (0);
399 }
400
401 size_t
402 process_list_get_no_running(struct process_list *plist)
403 {
404 struct process_list_entry *entry;
405 size_t res;
406
407 res = 0;
408
409 TAILQ_FOREACH(entry, &plist->active_list, entries) {
410 if (entry->state == PROCESS_LIST_ENTRY_STATE_RUNNING) {
411 res++;
412 }
413 }
414
415 return (res);
416 }
417
418 /*
419 * -1 = Not all processes finished
420 * 0 = All processes finished successfully
421 * 1 - All processes finished but some of them not successfully
422 */
423 int
424 process_list_get_summary_result(struct process_list *plist)
425 {
426 struct process_list_entry *entry;
427 int res;
428
429 res = 0;
430
431 TAILQ_FOREACH(entry, &plist->active_list, entries) {
432 if (entry->state != PROCESS_LIST_ENTRY_STATE_FINISHED) {
433 return (-1);
434 }
435
436 if (!WIFEXITED(entry->exit_status) || WEXITSTATUS(entry->exit_status) != 0) {
437 res = 1;
438 }
439 }
440
441 return (res);
442 }
443
444 /*
445 * 0 = All processes finished successfully
446 * 1 = Some process finished and failed
447 * -1 = Not all processed finished and none of finished failed
448 */
449 int
450 process_list_get_summary_result_short(struct process_list *plist)
451 {
452 struct process_list_entry *entry;
453 int res;
454
455 res = 0;
456
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) {
460 return (1);
461 }
462 } else {
463 res = -1;
464 }
465 }
466
467 return (res);
468 }
469
470 static void
471 process_list_move_entry_to_kill_list(struct process_list *plist, struct process_list_entry *entry)
472 {
473
474 TAILQ_REMOVE(&plist->active_list, entry, entries);
475 TAILQ_INSERT_TAIL(&plist->to_kill_list, entry, entries);
476 }
477
478 void
479 process_list_move_active_entries_to_kill_list(struct process_list *plist)
480 {
481 struct process_list_entry *entry;
482 struct process_list_entry *entry_next;
483
484 entry = TAILQ_FIRST(&plist->active_list);
485
486 while (entry != NULL) {
487 entry_next = TAILQ_NEXT(entry, entries);
488
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--;
494 } else {
495 process_list_move_entry_to_kill_list(plist, entry);
496 }
497
498 entry = entry_next;
499 }
500 }
501
502 static int
503 process_list_process_kill_list_entry(struct process_list *plist, struct process_list_entry *entry)
504 {
505 int sig_to_send;
506 enum process_list_entry_state new_state;
507 int res;
508
509 sig_to_send = 0;
510 new_state = PROCESS_LIST_ENTRY_STATE_INITIALIZED;
511
512 switch (entry->state) {
513 case PROCESS_LIST_ENTRY_STATE_INITIALIZED:
514 /*
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.
517 */
518 assert(entry->state != PROCESS_LIST_ENTRY_STATE_INITIALIZED);
519 break;
520 case PROCESS_LIST_ENTRY_STATE_FINISHED:
521 /*
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.
524 */
525 assert(entry->state != PROCESS_LIST_ENTRY_STATE_FINISHED);
526 break;
527 case PROCESS_LIST_ENTRY_STATE_RUNNING:
528 sig_to_send = SIGTERM;
529 new_state = PROCESS_LIST_ENTRY_STATE_SIGTERM_SENT;
530 break;
531 case PROCESS_LIST_ENTRY_STATE_SIGTERM_SENT:
532 sig_to_send = SIGKILL;
533 new_state = PROCESS_LIST_ENTRY_STATE_SIGKILL_SENT;
534 break;
535 case PROCESS_LIST_ENTRY_STATE_SIGKILL_SENT:
536 sig_to_send = SIGKILL;
537 new_state = PROCESS_LIST_ENTRY_STATE_SIGKILL_SENT;
538 break;
539 }
540
541 res = 0;
542
543 if (kill(entry->pid, sig_to_send) == -1) {
544 if (errno == EPERM || errno == EINVAL) {
545 res = -1;
546 }
547 }
548
549 entry->state = new_state;
550
551 return (res);
552 }
553
554 int
555 process_list_process_kill_list(struct process_list *plist)
556 {
557 struct process_list_entry *entry;
558
559 if (process_list_waitpid(plist) != 0) {
560 return (-1);
561 }
562
563 TAILQ_FOREACH(entry, &plist->to_kill_list, entries) {
564 if (process_list_process_kill_list_entry(plist, entry) != 0) {
565 return (-1);
566 }
567 }
568
569 return (0);
570 }
571
572 size_t
573 process_list_get_kill_list_items(struct process_list *plist)
574 {
575 struct process_list_entry *entry;
576 size_t res;
577
578 res = 0;
579
580 TAILQ_FOREACH(entry, &plist->to_kill_list, entries) {
581 res++;
582 }
583
584 return (res);
585 }
586
587 int
588 process_list_killall(struct process_list *plist, uint32_t timeout)
589 {
590 uint32_t action_timeout;
591 int i;
592
593 process_list_move_active_entries_to_kill_list(plist);
594
595 action_timeout = timeout / 10;
596 if (action_timeout < 1) {
597 action_timeout = 1;
598 }
599
600 for (i = 0; i < 10; i++) {
601 /*
602 * Make sure all process got signal (quick phase)
603 */
604 if (process_list_process_kill_list(plist) != 0) {
605 return (-1);
606 }
607 }
608
609 for (i = 0; i < 10 && process_list_get_kill_list_items(plist) > 0; i++) {
610 if (process_list_process_kill_list(plist) != 0) {
611 return (-1);
612 }
613
614 poll(NULL, 0, action_timeout);
615 }
616
617 if (process_list_get_kill_list_items(plist) > 0) {
618 return (-1);
619 }
620
621 return (0);
622 }