Version 1.8.0.0
[socat.git] / xiosigchld.c
blob10f7f49cbafb8b53c2fb5dc0aca2aa77c07deffe
1 /* source: xiosigchld.c */
2 /* Copyright Gerhard Rieger and contributors (see file CHANGES) */
3 /* Published under the GNU General Public License V.2, see file COPYING */
5 /* this is the source of the extended child signal handler */
8 #include "xiosysincludes.h"
9 #include "xioopen.h"
12 /*!! with socat, at most 4 exec children exist */
13 pid_t diedunknown[NUMUNKNOWN]; /* children that died before they were registered */
14 int statunknown[NUMUNKNOWN]; /* exit state of unknown dead child */
15 size_t nextunknown;
16 int engine_result = EXIT_SUCCESS;
19 /* register for a xio filedescriptor a callback (handler).
20 when a SIGCHLD occurs, the signal handler will ??? */
21 int xiosetsigchild(xiofile_t *xfd, int (*callback)(struct single *)) {
22 if (xfd->tag != XIO_TAG_DUAL) {
23 xfd->stream.sigchild = callback;
24 } else {
25 xfd->dual.stream[0]->sigchild = callback;
26 xfd->dual.stream[1]->sigchild = callback;
28 return 0;
31 /* exec'd child has died, perform appropriate changes to descriptor */
32 /* is async-signal-safe */
33 static int sigchld_stream(struct single *file) {
34 /*!! call back to application */
35 file->para.exec.pid = 0;
36 if (file->sigchild) {
37 return (*file->sigchild)(file);
39 return 0;
42 /* return 0 if socket is not responsible for deadchild */
43 static int xio_checkchild(xiofile_t *socket, int socknum, pid_t deadchild) {
44 int retval;
45 if (socket != NULL) {
46 if (socket->tag != XIO_TAG_DUAL) {
47 if ((socket->stream.howtoend == END_KILL ||
48 socket->stream.howtoend == END_CLOSE_KILL ||
49 socket->stream.howtoend == END_SHUTDOWN_KILL) &&
50 socket->stream.para.exec.pid == deadchild) {
51 Info2("exec'd process %d on socket %d terminated",
52 socket->stream.para.exec.pid, socknum);
53 sigchld_stream(&socket->stream); /* is async-signal-safe */
54 return 1;
56 } else {
57 if (retval = xio_checkchild((xiofile_t *)socket->dual.stream[0], socknum, deadchild))
58 return retval;
59 else
60 return xio_checkchild((xiofile_t *)socket->dual.stream[1], socknum, deadchild);
63 return 0;
66 /* this is the "physical" signal handler for SIGCHLD */
67 /* the current socat/xio implementation knows two kinds of children:
68 exec/system addresses perform a fork: these children are registered and
69 there death influences the parents flow;
70 listen-socket with fork children: these children are "anonymous" and their
71 death does not affect the parent process (now; maybe we have a child
72 process counter later) */
73 void childdied(int signum) {
74 pid_t pid;
75 int _errno;
76 int status = 0;
77 bool wassig = false;
78 int i;
80 _errno = errno; /* save current value; e.g., select() on Cygwin seems
81 to set it to EINTR _before_ handling the signal, and
82 then passes the value left by the signal handler to
83 the caller of select(), accept() etc. */
84 diag_in_handler = 1;
85 Notice1("childdied(): handling signal %d", signum);
86 Info1("childdied(signum=%d)", signum);
87 do {
88 pid = Waitpid(-1, &status, WNOHANG);
89 if (pid == 0) {
90 Msg(wassig?E_INFO:E_WARN,
91 "waitpid(-1, {}, WNOHANG): no child has exited");
92 Info("childdied() finished");
93 diag_in_handler = 0;
94 errno = _errno;
95 return;
96 } else if (pid < 0 && errno == EINTR) {
97 Info1("childdied(): %s", strerror(errno));
98 } else if (pid < 0 && errno == ECHILD) {
99 Msg(wassig?E_INFO:E_NOTICE,
100 "waitpid(-1, {}, WNOHANG): "F_strerror);
101 Info("childdied() finished");
102 diag_in_handler = 0;
103 errno = _errno;
104 return;
106 wassig = true;
107 if (pid < 0) {
108 Warn1("waitpid(-1, {%d}, WNOHANG): "F_strerror, status);
109 Info("childdied() finished");
110 diag_in_handler = 0;
111 errno = _errno;
112 return;
114 /*! indent */
115 if (num_child) {
116 num_child--;
117 Info1("number of children decreased to %d", num_child);
119 /* check if it was a registered child process */
120 i = 0;
121 while (i < XIO_MAXSOCK) {
122 if (xio_checkchild(sock[i], i, pid)) break;
123 ++i;
125 if (i == XIO_MAXSOCK) {
126 Info2("childdied(%d): cannot identify child %d", signum, pid);
127 if (num_child) num_child--;
128 if (nextunknown == NUMUNKNOWN) {
129 nextunknown = 0;
131 diedunknown[nextunknown] = pid;
132 statunknown[nextunknown++] = WEXITSTATUS(status);
133 Debug1("saving pid in diedunknown"F_Zu,
134 nextunknown/*sic, for compatibility*/);
137 if (WIFEXITED(status)) {
138 if (WEXITSTATUS(status) == 0) {
139 Info2("waitpid(): child %d exited with status %d",
140 pid, WEXITSTATUS(status));
141 } else {
142 if (i == XIO_MAXSOCK) {
143 Info2("waitpid(): child %d exited with status %d",
144 pid, WEXITSTATUS(status));
145 } else {
146 Warn2("waitpid(): child %d exited with status %d",
147 pid, WEXITSTATUS(status));
148 engine_result = 1;
151 } else if (WIFSIGNALED(status)) {
152 if (i == XIO_MAXSOCK) {
153 Info2("waitpid(): child %d exited on signal %d",
154 pid, WTERMSIG(status));
155 } else {
156 Warn2("waitpid(): child %d exited on signal %d",
157 pid, WTERMSIG(status));
158 engine_result = 1;
160 } else if (WIFSTOPPED(status)) {
161 Info2("waitpid(): child %d stopped on signal %d",
162 pid, WSTOPSIG(status));
163 } else {
164 Warn1("waitpid(): cannot determine status of child %d", pid);
167 #if !HAVE_SIGACTION
168 /* we might need to re-register our handler */
169 if (Signal(SIGCHLD, childdied) == SIG_ERR) {
170 Warn("signal(SIGCHLD, childdied): "F_strerror);
172 #endif /* !HAVE_SIGACTION */
173 } while (1);
174 Info("childdied() finished");
175 diag_in_handler = 0;
176 errno = _errno;
180 int xiosetchilddied(void) {
181 #if HAVE_SIGACTION
182 struct sigaction act;
183 memset(&act, 0, sizeof(struct sigaction));
184 act.sa_flags = SA_NOCLDSTOP/*|SA_RESTART*/
185 #ifdef SA_NOMASK
186 |SA_NOMASK
187 #endif
189 act.sa_handler = childdied;
190 sigfillset(&act.sa_mask);
191 if (Sigaction(SIGCHLD, &act, NULL) < 0) {
192 /*! man does not say that errno is defined */
193 Warn2("sigaction(SIGCHLD, %p, NULL): %s", childdied, strerror(errno));
195 #else /* HAVE_SIGACTION */
196 if (Signal(SIGCHLD, childdied) == SIG_ERR) {
197 Warn2("signal(SIGCHLD, %p): %s", childdied, strerror(errno));
199 #endif /* !HAVE_SIGACTION */
200 return 0;