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