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