2 * after: wait for an unrelated process to terminate, returning
3 * its exit code if possible.
7 * Possible future extensions:
9 * - improvements to existing methods
10 * * the ptrace method on MacOS has the really unpleasant
11 * property that if after is interrupted or killed then the
12 * traced process dies too. We could mitigate that by
13 * trapping some common fatal signals (certainly including
14 * SIGINT and SIGTERM) and have them PT_DETACH before
15 * self-reraising. Alternatively, we could fork off a
16 * subprocess to do the tracing, and a pipe communicating
17 * with the parent; then if the subprocess detected its pipe
18 * end being closed it could PT_DETACH. Then even kill -9
19 * against the primary `after' process would allow the
20 * subprocess to clean up.
22 * - alternatives to ptrace on systems supporting different
23 * process-debugging mechanisms
25 * - possibly an approach based on invoking ps?
26 * * with a longish interval, to prevent system overload
27 * * and we have to deal with the profusion of ps options. I
28 * suspect going by SUS/POSIX is the best thing here: set
29 * POSIXLY_CORRECT and expect ps to support the options
31 * * need to try to avoid race condition if the process ends
32 * and another one of the same name starts up. Printing the
33 * start time seems like the sensible thing, except that SUS
34 * doesn't give a standard option to do that; we can only
35 * print elapsed time since the process began, which means
36 * we have to be alert for off-by-1s errors if we wait 30s
37 * and the time only changes by 29 for some irritating
48 #include <sys/types.h>
58 * ptrace-based waiting method, which gets the process's exit
62 #include <sys/ptrace.h>
64 #if !defined PTRACE_FLAVOUR_LINUX && !defined PTRACE_FLAVOUR_BSD
66 * Attempt to autodetect ptrace flavour.
68 #if defined PTRACE_ATTACH
73 #define ATTACH_TO(pid) ptrace(PTRACE_ATTACH, pid, NULL, 0)
74 #define ATTACH_STR "ptrace(PTRACE_ATTACH, %d)"
75 #define CONTINUE(pid) ptrace(PTRACE_CONT, pid, NULL, 0)
76 #define CONT_STR "ptrace(PTRACE_CONT, %d)"
78 #elif defined PT_ATTACH
83 #define ATTACH_TO(pid) ptrace(PT_ATTACH, pid, NULL, 0)
84 #define ATTACH_STR "ptrace(PT_ATTACH, %d)"
85 #define CONTINUE(pid) ptrace(PT_CONTINUE, pid, (caddr_t)1, 0)
86 #define CONT_STR "ptrace(PT_CONTINUE, %d)"
89 #error Unable to autodetect ptrace flavour.
90 #endif /* ptrace flavour detection */
92 #endif /* outer ifdef containing flavour detection */
94 int after_ptrace(int pid
)
97 #ifdef PTRACE_FLAVOUR_LINUX
98 ptrace(PTRACE_ATTACH
, pid
, NULL
, 0)
100 ptrace(PT_ATTACH
, pid
, NULL
, 0)
104 * If we can't attach to the process, that's not a fatal
105 * error; it just means we fall back to the next available
108 errlen
+= sprintf(errbuf
+errlen
,
109 "%s: "ATTACH_STR
": %.256s\n",
110 pname
, pid
, strerror(errno
));
115 * Having successfully attached to the process, however, any
116 * subsequent error is fatal.
121 wpid
= waitpid(pid
, &wstatus
, 0);
125 } else if (WIFEXITED(wstatus
)) {
126 return WEXITSTATUS(wstatus
);
128 if (CONTINUE(pid
) < 0) {
129 fprintf(stderr
, "%s: "CONT_STR
": %.256s\n",
130 pname
, pid
, strerror(errno
));
142 * /proc-based waiting method.
145 int after_proc(int pid
)
149 sprintf(buf
, "/proc/%d", pid
);
151 if (chdir(buf
) < 0) {
155 errlen
+= sprintf(errbuf
+errlen
,
156 "%s: chdir(\"%.32s\"): %.256s\n",
157 pname
, buf
, strerror(errno
));
163 } while (getcwd(buf
, sizeof(buf
)));
174 const char usagemsg
[] =
181 "where: -x return an error if the process's exit code is unavailable\n"
182 " -z return 0 instead of the process's exit code\n"
184 " also: after --version report version number\n"
185 " after --help display this help text\n"
186 " after --licence display the (MIT) licence text\n"
190 fputs(usagemsg
, stdout
);
193 const char licencemsg
[] =
194 "after is copyright 2008 Simon Tatham.\n"
196 "Permission is hereby granted, free of charge, to any person\n"
197 "obtaining a copy of this software and associated documentation files\n"
198 "(the \"Software\"), to deal in the Software without restriction,\n"
199 "including without limitation the rights to use, copy, modify, merge,\n"
200 "publish, distribute, sublicense, and/or sell copies of the Software,\n"
201 "and to permit persons to whom the Software is furnished to do so,\n"
202 "subject to the following conditions:\n"
204 "The above copyright notice and this permission notice shall be\n"
205 "included in all copies or substantial portions of the Software.\n"
207 "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n"
208 "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n"
209 "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n"
210 "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\n"
211 "BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n"
212 "ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n"
213 "CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n"
218 fputs(licencemsg
, stdout
);
222 #define SVN_REV "$Revision$"
223 char rev
[sizeof(SVN_REV
)];
226 strcpy(rev
, SVN_REV
);
228 for (p
= rev
; *p
&& *p
!= ':'; p
++);
231 while (*p
&& isspace((unsigned char)*p
)) p
++;
232 for (q
= p
; *q
&& !isspace((unsigned char)*q
) && *q
!= '$'; q
++);
234 printf("after revision %s", p
);
236 printf("after: unknown version");
241 int main(int argc
, char **argv
)
244 enum { UNWANTED
, OPTIONAL
, MANDATORY
} exitcode
= OPTIONAL
;
249 /* parse the command line arguments */
254 if (!strcmp(p
, "-x")) {
255 exitcode
= MANDATORY
;
256 } else if (!strcmp(p
, "-z")) {
260 if (!strcmp(p
, "--help")) {
263 } else if (!strcmp(p
, "--version")) {
266 } else if (!strcmp(p
, "--licence") || !strcmp(p
, "--license")) {
269 } else if (*p
=='-') {
270 fprintf(stderr
, "%s: unrecognised option `%s'\n", pname
, p
);
274 fprintf(stderr
, "%s: parameter `%s' unexpected\n", pname
, p
);
288 ret
= after_ptrace(pid
);
290 return (exitcode
== UNWANTED ?
0 : ret
);
293 if (exitcode
== MANDATORY
) {
295 * If we reach here, the user has demanded the process's
296 * return code, and we haven't been able to get it. Print
297 * the error messages we accrued while trying, and abandon
300 fputs(errbuf
, stderr
);
305 ret
= after_proc(pid
);
311 * If we reach here, we have run out of all our options. Print
312 * all our error messages, and return total failure.
314 fputs(errbuf
, stderr
);