version 1.7.3.0
[socat.git] / xiosigchld.c
blob0e7d0b01dcd2536826282ac5e5ab78ff0925a09a
1 /* source: xiosigchld.c */
2 /* Copyright Gerhard Rieger */
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 size_t nextunknown;
17 /* register for a xio filedescriptor a callback (handler).
18 when a SIGCHLD occurs, the signal handler will ??? */
19 int xiosetsigchild(xiofile_t *xfd, int (*callback)(struct single *)) {
20 if (xfd->tag != XIO_TAG_DUAL) {
21 xfd->stream.sigchild = callback;
22 } else {
23 xfd->dual.stream[0]->sigchild = callback;
24 xfd->dual.stream[1]->sigchild = callback;
26 return 0;
29 /* exec'd child has died, perform appropriate changes to descriptor */
30 /* is async-signal-safe */
31 static int sigchld_stream(struct single *file) {
32 /*!! call back to application */
33 file->para.exec.pid = 0;
34 if (file->sigchild) {
35 return (*file->sigchild)(file);
37 return 0;
40 /* return 0 if socket is not responsible for deadchild */
41 static int xio_checkchild(xiofile_t *socket, int socknum, pid_t deadchild) {
42 int retval;
43 if (socket != NULL) {
44 if (socket->tag != XIO_TAG_DUAL) {
45 if ((socket->stream.howtoend == END_KILL ||
46 socket->stream.howtoend == END_CLOSE_KILL ||
47 socket->stream.howtoend == END_SHUTDOWN_KILL) &&
48 socket->stream.para.exec.pid == deadchild) {
49 Info2("exec'd process %d on socket %d terminated",
50 socket->stream.para.exec.pid, socknum);
51 sigchld_stream(&socket->stream); /* is async-signal-safe */
52 return 1;
54 } else {
55 if (retval = xio_checkchild((xiofile_t *)socket->dual.stream[0], socknum, deadchild))
56 return retval;
57 else
58 return xio_checkchild((xiofile_t *)socket->dual.stream[1], socknum, deadchild);
61 return 0;
64 /* this is the "physical" signal handler for SIGCHLD */
65 /* the current socat/xio implementation knows two kinds of children:
66 exec/system addresses perform a fork: these children are registered and
67 there death influences the parents flow;
68 listen-socket with fork children: these children are "anonymous" and their
69 death does not affect the parent process (now; maybe we have a child
70 process counter later) */
71 void childdied(int signum) {
72 pid_t pid;
73 int _errno;
74 int status = 0;
75 bool wassig = false;
76 int i;
78 _errno = errno; /* save current value; e.g., select() on Cygwin seems
79 to set it to EINTR _before_ handling the signal, and
80 then passes the value left by the signal handler to
81 the caller of select(), accept() etc. */
82 diag_in_handler = 1;
83 Notice1("childdied(): handling signal %d", signum);
84 Info1("childdied(signum=%d)", signum);
85 do {
86 pid = Waitpid(-1, &status, WNOHANG);
87 if (pid == 0) {
88 Msg(wassig?E_INFO:E_WARN,
89 "waitpid(-1, {}, WNOHANG): no child has exited");
90 Info("childdied() finished");
91 diag_in_handler = 0;
92 errno = _errno;
93 return;
94 } else if (pid < 0 && errno == ECHILD) {
95 Msg(wassig?E_INFO:E_WARN,
96 "waitpid(-1, {}, WNOHANG): "F_strerror);
97 Info("childdied() finished");
98 diag_in_handler = 0;
99 errno = _errno;
100 return;
102 wassig = true;
103 if (pid < 0) {
104 Warn1("waitpid(-1, {%d}, WNOHANG): "F_strerror, status);
105 Info("childdied() finished");
106 diag_in_handler = 0;
107 errno = _errno;
108 return;
110 /*! indent */
111 if (num_child) num_child--;
112 /* check if it was a registered child process */
113 i = 0;
114 while (i < XIO_MAXSOCK) {
115 if (xio_checkchild(sock[i], i, pid)) break;
116 ++i;
118 if (i == XIO_MAXSOCK) {
119 Info2("childdied(%d): cannot identify child %d", signum, pid);
120 if (nextunknown == NUMUNKNOWN) {
121 nextunknown = 0;
123 diedunknown[nextunknown++] = pid;
124 Debug1("saving pid in diedunknown"F_Zu,
125 nextunknown/*sic, for compatibility*/);
128 if (WIFEXITED(status)) {
129 if (WEXITSTATUS(status) == 0) {
130 Info2("waitpid(): child %d exited with status %d",
131 pid, WEXITSTATUS(status));
132 } else {
133 Warn2("waitpid(): child %d exited with status %d",
134 pid, WEXITSTATUS(status));
136 } else if (WIFSIGNALED(status)) {
137 Info2("waitpid(): child %d exited on signal %d",
138 pid, WTERMSIG(status));
139 } else if (WIFSTOPPED(status)) {
140 Info2("waitpid(): child %d stopped on signal %d",
141 pid, WSTOPSIG(status));
142 } else {
143 Warn1("waitpid(): cannot determine status of child %d", pid);
146 #if !HAVE_SIGACTION
147 /* we might need to re-register our handler */
148 if (Signal(SIGCHLD, childdied) == SIG_ERR) {
149 Warn("signal(SIGCHLD, childdied): "F_strerror);
151 #endif /* !HAVE_SIGACTION */
152 } while (1);
153 Info("childdied() finished");
154 diag_in_handler = 0;
155 errno = _errno;
159 int xiosetchilddied(void) {
160 #if HAVE_SIGACTION
161 struct sigaction act;
162 memset(&act, 0, sizeof(struct sigaction));
163 act.sa_flags = SA_NOCLDSTOP/*|SA_RESTART*/
164 #ifdef SA_NOMASK
165 |SA_NOMASK
166 #endif
168 act.sa_handler = childdied;
169 sigfillset(&act.sa_mask);
170 if (Sigaction(SIGCHLD, &act, NULL) < 0) {
171 /*! man does not say that errno is defined */
172 Warn2("sigaction(SIGCHLD, %p, NULL): %s", childdied, strerror(errno));
174 #else /* HAVE_SIGACTION */
175 if (Signal(SIGCHLD, childdied) == SIG_ERR) {
176 Warn2("signal(SIGCHLD, %p): %s", childdied, strerror(errno));
178 #endif /* !HAVE_SIGACTION */
179 return 0;