File: xiosigchld.c

package info (click to toggle)
socat 1.7.3.2-2
  • links: PTS
  • area: main
  • in suites: bullseye, buster, sid
  • size: 3,888 kB
  • sloc: ansic: 28,032; sh: 11,782; makefile: 146
file content (190 lines) | stat: -rw-r--r-- 5,808 bytes parent folder | download
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
181
182
183
184
185
186
187
188
189
190
/* source: xiosigchld.c */
/* Copyright Gerhard Rieger and contributors (see file CHANGES) */
/* 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 {
	 if (i == XIO_MAXSOCK) {
	    Info2("waitpid(): child %d exited with status %d",
		   pid, WEXITSTATUS(status));
	 } else {
	    Error2("waitpid(): child %d exited with status %d",
		   pid, WEXITSTATUS(status));
	 }
      }
   } else if (WIFSIGNALED(status)) {
      if (i == XIO_MAXSOCK) {
	 Info2("waitpid(): child %d exited on signal %d",
	       pid, WTERMSIG(status));
      } else {
	 Error2("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;
}