Path: utzoo!mnetor!uunet!husc6!cmcl2!brl-adm!umd5!trantor.umd.edu!chris From: chris@trantor.umd.edu (Chris Torek) Newsgroups: comp.unix.questions Subject: Re: Wait on an arbitrary process containing a pattern Message-ID: <2275@umd5.umd.edu> Date: 11 Feb 88 06:35:05 GMT References: <9493@steinmetz.steinmetz.UUCP> Sender: news@umd5.umd.edu Reply-To: chris@trantor.umd.edu (Chris Torek) Organization: University of Maryland, College Park Lines: 204 In article <9493@steinmetz.steinmetz.UUCP> montnaro@sprite.steinmetz.ge.com (Skip Montanaro) writes: >I would like to be able to wait on an arbitrary process.... No good. There is, however, an alternate solution, given what you want done: >... I have a peripheral cleanup task in a Makefile that compresses >and archives some intermediate files. I don't want the mainline build >to be slowed down by this peripheral work, but at the same time, the >separate archiving commands all write to the same archive file, so >they have to wait for each other. Instead of having the programs wait for each other, have them wait for exclusive access to the file(s) involved. In this case, a short C program using the system's locking primitives (modern SysV and BSD systems have advisory file locking) and then calling the shell will do the trick: /* this code is utterly untested, but it probably works */ #include #include #include #include /* BSD; else make up values for EX_xxx */ char *progname; char *buildcmd(), *malloc(); /* #define index strchr */ /* if not BSD */ char *index(); /* * Print this program's name and call perror, then optionally exit. * The constant saving and restoring of errno (which might be * clobbered by fprintf) would get tiresome otherwise. * * A routine like this, but with printf-style formats, really * should be part of the standard library (and is at U of MD CSD). */ void gripe(quit, e, msg) int quit; int e; char *msg; { extern int errno; if (e < 0) /* default */ e = errno; fprintf(stderr, "%s: ", progname); errno = e; perror(msg); if (quit) exit(quit); } main(argc, argv) int argc; char **argv; { char *cmd; int fd, saverr, pid, status, w; progname = argv[0]; if (argc < 3) { fprintf(stderr, "usage: %s file command...\n", progname); exit(EX_USAGE); } /* * Lock the file, then execute the command in a fork. * The shell re-parses the command, so we have to quote it * (see buildcmd). */ fd = lockit(argv[1]); cmd = buildcmd(argc - 2, argv + 2); (void) fflush(stderr); /* not supposed to be buffered, but... */ switch (pid = fork()) { case -1: /* failed */ gripe(EX_OSERR, -1, "fork"); /* NOTREACHED */ case 0: /* child */ (void) close(fd); execlp("sh", "sh", "-c", cmd, (char *)0); execl("/bin/sh", "sh", "-c", cmd, (char *)0); gripe(0, -1, "exec(/bin/sh)"); fflush(stderr); _exit(1); /* NOTREACHED */ } /* THE FOLLOWING ASSUMES EXITING UNLOCKS */ /* parent */ while ((w = wait(&status)) != pid && w != -1) /* void */; if (w == -1) gripe(EX_OSERR, -1, "child vanished?! wait"); /* this is somewhat gross */ if (status & 0xff) { fprintf(stderr, "%s: died on signal %d%s\n", cmd, status & 0x7f, status & 0x80 ? " (core dumped)" : ""); exit(EX_UNAVAILABLE); /* ??? */ } exit((status >> 8) & 0xff); /* NOTREACHED */ } /* * Shell special characters, for buildcmd, cmdlen, and quotecmd. * Each of these except \n can be quoted by prepending a backslash; * \n requires "" or ''. */ static char specials[] = "'\"\\*?[$^&()`|<>{};= \t\n"; /* auxiliary for buildcmd: return the length of the quoted version of s */ static int cmdlen(s) register char *s; { register int l = 0, c; while ((c = *s++) != 0) { len++; /* count it */ if (index(specials, c)) /* and maybe \ or "" too */ len += c == '\n' ? 2 : 1; } return (len); } /* * auxiliary for buildcmd: stuff into p the quoted version of s; return * the place after the last stuffed character. */ static char * quotecmd(p, s) register char *p, *s; { register int c; while ((c = *s++) != 0) { if (index(specials, c)) *p++ = c == '\n' ? '"' : '\\'; *p++ = c; if (c == '\n') *p++ = '"'; } return (p); } /* * Build a quoted version of a command given an argument vector v of * length n. */ char * buildcmd(n, v) int n; register char **v; { register int sum = 0; /* should be size_t, but no */ register char **p = v + n; register char *s; char *cmd; /* how much room for 'x y z\0'? */ while (--p >= v) sum += cmdlen(*p) + 1; if ((cmd = malloc(sum)) == NULL) gripe(EX_OSERR, -1, "malloc"); /* put them in */ for (sum = n, s = cmd; sum > 0;) { s = quotecmd(s, *v++); *s++ = --sum > 0 ? ' ' : 0; } return (cmd); } /* * The next function is clearly O/S dependent. I have only a vague * idea as to how to do this on SysV. * * Lock the named file, creating it first if necessary. */ int lockit(fname) char *fname; { int fd = open(fname, O_RDONLY | O_CREAT, 0666); if (fd < 0) gripe(EX_CANTCREAT, -1, fname); /* or EX_NOINPUT? */ /* BSD: get exclusive lock, waiting if necessary */ if (flock(fd, LOCK_EX)) gripe(EX_OSERR, -1, "flock"); return (fd); } -- In-Real-Life: Chris Torek, Univ of MD Computer Science, +1 301 454 7163 (hiding out on trantor.umd.edu until mimsy is reassembled in its new home) Domain: chris@mimsy.umd.edu Path: not easily reachable