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