Change to xargs to avoid orphaning of utility processes on
signal|exit 255 from child
Matthew Story
matthewstory at gmail.com
Sat Mar 10 18:23:04 UTC 2012
On Fri, Mar 9, 2012 at 11:27 PM, Matthew Story <matthewstory at gmail.com>wrote:
> On Fri, Mar 9, 2012 at 6:13 PM, Matthew Story <matthewstory at gmail.com>wrote:
>
>> On Fri, Mar 9, 2012 at 5:54 PM, Jilles Tjoelker <jilles at stack.nl> wrote:
>>
>>> On Tue, Feb 21, 2012 at 12:50:19PM -0500, Matthew Story wrote:
>>> > On Thu, Feb 16, 2012 at 7:09 PM, Matthew Story <matthewstory at gmail.com
>>> >wrote:
>>> > > Apologies if this is the wrong list, I would like to submit a patch
>>> that
>>> > > changes the behavior of xargs(1) on signal to child utility process
>>> or
>>> > > child utility process exiting 255. The patch(es) is|are available
>>> here:
>>>
>>> [...snip]
>>>
>>> > This would cause xargs to wait on children if the command line cannot
>>> be
>>> > assembled, or utility cannot be invoked, in addition to the 2 cases
>>> covered
>>> > by the patch. This should leave termination via signal to the xargs
>>>
>> > process itself as the only outstanding case where xargs orphans
>>> utilities,
>>> > which is congruent with sh(1) behavior, and still allows for signaling
>>> all
>>> > processes via signal to the process group, if you actually desire to
>>> signal
>>> > all utility processes, along with xargs itself. Thoughts?
>>>
>>> I think that makes sense.
>>>
>>> I updated the patch to the recent changes in -current and changed a
>>> local from short to int:
>>> [...snip]
>>
>> changes look good to me, thanks for cleaning it up for -CURRENT. I will
>> provide an additional patch to cover waiting for the other 2 cases (cannot
>> assemble, or cannot invoke) later tonight after some more testing.
>> [...snip]
>>
>
> I believe I have finished removing the orphan cases for the assembly
> failure and invocation failure cases, updated patch attached for review,
> also available here
>
>
> http://axe0.blackskyresearch.net/patches/matt/xargs.no_orphan.full.patch.txt
>
Updated this patch (also attached), to consolidate all waitchildren, exit
to use xexit, and tidied up a multiple warning condition on childerr to
warn only once while waiting to exit.
>
> This patch includes the cleaned-up changes Jilles sent back earlier
> (thanks again).
>
> --
> regards,
> matt
>
--
regards,
matt
-------------- next part --------------
Index: xargs.1
===================================================================
--- xargs.1 (revision 232787)
+++ xargs.1 (working copy)
@@ -294,17 +294,17 @@
.Ar utility
reads from the standard input.
.Pp
-The
-.Nm
-utility exits immediately (without processing any further input) if a
-command line cannot be assembled,
+If a command line cannot be assembled, or
+cannot be invoked, or if an invocation of
.Ar utility
-cannot be invoked, an invocation of
-.Ar utility
is terminated by a signal,
or an invocation of
.Ar utility
-exits with a value of 255.
+exits with a value of 255, the
+.Nm
+utility stops processing input and exits after all invocations of
+.Ar utility
+finish processing.
.Sh EXIT STATUS
The
.Nm
Index: xargs.c
===================================================================
--- xargs.c (revision 232787)
+++ xargs.c (working copy)
@@ -70,6 +70,7 @@
static void usage(void);
void strnsubst(char **, const char *, const char *, size_t);
static pid_t xwait(int block, int *status);
+static void xexit(const char *, const int);
static void waitchildren(const char *, int);
static void pids_init(void);
static int pids_empty(void);
@@ -280,10 +281,8 @@
switch (ch = getchar()) {
case EOF:
/* No arguments since last exec. */
- if (p == bbp) {
- waitchildren(*av, 1);
- exit(rval);
- }
+ if (p == bbp)
+ xexit(*av, rval);
goto arg1;
case ' ':
case '\t':
@@ -308,8 +307,10 @@
count++; /* Indicate end-of-line (used by -L) */
/* Quotes do not escape newlines. */
-arg1: if (insingle || indouble)
- errx(1, "unterminated quote");
+arg1: if (insingle || indouble) {
+ warnx("unterminated quote");
+ xexit(*av, 1);
+ }
arg2:
foundeof = *eofstr != '\0' &&
strncmp(argp, eofstr, p - argp) == 0;
@@ -342,8 +343,10 @@
*/
inpline = realloc(inpline, curlen + 2 +
strlen(argp));
- if (inpline == NULL)
- errx(1, "realloc failed");
+ if (inpline == NULL) {
+ warnx("realloc failed");
+ xexit(*av, 1);
+ }
if (curlen == 1)
strcpy(inpline, argp);
else
@@ -360,17 +363,17 @@
*/
if (xp == endxp || p > ebp || ch == EOF ||
(Lflag <= count && xflag) || foundeof) {
- if (xflag && xp != endxp && p > ebp)
- errx(1, "insufficient space for arguments");
+ if (xflag && xp != endxp && p > ebp) {
+ warnx("insufficient space for arguments");
+ xexit(*av, 1);
+ }
if (jfound) {
for (avj = argv; *avj; avj++)
*xp++ = *avj;
}
prerun(argc, av);
- if (ch == EOF || foundeof) {
- waitchildren(*av, 1);
- exit(rval);
- }
+ if (ch == EOF || foundeof)
+ xexit(*av, rval);
p = bbp;
xp = bxp;
count = 0;
@@ -394,8 +397,10 @@
if (zflag)
goto addch;
/* Backslash escapes anything, is escaped by quotes. */
- if (!insingle && !indouble && (ch = getchar()) == EOF)
- errx(1, "backslash at EOF");
+ if (!insingle && !indouble && (ch = getchar()) == EOF) {
+ warnx("backslash at EOF");
+ xexit(*av, 1);
+ }
/* FALLTHROUGH */
default:
addch: if (p < ebp) {
@@ -404,11 +409,15 @@
}
/* If only one argument, not enough buffer space. */
- if (bxp == xp)
- errx(1, "insufficient space for argument");
+ if (bxp == xp) {
+ warnx("insufficient space for argument");
+ xexit(*av, 1);
+ }
/* Didn't hit argument limit, so if xflag object. */
- if (xflag)
- errx(1, "insufficient space for arguments");
+ if (xflag) {
+ warnx("insufficient space for arguments");
+ xexit(*av, 1);
+ }
if (jfound) {
for (avj = argv; *avj; avj++)
@@ -449,16 +458,20 @@
* a NULL at the tail.
*/
tmp = malloc((argc + 1) * sizeof(char**));
- if (tmp == NULL)
- errx(1, "malloc failed");
+ if (tmp == NULL) {
+ warnx("malloc failed");
+ xexit(*argv, 1);
+ }
tmp2 = tmp;
/*
* Save the first argument and iterate over it, we
* cannot do strnsubst() to it.
*/
- if ((*tmp++ = strdup(*avj++)) == NULL)
- errx(1, "strdup failed");
+ if ((*tmp++ = strdup(*avj++)) == NULL) {
+ warnx("strdup failed");
+ xexit(*argv, 1);
+ }
/*
* For each argument to utility, if we have not used up
@@ -475,8 +488,10 @@
if (repls > 0)
repls--;
} else {
- if ((*tmp = strdup(*tmp)) == NULL)
- errx(1, "strdup failed");
+ if ((*tmp = strdup(*tmp)) == NULL) {
+ warnx("strdup failed");
+ xexit(*argv, 1);
+ }
tmp++;
}
}
@@ -547,7 +562,8 @@
childerr = 0;
switch (pid = vfork()) {
case -1:
- err(1, "vfork");
+ warn("vfork");
+ xexit(*argv, 1);
case 0:
if (oflag) {
if ((fd = open(_PATH_TTY, O_RDONLY)) == -1)
@@ -593,26 +609,47 @@
}
static void
+xexit(const char *name, const int exit_code) {
+ waitchildren(name, 1);
+ exit(exit_code);
+}
+
+static void
waitchildren(const char *name, int waitall)
{
pid_t pid;
int status;
+ int cause_exit = 0;
while ((pid = xwait(waitall || pids_full(), &status)) > 0) {
- /* If we couldn't invoke the utility, exit. */
- if (childerr != 0) {
+ /* If we couldn't invoke the utility, warn and flag for exit. */
+ if (childerr != 0 && cause_exit == 0) {
errno = childerr;
- err(errno == ENOENT ? 127 : 126, "%s", name);
+ waitall = 1;
+ cause_exit = ENOENT ? 127 : 126;
+ warn("%s", name);
}
- if (WIFSIGNALED(status))
- errx(1, "%s: terminated with signal %d; aborting",
+ /*
+ * If utility exited because of a signal or with a value of
+ * 255, warn (per POSIX), and then wait until all other
+ * children have exited before exiting 1-125. POSIX requires
+ * xargs to stop reading if child exits because of a signal or
+ * with 255, but it does not require us to exit immediately;
+ * waiting is preferable to orphaning.
+ */
+ if (WIFSIGNALED(status)) {
+ waitall = cause_exit = 1;
+ warnx("%s: terminated with signal %d; aborting",
name, WTERMSIG(status));
- if (WEXITSTATUS(status) == 255)
- errx(1, "%s: exited with status 255; aborting", name);
- if (WEXITSTATUS(status))
- rval = 1;
+ } else if (WEXITSTATUS(status) == 255) {
+ waitall = cause_exit = 1;
+ warnx("%s: exited with status 255; aborting", name);
+ } else if (WEXITSTATUS(status))
+ rval = 1;
}
+ if (cause_exit)
+ exit(cause_exit);
if (pid == -1 && errno != ECHILD)
err(1, "waitpid");
}
More information about the freebsd-arch
mailing list