Xref: utzoo comp.unix.questions:9874 comp.unix.wizards:11880 Path: utzoo!utgpu!water!watmath!clyde!att!osu-cis!tut.cis.ohio-state.edu!bloom-beacon!athena.mit.edu!scs From: scs@athena.mit.edu (Steve Summit) Newsgroups: comp.unix.questions,comp.unix.wizards Subject: Re: Why does a shell script fail in crontab? Message-ID: <7622@bloom-beacon.MIT.EDU> Date: 23 Oct 88 19:39:39 GMT References: <287@lakart.UUCP> <1988Oct23.010903.21369@utzoo.uucp> Sender: daemon@bloom-beacon.MIT.EDU Reply-To: scs@adam.pika.mit.edu (Steve Summit) Lines: 86 In article <1988Oct23.010903.21369@utzoo.uucp> henry@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@adam.pika.mit.edu