1 /* This file is part of gacopyz.
2 Copyright (C) 2006-2021 Sergey Poznyakoff
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <gacopyz_priv.h>
18
19 static int cleanup_needed;
20
21 #define PIDTAB_INCR 256
22
23 int
gacopyz_register_child(gacopyz_conn_t conn,pid_t pid)24 gacopyz_register_child(gacopyz_conn_t conn, pid_t pid)
25 {
26 size_t i, newcount;
27 pid_t *p;
28
29 for (i = 0; i < conn->pidcount; i++)
30 if (conn->pidtab[i] == 0) {
31 conn->pidtab[i] = pid;
32 return 0;
33 }
34
35 newcount = conn->pidcount + PIDTAB_INCR;
36 p = realloc(conn->pidtab, newcount * sizeof(conn->pidtab[0]));
37 if (!p)
38 return 1;
39 memset(p + i, 0, PIDTAB_INCR * sizeof(conn->pidtab[0]));
40 conn->pidcount = newcount;
41 conn->pidtab = p;
42
43 conn->pidtab[i++] = pid;
44 return 0;
45 }
46
47 void
gacopyz_unregister_child(gacopyz_conn_t conn,pid_t pid)48 gacopyz_unregister_child(gacopyz_conn_t conn, pid_t pid)
49 {
50 size_t i;
51
52 for (i = 0; i < conn->pidcount; i++)
53 if (conn->pidtab[i] == pid)
54 conn->pidtab[i] = 0;
55 }
56
57 static void
print_status(gacopyz_conn_t conn,pid_t pid,int status,int expect_term)58 print_status(gacopyz_conn_t conn, pid_t pid, int status, int expect_term)
59 {
60 if (WIFEXITED(status)) {
61 if (WEXITSTATUS(status) == 0) {
62 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_DEBUG))
63 gacopyz_log(SMI_LOG_DEBUG,
64 _("child %lu exited successfully"),
65 (unsigned long) pid);
66 } else {
67 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
68 gacopyz_log(SMI_LOG_ERR,
69 _("child %lu failed with status %d"),
70 (unsigned long) pid,
71 WEXITSTATUS(status));
72 }
73 } else if (WIFSIGNALED(status)) {
74 int prio;
75 if (expect_term && WTERMSIG(status) == SIGTERM)
76 prio = SMI_LOG_DEBUG;
77 else
78 prio = SMI_LOG_ERR;
79 if (GACOPYZ_CONN_LOG_MATCH(conn, prio))
80 gacopyz_log(prio,
81 _("child %lu terminated on signal %d"),
82 (unsigned long) pid, WTERMSIG(status));
83 } else if (WIFSTOPPED(status)) {
84 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
85 gacopyz_log(SMI_LOG_ERR,
86 _("child %lu stopped on signal %d"),
87 (unsigned long) pid, WSTOPSIG(status));
88 }
89 #ifdef WCOREDUMP
90 else if (WCOREDUMP(status)) {
91 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
92 gacopyz_log(SMI_LOG_ERR,
93 _("child %lu dumped core"),
94 (unsigned long) pid);
95 }
96 #endif
97 else if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_ERR))
98 gacopyz_log(SMI_LOG_ERR,
99 _("child %lu terminated with unrecognized status"),
100 (unsigned long) pid);
101 }
102
103 static void
cleanup_children(gacopyz_conn_t conn,int expect_term)104 cleanup_children(gacopyz_conn_t conn, int expect_term)
105 {
106 if (!cleanup_needed)
107 return;
108 for (;;) {
109 int status;
110 pid_t pid = waitpid((pid_t)-1, &status, WNOHANG);
111 if (pid <= 0)
112 break;
113 gacopyz_unregister_child(conn, pid);
114 print_status(conn, pid, status, expect_term);
115 }
116 cleanup_needed = 0;
117 }
118
119 void
gacopyz_cleanup_children(gacopyz_conn_t conn)120 gacopyz_cleanup_children(gacopyz_conn_t conn)
121 {
122 cleanup_children(conn, 0);
123 }
124
125 void
gacopyz_cleanup_conn(gacopyz_conn_t conn)126 gacopyz_cleanup_conn(gacopyz_conn_t conn)
127 {
128 size_t i;
129
130 if (GACOPYZ_CONN_LOG_MATCH(conn, SMI_LOG_DEBUG))
131 gacopyz_log(SMI_LOG_DEBUG, _("terminating subprocesses"));
132
133 for (i = 0; i < conn->pidcount; i++)
134 if (conn->pidtab[i])
135 kill(conn->pidtab[i], SIGTERM);
136 cleanup_children(conn, 1);
137
138 if (conn->cleanup)
139 conn->cleanup(conn, conn->cleanup_data);
140 }
141
142 static RETSIGTYPE
sig_child(int sig)143 sig_child(int sig)
144 {
145 cleanup_needed = 1;
146 signal(sig, sig_child);
147 }
148
149 void
gacopyz_setup_signals()150 gacopyz_setup_signals()
151 {
152 signal(SIGCHLD, sig_child);
153 signal(SIGPIPE, SIG_IGN); /* FIXME */
154 }
155