]>
git.saurik.com Git - apple/system_cmds.git/blob - atrun.tproj/atrun.c
0634fb2ec6025cac86ddd5389160fc79f8c43f8c
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * atrun.c - run jobs queued by at; run with root privileges.
27 * Copyright (c) 1993 by Thomas Koenig
28 * All rights reserved.
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. The name of the author(s) may not be used to endorse or promote
36 * products derived from this software without specific prior written
39 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
40 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
41 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
42 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
43 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
45 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
46 * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
47 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
48 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
53 #include <sys/fcntl.h>
54 #include <sys/types.h>
75 #include "pathnames.h"
78 /* File scope variables */
81 static char rcsid
[] = "$Id: atrun.c,v 1.1.1.2 2000/01/11 02:10:06 wsanchez Exp $";
88 syslog(LOG_ERR
, "%s: %m", a
);
97 return write(fd
, a
, strlen(a
));
101 run_file(filename
, uid
, gid
)
102 const char *filename
;
107 * Run a file by by spawning off a process which redirects I/O,
108 * spawns a subshell, then waits for it to complete and spawns another
109 * process to send mail to the user.
115 char *mailname
= NULL
;
120 struct passwd
*pentry
;
130 * Let's see who we mail to. Hopefully, we can read it from the
131 * command file; if not, send it to the owner, or, failing that, to
137 stream
= fopen(filename
, "r");
142 perr("Cannot open input file");
144 if ((fd_in
= dup(fileno(stream
))) < 0)
145 perr("Error duplicating input file descriptor");
147 if ((fflags
= fcntl(fd_in
, F_GETFD
)) < 0)
148 perr("Error in fcntl");
150 fcntl(fd_in
, F_SETFD
, fflags
& ~FD_CLOEXEC
);
152 if (fscanf(stream
, "#! /bin/sh\n# mail %8s %d", mailbuf
, &send_mail
) == 2) {
155 pentry
= getpwuid(uid
);
159 mailname
= pentry
->pw_name
;
162 if (chdir(_PATH_ATSPOOL
) < 0)
163 perr("Cannot chdir to " _PATH_ATSPOOL
);
166 * Create a file to hold the output of the job we are about to
167 * run. Write the mail header.
169 if ((fd_out
= open(filename
,
170 O_WRONLY
| O_CREAT
| O_EXCL
, S_IWUSR
| S_IRUSR
)) < 0)
171 perr("Cannot create output file");
173 write_string(fd_out
, "Subject: Output from your job ");
174 write_string(fd_out
, filename
);
175 write_string(fd_out
, "\n\n");
180 close(STDOUT_FILENO
);
181 close(STDERR_FILENO
);
185 perr("Error in fork");
191 * Set up things for the child; we want standard input from
192 * the input file, and standard output and error sent to
196 if (lseek(fd_in
, (off_t
) 0, SEEK_SET
) < 0)
197 perr("Error in lseek");
199 if (dup(fd_in
) != STDIN_FILENO
)
200 perr("Error in I/O redirection");
202 if (dup(fd_out
) != STDOUT_FILENO
)
203 perr("Error in I/O redirection");
205 if (dup(fd_out
) != STDERR_FILENO
)
206 perr("Error in I/O redirection");
210 if (chdir(_PATH_ATJOBS
) < 0)
211 perr("Cannot chdir to " _PATH_ATJOBS
);
221 perr("Cannot change group");
224 perr("Cannot set user id");
228 if (execle("/bin/sh", "sh", (char *) NULL
, nenvp
) != 0)
233 /* We're the parent. Let's wait. */
236 waitpid(pid
, (int *) NULL
, 0);
238 stat(filename
, &buf
);
239 if ((buf
.st_size
!= size
) || send_mail
) {
240 /* Fork off a child for sending mail */
245 if (open(filename
, O_RDONLY
) != STDIN_FILENO
)
246 perr("Cannot reopen output file");
248 execl(_PATH_SENDMAIL
, _PATH_SENDMAIL
, mailname
,
252 waitpid(pid
, (int *) NULL
, 0);
258 /* Global functions */
266 * Browse through _PATH_ATJOBS, checking all the jobfiles wether
267 * they should be executed and or deleted. The queue is coded into
268 * the first byte of the job filename, the date (in minutes since
269 * Eon) as a hex number in the following eight bytes, followed by
270 * a dot and a serial number. A file which has not been executed
271 * yet is denoted by its execute - bit set. For those files which
272 * are to be executed, run_file() is called, which forks off a
273 * child which takes care of I/O redirection, forks off another
274 * child for execution and yet another one, optionally, for sending
275 * mail. Files which already have run are removed during the
279 struct dirent
*dirent
;
286 * We don't need root privileges all the time; running under uid
287 * and gid daemon is fine.
290 RELINQUISH_PRIVS_ROOT(0) /* it's setuid root */
291 openlog("atrun", LOG_PID
, LOG_CRON
);
294 if (chdir(_PATH_ATJOBS
) != 0)
295 perr("Cannot change to " _PATH_ATJOBS
);
298 * Main loop. Open spool directory for reading and look over all
299 * the files in there. If the filename indicates that the job
300 * should be run and the x bit is set, fork off a child which sets
301 * its user and group id to that of the files and exec a /bin/sh
302 * which executes the shell script. Unlink older files if they
303 * should no longer be run. For deletion, their r bit has to be
306 if ((spool
= opendir(".")) == NULL
)
307 perr("Cannot read " _PATH_ATJOBS
);
309 while ((dirent
= readdir(spool
)) != NULL
) {
312 if (stat(dirent
->d_name
, &buf
) != 0)
313 perr("Cannot stat in " _PATH_ATJOBS
);
315 /* We don't want directories */
316 if (!S_ISREG(buf
.st_mode
))
319 if (sscanf(dirent
->d_name
, "%c%8lx", &queue
, &ctm
) != 2)
322 if ((queue
== 'b') && ((getloadavg(&la
, 1) != 1) ||
323 (la
> ATRUN_MAXLOAD
)))
326 older
= (time_t) ctm
*60 <= time(NULL
);
328 /* The file is executable and old enough */
329 if (older
&& (S_IXUSR
& buf
.st_mode
)) {
331 * Now we know we want to run the file, we can turn
332 * off the execute bit
337 if (chmod(dirent
->d_name
, S_IRUSR
) != 0)
338 perr("Cannot change file permissions");
342 run_file(dirent
->d_name
, buf
.st_uid
, buf
.st_gid
);
344 /* Delete older files */
345 if (older
&& !(S_IXUSR
& buf
.st_mode
) &&
346 (S_IRUSR
& buf
.st_mode
))
347 unlink(dirent
->d_name
);