Why does a shell script fail in crontab?

Steve Summit scs at athena.mit.edu
Mon Oct 24 05:39:39 AEST 1988


In article <1988Oct23.010903.21369 at utzoo.uucp> henry at utzoo.uucp (Henry Spencer) writes:
>The three things to look for when a program runs fine manually but
>won't run from cron are:

I might add

     4.	Make sure it doesn't rely on a file descriptor being
	attached to a terminal, perhaps for the purposes of doing
	ioctl's.

I can't say I've been burned by this, but it can be a problem,
particularly with poorly-written software.

A related problem is

     5.	Make sure it doesn't rely on getlogin().

As getlogin(3) notes, 

	If getlogin is called within a process that is not
	attached to a terminal... getlogin returns a NULL pointer.
	A reasonable procedure for determining the login name is
	to first call getlogin and if it fails, to call
	getpwuid(getuid()).

These problems arise when neither file descriptors 0, 1, nor 2
(nor perhaps /dev/tty) are attached to a terminal.  A more subtle
problem, which is exceedingly rare but is worth knowing about if
you're into heavy-duty paranoia, arises when at least one of
file descriptors 0, 1, or 2 is not open at all.  It is
surprisingly easy to write a program which fails if, for
example, open returns 0.  One way is to write code which avoids
using dup2 (I suppose I do this because the man page mutters
something about its not being as portable, not that it should be
any more or less portable than dup):

	if(fork() == 0)
		{
		/* set up stdin for child */

		fd = open(infile, 0);
		close(0);
		dup(fd);	/* should return 0 */
		close(fd);

Note the failure mode if open returns 0.  Other than using dup2,
the "right" way of writing this is

		fd = open(infile, 0);	/* also check error here */
		if(fd != 0)
			{
			close(0);
			dup(fd);
			close(fd);
			}

although the contortions you have to go through if you're opening
all three standard file descriptors, and you want to worry about
all possible failure modes, are pretty fantastic.

The reason this problem is rare is that program-spawning daemons
such as cron take care to leave the first three file descriptors
open, usually on /dev/null.  (I've always suspected that this was
precisely to prevent the problem I described, although the topic
was discussed at length on this newsgroup a while back, under a
subject like "how to write a daemon," and several competing
hypotheses were advanced, which probably don't need to be
repeated.)

The one time I've actually seen this kind of problem involved not
cron but make and cpp.  It turns out that make always closes the
makefile after reading it, so make -f - (yes, Virginia, you can
put the Makefile on stdin) closes stdin, which then confuses cpp
because

	cpp infile outfile

gets file descriptor 0 when it opens infile, but later notices
that ifd==stdin, which it interprets to mean that it's been
reading standard input, so it no longer knows what to do with two
filename arguments.  (Apparently at least one of these bugs has
been fixed, because I can't reproduce it just now.  I wish this
machine had source.)

                                            Steve Summit
                                            scs at adam.pika.mit.edu



More information about the Comp.unix.questions mailing list