Linux waitpid/__WALL emulation wrapper: If WNOHANG is set, don't touch sigprocmask.
[platform/upstream/binutils.git] / gdb / nat / linux-waitpid.c
1 /* Wrapper implementation for waitpid for GNU/Linux (LWP layer).
2
3    Copyright (C) 2001-2014 Free Software Foundation, Inc.
4
5    This file is part of GDB.
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20 #ifdef GDBSERVER
21 #include "server.h"
22 #else
23 #include "defs.h"
24 #include "signal.h"
25 #endif
26
27 #include "nat/linux-nat.h"
28 #include "nat/linux-waitpid.h"
29 #include "gdb_wait.h"
30
31 #include <string.h>
32
33 /* Print debugging output based on the format string FORMAT and
34    its parameters.  */
35
36 static inline void
37 linux_debug (const char *format, ...)
38 {
39 #ifdef GDBSERVER
40   if (debug_threads)
41     {
42       va_list args;
43       va_start (args, format);
44       vfprintf (stderr, format, args);
45       va_end (args);
46     }
47 #else
48   /* GDB-specific debugging output.  */
49 #endif
50 }
51
52 /* Convert wait status STATUS to a string.  Used for printing debug
53    messages only.  */
54
55 char *
56 status_to_str (int status)
57 {
58   static char buf[64];
59
60   if (WIFSTOPPED (status))
61     {
62       if (WSTOPSIG (status) == SYSCALL_SIGTRAP)
63         snprintf (buf, sizeof (buf), "%s (stopped at syscall)",
64                   strsignal (SIGTRAP));
65       else
66         snprintf (buf, sizeof (buf), "%s (stopped)",
67                   strsignal (WSTOPSIG (status)));
68     }
69   else if (WIFSIGNALED (status))
70     snprintf (buf, sizeof (buf), "%s (terminated)",
71               strsignal (WTERMSIG (status)));
72   else
73     snprintf (buf, sizeof (buf), "%d (exited)", WEXITSTATUS (status));
74
75   return buf;
76 }
77
78 /* Wrapper function for waitpid which handles EINTR, and emulates
79    __WALL for systems where that is not available.  */
80
81 int
82 my_waitpid (int pid, int *status, int flags)
83 {
84   int ret, out_errno;
85
86   linux_debug ("my_waitpid (%d, 0x%x)\n", pid, flags);
87
88   if (flags & __WALL)
89     {
90       sigset_t block_mask, org_mask, wake_mask;
91       int wnohang;
92
93       wnohang = (flags & WNOHANG) != 0;
94       flags &= ~(__WALL | __WCLONE);
95
96       if (!wnohang)
97         {
98           flags |= WNOHANG;
99
100           /* Block all signals while here.  This avoids knowing about
101              LinuxThread's signals.  */
102           sigfillset (&block_mask);
103           sigprocmask (SIG_BLOCK, &block_mask, &org_mask);
104
105           /* ... except during the sigsuspend below.  */
106           sigemptyset (&wake_mask);
107         }
108
109       while (1)
110         {
111           /* Since all signals are blocked, there's no need to check
112              for EINTR here.  */
113           ret = waitpid (pid, status, flags);
114           out_errno = errno;
115
116           if (ret == -1 && out_errno != ECHILD)
117             break;
118           else if (ret > 0)
119             break;
120
121           if (flags & __WCLONE)
122             {
123               /* We've tried both flavors now.  If WNOHANG is set,
124                  there's nothing else to do, just bail out.  */
125               if (wnohang)
126                 break;
127
128               linux_debug ("blocking\n");
129
130               /* Block waiting for signals.  */
131               sigsuspend (&wake_mask);
132             }
133           flags ^= __WCLONE;
134         }
135
136       if (!wnohang)
137         sigprocmask (SIG_SETMASK, &org_mask, NULL);
138     }
139   else
140     {
141       do
142         ret = waitpid (pid, status, flags);
143       while (ret == -1 && errno == EINTR);
144       out_errno = errno;
145     }
146
147   linux_debug ("my_waitpid (%d, 0x%x): status(%x), %d\n",
148                pid, flags, status ? *status : -1, ret);
149
150   errno = out_errno;
151   return ret;
152 }