]>
git.saurik.com Git - apple/system_cmds.git/blob - atrun.tproj/atrun.c
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
22 * @APPLE_LICENSE_HEADER_END@
25 * atrun.c - run jobs queued by at; run with root privileges.
26 * Copyright (c) 1993 by Thomas Koenig
27 * All rights reserved.
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
32 * 1. Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
34 * 2. The name of the author(s) may not be used to endorse or promote
35 * products derived from this software without specific prior written
38 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
39 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
40 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
41 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
42 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
43 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
44 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
45 * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
47 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 #include <sys/fcntl.h>
53 #include <sys/types.h>
74 #include "pathnames.h"
77 /* File scope variables */
80 static char rcsid
[] = "$Id: atrun.c,v 1.1.1.2 2000/01/11 02:10:06 wsanchez Exp $";
87 syslog(LOG_ERR
, "%s: %m", a
);
96 return write(fd
, a
, strlen(a
));
100 run_file(filename
, uid
, gid
)
101 const char *filename
;
106 * Run a file by by spawning off a process which redirects I/O,
107 * spawns a subshell, then waits for it to complete and spawns another
108 * process to send mail to the user.
114 char *mailname
= NULL
;
119 struct passwd
*pentry
;
129 * Let's see who we mail to. Hopefully, we can read it from the
130 * command file; if not, send it to the owner, or, failing that, to
136 stream
= fopen(filename
, "r");
141 perr("Cannot open input file");
143 if ((fd_in
= dup(fileno(stream
))) < 0)
144 perr("Error duplicating input file descriptor");
146 if ((fflags
= fcntl(fd_in
, F_GETFD
)) < 0)
147 perr("Error in fcntl");
149 fcntl(fd_in
, F_SETFD
, fflags
& ~FD_CLOEXEC
);
151 if (fscanf(stream
, "#! /bin/sh\n# mail %8s %d", mailbuf
, &send_mail
) == 2) {
154 pentry
= getpwuid(uid
);
158 mailname
= pentry
->pw_name
;
161 if (chdir(_PATH_ATSPOOL
) < 0)
162 perr("Cannot chdir to " _PATH_ATSPOOL
);
165 * Create a file to hold the output of the job we are about to
166 * run. Write the mail header.
168 if ((fd_out
= open(filename
,
169 O_WRONLY
| O_CREAT
| O_EXCL
, S_IWUSR
| S_IRUSR
)) < 0)
170 perr("Cannot create output file");
172 write_string(fd_out
, "Subject: Output from your job ");
173 write_string(fd_out
, filename
);
174 write_string(fd_out
, "\n\n");
179 close(STDOUT_FILENO
);
180 close(STDERR_FILENO
);
184 perr("Error in fork");
190 * Set up things for the child; we want standard input from
191 * the input file, and standard output and error sent to
195 if (lseek(fd_in
, (off_t
) 0, SEEK_SET
) < 0)
196 perr("Error in lseek");
198 if (dup(fd_in
) != STDIN_FILENO
)
199 perr("Error in I/O redirection");
201 if (dup(fd_out
) != STDOUT_FILENO
)
202 perr("Error in I/O redirection");
204 if (dup(fd_out
) != STDERR_FILENO
)
205 perr("Error in I/O redirection");
209 if (chdir(_PATH_ATJOBS
) < 0)
210 perr("Cannot chdir to " _PATH_ATJOBS
);
220 perr("Cannot change group");
223 perr("Cannot set user id");
227 if (execle("/bin/sh", "sh", (char *) NULL
, nenvp
) != 0)
232 /* We're the parent. Let's wait. */
235 waitpid(pid
, (int *) NULL
, 0);
237 stat(filename
, &buf
);
238 if ((buf
.st_size
!= size
) || send_mail
) {
239 /* Fork off a child for sending mail */
244 if (open(filename
, O_RDONLY
) != STDIN_FILENO
)
245 perr("Cannot reopen output file");
247 execl(_PATH_SENDMAIL
, _PATH_SENDMAIL
, mailname
,
251 waitpid(pid
, (int *) NULL
, 0);
257 /* Global functions */
265 * Browse through _PATH_ATJOBS, checking all the jobfiles wether
266 * they should be executed and or deleted. The queue is coded into
267 * the first byte of the job filename, the date (in minutes since
268 * Eon) as a hex number in the following eight bytes, followed by
269 * a dot and a serial number. A file which has not been executed
270 * yet is denoted by its execute - bit set. For those files which
271 * are to be executed, run_file() is called, which forks off a
272 * child which takes care of I/O redirection, forks off another
273 * child for execution and yet another one, optionally, for sending
274 * mail. Files which already have run are removed during the
278 struct dirent
*dirent
;
285 * We don't need root privileges all the time; running under uid
286 * and gid daemon is fine.
289 RELINQUISH_PRIVS_ROOT(0) /* it's setuid root */
290 openlog("atrun", LOG_PID
, LOG_CRON
);
293 if (chdir(_PATH_ATJOBS
) != 0)
294 perr("Cannot change to " _PATH_ATJOBS
);
297 * Main loop. Open spool directory for reading and look over all
298 * the files in there. If the filename indicates that the job
299 * should be run and the x bit is set, fork off a child which sets
300 * its user and group id to that of the files and exec a /bin/sh
301 * which executes the shell script. Unlink older files if they
302 * should no longer be run. For deletion, their r bit has to be
305 if ((spool
= opendir(".")) == NULL
)
306 perr("Cannot read " _PATH_ATJOBS
);
308 while ((dirent
= readdir(spool
)) != NULL
) {
311 if (stat(dirent
->d_name
, &buf
) != 0)
312 perr("Cannot stat in " _PATH_ATJOBS
);
314 /* We don't want directories */
315 if (!S_ISREG(buf
.st_mode
))
318 if (sscanf(dirent
->d_name
, "%c%8lx", &queue
, &ctm
) != 2)
321 if ((queue
== 'b') && ((getloadavg(&la
, 1) != 1) ||
322 (la
> ATRUN_MAXLOAD
)))
325 older
= (time_t) ctm
*60 <= time(NULL
);
327 /* The file is executable and old enough */
328 if (older
&& (S_IXUSR
& buf
.st_mode
)) {
330 * Now we know we want to run the file, we can turn
331 * off the execute bit
336 if (chmod(dirent
->d_name
, S_IRUSR
) != 0)
337 perr("Cannot change file permissions");
341 run_file(dirent
->d_name
, buf
.st_uid
, buf
.st_gid
);
343 /* Delete older files */
344 if (older
&& !(S_IXUSR
& buf
.st_mode
) &&
345 (S_IRUSR
& buf
.st_mode
))
346 unlink(dirent
->d_name
);