Stackdb
Stackdb is a stackable, multi-target and -level source debugger and memory forensics library.
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
waitpipe.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012, 2013 The University of Utah
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) 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, write to the Free Software
16  * Foundation, 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "log.h"
20 #include "waitpipe.h"
21 
22 #include <signal.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <inttypes.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <sys/types.h>
29 #include <sys/wait.h>
30 
31 struct waitpipectl {
32  GHashTable *pids;
33  GHashTable *readfds;
34  void (*alt_handler)(int,siginfo_t *,void *);
35 };
36 
37 /* Our internal sighandler. */
38 static void waitpipe_sigchld(int signo,siginfo_t *siginfo,void *ucontext);
39 
40 /*
41  * There can only be one waitpipe per process, so this is safe.
42  */
43 static struct waitpipectl waitpipe = { NULL,NULL,NULL };
44 static struct sigaction waitpipe_act = {
45  .sa_sigaction = waitpipe_sigchld,
46  .sa_flags = SA_SIGINFO,
47 };
48 
49 void waitpipe_notify(int signo,siginfo_t *siginfo) {
50  return waitpipe_sigchld(signo,siginfo,NULL);
51 }
52 
53 static void waitpipe_sigchld(int signo,siginfo_t *siginfo,void *ucontext) {
54  pid_t pid;
55  /* int code; */
56  /* int status; */
57  int *pipefds;
58  /* int rc; */
59 
60  pid = siginfo->si_pid;
61  if (signo == SIGCHLD && pid > 0) {
62 
63  /*
64  * code = siginfo->si_code;
65  * status = siginfo->si_status;
66  */
67  vdebug(9,LA_LIB,LF_WAITPIPE,"pid %d code 0x%08x status 0x%08x\n",
68  pid,siginfo->si_code,siginfo->si_status);
69 
70  pipefds = (int *)g_hash_table_lookup(waitpipe.pids,
71  (gpointer)(uintptr_t)pid);
72  if (pipefds) {
73  /*
74  * NB: we could also write the code or status, but waitpid()
75  * can get those things too, so why bother?
76  */
77  vdebug(9,LA_LIB,LF_WAITPIPE,"writing to writefd %d for pid %d\n",
78  pipefds[1],pid);
79 
80  if (write(pipefds[1],"",1) < 0)
81  verror("write(fd %d): %s\n",pipefds[1],strerror(errno));
82  }
83  else if (waitpipe.alt_handler) {
84  vdebug(9,LA_LIB,LF_WAITPIPE,"invoking alt sigchld handler for pid %d\n",
85  pid);
86 
87  waitpipe.alt_handler(signo,siginfo,ucontext);
88  }
89  /*
90  * XXX: don't try this! We cannot wait for "unknown" children;
91  * they might just signal us before we record them in our
92  * table. Or, they might be *vanishing* from our table, but
93  * signal us, I think? Anyway, we can't wait for anything here;
94  * any waiting has to happen elsewhere once we *know* that a
95  * specific process should be signaling us.
96  */
97  /*
98  else {
99  status = 0;
100  rc = waitpid(pid,&status,WNOHANG);
101  if (rc == 0) {
102  vdebug(9,LA_LIB,LF_WAITPIPE,
103  "SIGCHLD for %d, but waitpid returned nothing!\n",pid);
104  }
105  else if (rc < 0) {
106  verror("waitpid(%d): %s\n",pid,strerror(errno));
107  }
108  else {
109  vdebug(9,LA_LIB,LF_WAITPIPE,
110  "waited for unknown child %d successfully\n",pid);
111  }
112  }
113  */
114  }
115 }
116 
118  if (waitpipe.pids)
119  return 1;
120 
121  return 0;
122 }
123 
124 int waitpipe_init_ext(void (*alt_handler)(int,siginfo_t *,void *)) {
125  if (waitpipe.pids) {
126  vwarn("global waitpipe already initialized!\n");
127  errno = EBUSY;
128  return -1;
129  }
130 
131  vdebug(9,LA_LIB,LF_WAITPIPE,"alt_handler = %p\n",alt_handler);
132 
133  waitpipe.pids = g_hash_table_new(g_direct_hash,g_direct_equal);
134  waitpipe.readfds = g_hash_table_new(g_direct_hash,g_direct_equal);
135  waitpipe.alt_handler = alt_handler;
136 
137  return 0;
138 }
139 
140 int waitpipe_init_auto(void (*alt_handler)(int,siginfo_t *,void *)) {
141  if (waitpipe.pids) {
142  vwarn("global waitpipe already initialized!\n");
143  errno = EBUSY;
144  return -1;
145  }
146 
147  vdebug(9,LA_LIB,LF_WAITPIPE,"alt_handler = %p\n",alt_handler);
148 
149  waitpipe.pids = g_hash_table_new(g_direct_hash,g_direct_equal);
150  waitpipe.readfds = g_hash_table_new(g_direct_hash,g_direct_equal);
151  waitpipe.alt_handler = alt_handler;
152 
153  if (sigaction(SIGCHLD,&waitpipe_act,NULL)) {
154  g_hash_table_destroy(waitpipe.pids);
155  g_hash_table_destroy(waitpipe.readfds);
156  waitpipe.pids = NULL;
157  waitpipe.readfds = NULL;
158  waitpipe.alt_handler = NULL;
159  verror("sigaction: %s\n",strerror(errno));
160  return -1;
161  }
162 
163  return 0;
164 }
165 
166 int waitpipe_fini(void) {
167  if (waitpipe.pids) {
168  g_hash_table_destroy(waitpipe.pids);
169  g_hash_table_destroy(waitpipe.readfds);
170  }
171  waitpipe.pids = NULL;
172  waitpipe.readfds = NULL;
173  waitpipe.alt_handler = NULL;
174 
175  vdebug(9,LA_LIB,LF_WAITPIPE,"fini\n");
176 
177  return 0;
178 }
179 
180 /*
181  * Returns half of a pipe -- the end that will receive the write when a
182  * SIGCHLD comes in for one of our pids.
183  */
184 int waitpipe_add(int pid) {
185  int *pipefds;
186  int rc;
187  siginfo_t si;
188 
189  if (!waitpipe.pids) {
190  verror("waitpipe not initialized!\n");
191  errno = EINVAL;
192  return -1;
193  }
194 
195  pipefds = malloc(sizeof(*pipefds)*2);
196 
197  pipefds[0] = -1;
198  pipefds[1] = -1;
199 
200  if (pipe(pipefds)) {
201  verror("pipe: %s\n",strerror(errno));
202  free(pipefds);
203  return -1;
204  }
205 
206  /*
207  * Make them nonblocking so our sighandler doesn't block on a full
208  * pipe. I can't imagine much how this could happen, but I suppose
209  * if the ptrace target has a bug and breakpoint keeps being hit
210  * over and over again, and the waitpipe_sigchld handler is called
211  * at least max pipe size times before the thread with the select()
212  * handling the other end of the waitpipe is called, then we have a
213  * problem. Other than that though...
214  *
215  * And it is also convenient for the child not to block on a read()
216  * of the pipe if there is nothing there, doh.
217  */
218  fcntl(pipefds[0],F_SETFL,fcntl(pipefds[0],F_GETFL) | O_NONBLOCK);
219  fcntl(pipefds[1],F_SETFL,fcntl(pipefds[1],F_GETFL) | O_NONBLOCK);
220 
221  /*
222  * Also make them close on exec; we don't want any forked/exec
223  * targets/analyses to inherit these...
224  */
225  fcntl(pipefds[0],F_SETFD,FD_CLOEXEC);
226  fcntl(pipefds[1],F_SETFD,FD_CLOEXEC);
227 
228  /* Place the pipe in the child pid demux table. */
229  g_hash_table_insert(waitpipe.pids,
230  (gpointer)(uintptr_t)pid,(gpointer)(uintptr_t)pipefds);
231 
232  /* Place the readfd/pid in the reverse child pid demux table. */
233  g_hash_table_insert(waitpipe.readfds,
234  (gpointer)(uintptr_t)pipefds[0],
235  (gpointer)(uintptr_t)pid);
236 
237  /*
238  * NB: once it is in our tables, and thus is ready to responded to
239  * from our signal handler, check and make sure @pid is actually
240  * live. If it is not, we won't get signaled.
241  */
242  memset(&si,0,sizeof(si));
243  rc = waitid(P_PID,pid,&si,WNOHANG | WEXITED | WNOWAIT);
244  if (rc < 0) {
245  if (errno == ECHILD) {
246  if (kill(pid,0) < 0 && errno == ESRCH) {
247  /* Unwind everything we just did. */
248  g_hash_table_remove(waitpipe.pids,(gpointer)(uintptr_t)pid);
249  g_hash_table_remove(waitpipe.readfds,(gpointer)(uintptr_t)pipefds[0]);
250  close(pipefds[1]);
251  close(pipefds[0]);
252 
253  vwarn("pid %d disappeared before it could be added: %s!\n",
254  pid,strerror(errno));
255  errno = EINVAL;
256  return -1;
257  }
258  }
259  else {
260  vwarn("pid %d disappeared before it could be added: %s!\n",
261  pid,strerror(errno));
262  errno = EINVAL;
263  return -1;
264  }
265  }
266  else if (rc == 0 && si.si_pid == pid) {
267  if (!g_hash_table_lookup(waitpipe.pids,(gpointer)(uintptr_t)pid)) {
268  vwarn("pid %d exited before we tracked it; notifying.\n",pid);
269  waitpipe_notify(si.si_signo,&si);
270  }
271  else {
272  vwarn("pid %d exited before we fully tracked it; notification"
273  " already in progress.\n",pid);
274  }
275  return 0;
276  }
277 
278  vdebug(9,LA_LIB,LF_WAITPIPE,"pid %d wfd %d rfd %d\n",pid,pipefds[1],pipefds[0]);
279 
280  /* Return the read half of the pipe for a select()-based loop to wait on. */
281  return pipefds[0];
282 }
283 
284 int waitpipe_remove(int pid) {
285  int *pipefds;
286 
287  if (!waitpipe.pids) {
288  verror("waitpipe not initialized!\n");
289  errno = EINVAL;
290  return -1;
291  }
292 
293  pipefds = (int *)g_hash_table_lookup(waitpipe.pids,(gpointer)(uintptr_t)pid);
294  if (pipefds) {
295  g_hash_table_remove(waitpipe.pids,(gpointer)(uintptr_t)pid);
296  g_hash_table_remove(waitpipe.readfds,(gpointer)(uintptr_t)pipefds[0]);
297  close(pipefds[1]);
298  close(pipefds[0]);
299 
300  vdebug(9,LA_LIB,LF_WAITPIPE,"pid %d wfd %d rfd %d\n",pid,pipefds[1],pipefds[0]);
301 
302  return 0;
303  }
304  else {
305  errno = ESRCH;
306  return -1;
307  }
308 }
309 
310 int waitpipe_get(int pid) {
311  int *pipefds;
312 
313  if (!waitpipe.pids) {
314  verror("waitpipe not initialized!\n");
315  errno = EINVAL;
316  return -1;
317  }
318 
319  pipefds = (int *)g_hash_table_lookup(waitpipe.pids,
320  (gpointer)(uintptr_t)pid);
321  if (pipefds && pipefds[0])
322  return pipefds[0];
323  else {
324  vdebug(9,LA_LIB,LF_WAITPIPE,"cannot find readfd for pid %d\n",pid);
325  errno = ESRCH;
326  return -1;
327  }
328 }
329 
330 int waitpipe_get_pid(int readfd) {
331  int pid;
332 
333  if (!waitpipe.pids) {
334  verror("waitpipe not initialized!\n");
335  errno = EINVAL;
336  return -1;
337  }
338 
339  pid = (int)(uintptr_t)g_hash_table_lookup(waitpipe.readfds,
340  (gpointer)(uintptr_t)readfd);
341  if (pid)
342  return pid;
343  else {
344  vdebug(9,LA_LIB,LF_WAITPIPE,"cannot find pid for readfd %d\n",readfd);
345  errno = ESRCH;
346  return -1;
347  }
348 }
349 
350 int waitpipe_drain(int pid) {
351  /* Do not make static to keep thread-safe. */
352  char buf[128];
353  int *pipefds;
354  int rc;
355  int retval = 0;
356 
357  if (!waitpipe.pids) {
358  verror("waitpipe not initialized!\n");
359  errno = EINVAL;
360  return -1;
361  }
362 
363  pipefds = (int *)g_hash_table_lookup(waitpipe.pids,(gpointer)(uintptr_t)pid);
364  if (pipefds) {
365  while ((rc = read(pipefds[0],buf,sizeof(buf)))) {
366  if (rc < 0) {
367  if (errno == EAGAIN || errno == EWOULDBLOCK)
368  return retval;
369  else if (errno == EINTR)
370  /* Keep trying? */
371  ;
372  else {
373  verror("read pipefd(pid %d): %s\n",pid,strerror(errno));
374  return -1;
375  }
376  }
377  else
378  retval += rc;
379  }
380 
381  vdebug(9,LA_LIB,LF_WAITPIPE,"pid %d wfd %d rfd %d: %d bytes\n",
382  pid,pipefds[1],pipefds[0],retval);
383 
384  return retval;
385  }
386  else {
387  errno = ESRCH;
388  return -1;
389  }
390 }
void(* alt_handler)(int, siginfo_t *, void *)
Definition: waitpipe.c:34
int waitpipe_remove(int pid)
Definition: waitpipe.c:284
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
Definition: qemuhacks.c:77
int waitpipe_is_initialized(void)
Definition: waitpipe.c:117
#define verror(format,...)
Definition: log.h:30
GHashTable * pids
Definition: waitpipe.c:32
#define vwarn(format,...)
Definition: log.h:33
void free(void *ptr)
Definition: debugserver.c:207
int waitpipe_get(int pid)
Definition: waitpipe.c:310
int waitpipe_fini(void)
Definition: waitpipe.c:166
int waitpipe_init_ext(void(*alt_handler)(int, siginfo_t *, void *))
Definition: waitpipe.c:124
Definition: log.h:68
int waitpipe_drain(int pid)
Definition: waitpipe.c:350
#define vdebug(devel, areas, flags, format,...)
Definition: log.h:302
void waitpipe_notify(int signo, siginfo_t *siginfo)
Definition: waitpipe.c:49
int waitpipe_add(int pid)
Definition: waitpipe.c:184
int waitpipe_init_auto(void(*alt_handler)(int, siginfo_t *, void *))
Definition: waitpipe.c:140
GHashTable * readfds
Definition: waitpipe.c:33
void * malloc(size_t size)
Definition: debugserver.c:214
int waitpipe_get_pid(int readfd)
Definition: waitpipe.c:330