Wait on an arbitrary process containing a pattern
Chris Torek
chris at trantor.umd.edu
Thu Feb 11 16:35:05 AEST 1988
In article <9493 at steinmetz.steinmetz.UUCP> montnaro at 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 <sys/types.h>
#include <sys/file.h>
#include <stdio.h>
#include <sysexits.h> /* 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 <stddef.h> */
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 at mimsy.umd.edu Path: not easily reachable
More information about the Comp.unix.questions
mailing list