[comp.unix.questions] Why does a shell script fail in crontab?

dg@lakart.UUCP (David Goodenough) (10/15/88)

I have a shell script ( /usr/local/dopath ) that has the job of updating
our UUCP map files every day at 7:00 AM. There is only one problem with it:
when it runs from crontab it produces a zero size Index file, yet when I
run it by hand from a uid 0 shell it succedes, and generates the correct
Index file. Here is the crontab entry:

0 7 * * *	root	/usr/local/dopath

and here is the contents of that same file:

#! /bin/sh
#
# first get the new map stuff from comp.mail.maps
#
umask 022
cd /usr/spool/maps
ln /usr/spool/news/comp/mail/maps/* .
/usr/local/unshar -c * 2>&1 >/dev/null
cp [ud].* /usr/lib/uucp/uumap
rm -f *
#
# Next get the temp stuff out of news.config
#
cd /usr/lib/uucp/uumap
rm -f d.Temp
/usr/local/getcon /usr/spool/news/news/config/* >d.Temp
#
# now build the path file
#
rm -f UUPATH
/usr/local/pathalias u.[A-Z]* d.* u.[a-z]* 2>/dev/null | \
    /usr/local/pathfilt | sort -u > UUPATH
#
# finally make the index
#
rm -f Index
bm "#N" [du].* | sed 's/:#N/ /' | tr -s '\011, ' ' ' | \
    awk '{ for (i = NF; i > 1; --i) printf "%s\t%s\n", $i, $1 }' | \
    sort -u > Index
#
chown uucp *
chmod 644 [ud].* UUPATH Index

--------
The thing I don't understand is that UUPATH is generated just fine, whereas
Index is only generated when I run this by hand. Note: this is a setuid
root shell script - yes I am aware of the possible security leak, but there
are only two people who use lakart that are unix.wizards, and we both know
the root password. I'm baffled to all H**L on this one - can anyone else
shed some light?
-- 
	dg@lakart.UUCP - David Goodenough		+---+
							| +-+-+
	....... !harvard!xait!lakart!dg			+-+-+ |
AKA:	dg%lakart.uucp@harvard.harvard.edu	  	  +---+

maart@cs.vu.nl (Maarten Litmaath) (10/18/88)

In article <287@lakart.UUCP> dg@lakart.UUCP (David Goodenough) writes:
\when it runs from crontab it produces a zero size Index file, yet when I
\run it by hand from a uid 0 shell it succedes, and generates the correct
\Index file.
\...
\bm "#N" [du].* | sed 's/:#N/ /' | tr -s '\011, ' ' ' | \
\    awk '{ for (i = NF; i > 1; --i) printf "%s\t%s\n", $i, $1 }' | \
\    sort -u > Index

I bet `bm' is in /usr/local, which is not in sh's standard PATH, but IS in
yours!
The `Index' file is created, but the command fails.

\Note: this is a setuid
\root shell script - yes I am aware of the possible security leak, but there
\are only two people who use lakart that are unix.wizards, and we both know
\the root password.

Some people will never learn...
-- 
Hippic sport:                         |Maarten Litmaath @ Free U Amsterdam:
             a contradiction in terms.|maart@cs.vu.nl, mcvax!botter!maart

rl@laura.UUCP (Ralf Luebeck) (10/18/88)

In article <287@lakart.UUCP> dg@lakart.UUCP (David Goodenough) writes:
>I have a shell script ( /usr/local/dopath ) that has the job of updating
>our UUCP map files every day at 7:00 AM. There is only one problem with it:
>when it runs from crontab it produces a zero size Index file, yet when I
>run it by hand from a uid 0 shell it succedes, and generates the correct
>Index file. Here is the crontab entry:
>
>0 7 * * *	root	/usr/local/dopath
>
>and here is the contents of that same file:
>[...deleted...]
>/usr/local/unshar -c * 2>&1 >/dev/null
>/usr/local/getcon /usr/spool/news/news/config/* >d.Temp
>/usr/local/pathalias u.[A-Z]* d.* u.[a-z]* 2>/dev/null | \
>    /usr/local/pathfilt | sort -u > UUPATH

David, I don't know what your local commands do, but I know, that commands
like 'su' fail as cronjob, when they call ttyname(3) or try to open(2)
"/dev/tty" or something else, 'cause cronjobs don't have a tty.
Hope this helps...

>	dg@lakart.UUCP - David Goodenough
ciao,
	Ralf

-  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
-  |>  |  .. |       |  	rl@unido.uucp (...seismo!unido!rl)
-  |\. |_ \/ |> <- < |< 	rl@unido.bitnet
-  -  -  -  -  - Computer Science Department (IRB) -  -  -  -  -  -  -
- University of Dortmund-  - P.O. Box 500500 -  - D-4600 Dortmund 50 -

randy@umn-cs.CS.UMN.EDU (Randy Orrison) (10/19/88)

In article <287@lakart.UUCP> dg@lakart.UUCP (David Goodenough) writes:
|0 7 * * *	root	/usr/local/dopath
		^^^^
|Note: this is a setuid
|root shell script - yes I am aware of the possible security leak, 

My question is: WHY is this a setuid shell script?  As set in your
crontab, it's executed by root, so there's no reason I can see to have
it setuid also.  In a situation like yours (only two users, both
trusted) I can see that it wouldn't be too much of a worry, but to do
it for no reason is just plain silly.

	-randy
--
Randy Orrison, Chemical Computer Thinking Battery  --  randy@cctb.mn.org
(aka randy@{ux.acss.umn.edu, umn-cs.uucp, umnacca.bitnet, halcdc.uucp})

		"So many bits, so few cycles."

henry@utzoo.uucp (Henry Spencer) (10/23/88)

The three things to look for when a program runs fine manually but
won't run from cron are:

1. Does it rely on any environment variables being set?  For example,
	we had some (binary-only, grr) typesetting software that refused
	to run if the HOME variable wasn't set.  Don't ask me why.

2. Does it rely on a non-standard (not /bin:/usr/bin) PATH?  Shell scripts
	in particular should *ALWAYS ALWAYS ALWAYS* set PATH first thing,
	if only to guarantee that they aren't getting the user's funny
	private versions of commands.  Don't forget to export the new PATH.

3. Does it rely on umask being non-zero?
-- 
The meek can have the Earth;    |    Henry Spencer at U of Toronto Zoology
the rest of us have other plans.|uunet!attcan!utzoo!henry henry@zoo.toronto.edu

scs@athena.mit.edu (Steve Summit) (10/24/88)

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