]>
git.saurik.com Git - apple/system_cmds.git/blob - at.tproj/at.c
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 * at.c : Put file into atrun queue
27 * Copyright (C) 1993 Thomas Koenig
29 * Atrun & Atq modifications
30 * Copyright (C) 1993 David Parsons
31 * All rights reserved.
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. The name of the author(s) may not be used to endorse or promote
39 * products derived from this software without specific prior written
42 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
43 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
44 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
45 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
46 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
47 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
48 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
49 * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
50 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
51 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
57 #include <sys/types.h>
76 #include "parsetime.h"
77 #include "pathnames.h"
83 #define ALARMC 10 /* Number of seconds to wait for timeout */
88 /* File scope variables */
89 static char rcsid
[] = "$Id: at.c,v 1.2 2003/10/15 22:48:12 lindak Exp $";
92 "TERM", "TERMCAP", "DISPLAY", "_"
96 /* External variables */
97 extern char **environ
;
100 char atfile
[FILENAME_MAX
];
102 char *atinput
= (char *) 0; /* where to get input from */
103 char atqueue
= 0; /* which queue to examine for jobs (atq) */
104 char atverify
= 0; /* verify time instead of queuing job */
106 /* Function declarations */
107 static void sigc
__P((int signo
));
108 static void alarmc
__P((int signo
));
109 static char *cwdname
__P((void));
110 static void writefile
__P((time_t runtimer
, char queue
));
111 static void list_jobs
__P((void));
113 /* Signal catching functions */
119 /* If the user presses ^C, remove the spool file and exit
134 /* Time out after some seconds
136 panic("File locking timed out");
139 /* Local functions */
144 /* Read in the current directory; the name will be overwritten on
147 static char *ptr
= NULL
;
148 static size_t size
= SIZE
;
151 ptr
= (char *) malloc(size
);
155 panic("Out of memory");
157 if (getcwd(ptr
, size
- 1) != NULL
)
161 perr("Cannot get directory");
165 ptr
= (char *) malloc(size
);
170 writefile(runtimer
, queue
)
175 * This does most of the work if at or batch are invoked for
179 char *ap
, *ppos
, *mailname
;
180 struct passwd
*pass_entry
;
182 int fdes
, lockdes
, fd2
;
184 struct sigaction act
;
191 * Install the signal handler for SIGINT; terminate after removing the
192 * spool file if necessary
194 act
.sa_handler
= sigc
;
195 sigemptyset(&(act
.sa_mask
));
198 sigaction(SIGINT
, &act
, NULL
);
200 (void)strlcpy(atfile
, _PATH_ATJOBS
, sizeof atfile
);
201 ppos
= atfile
+ strlen(_PATH_ATJOBS
);
204 * Loop over all possible file names for running something at this
205 * particular time, see if a file is there; the first empty slot at
206 * any particular time is used. Lock the file _PATH_LOCKFILE first
207 * to make sure we're alone when doing this.
212 if ((lockdes
= open(_PATH_LOCKFILE
, O_WRONLY
| O_CREAT
, 0600)) < 0)
213 perr2("Cannot open lockfile ", _PATH_LOCKFILE
);
215 lock
.l_type
= F_WRLCK
;
216 lock
.l_whence
= SEEK_SET
;
220 act
.sa_handler
= alarmc
;
221 sigemptyset(&(act
.sa_mask
));
225 * Set an alarm so a timeout occurs after ALARMC seconds, in case
226 * something is seriously broken.
228 sigaction(SIGALRM
, &act
, NULL
);
230 fcntl(lockdes
, F_SETLKW
, &lock
);
233 for (i
= 0; i
< AT_MAXJOBS
; i
++) {
234 sprintf(ppos
, "%c%8lx.%3x", queue
,
235 (unsigned long) (runtimer
/ 60), i
);
236 for (ap
= ppos
; *ap
!= '\0'; ap
++)
240 if (stat(atfile
, &statbuf
) != 0) {
244 perr2("Cannot access ", _PATH_ATJOBS
);
249 panic("Too many jobs already");
252 * Create the file. The x bit is only going to be set after it has
253 * been completely written out, to make sure it is not executed in
254 * the meantime. To make sure they do not get deleted, turn off
255 * their r bit. Yes, this is a kluge.
257 cmask
= umask(S_IRUSR
| S_IWUSR
| S_IXUSR
);
258 if ((fdes
= creat(atfile
, O_WRONLY
)) == -1)
259 perr("Cannot create atjob file");
261 if ((fd2
= dup(fdes
)) < 0)
262 perr("Error in dup() of job file");
264 if (fchown(fd2
, real_uid
, -1) != 0)
265 perr("Cannot give away file");
270 * We no longer need suid root; now we just need to be able to
271 * write to the directory, if necessary.
277 * We've successfully created the file; let's set the flag so it
278 * gets removed in case of an interrupt or error.
282 /* Now we can release the lock, so other people can access it */
283 lock
.l_type
= F_UNLCK
;
284 lock
.l_whence
= SEEK_SET
;
287 fcntl(lockdes
, F_SETLKW
, &lock
);
290 if ((fp
= fdopen(fdes
, "w")) == NULL
)
291 panic("Cannot reopen atjob file");
294 * Get the userid to mail to, first by trying getlogin(), which
295 * reads /etc/utmp, then from LOGNAME, finally from getpwuid().
297 mailname
= getlogin();
298 if (mailname
== NULL
)
299 mailname
= getenv("LOGNAME");
301 if ((mailname
== NULL
) || (mailname
[0] == '\0')
302 || (strlen(mailname
) > 8)) {
303 pass_entry
= getpwuid(getuid());
304 if (pass_entry
!= NULL
)
305 mailname
= pass_entry
->pw_name
;
308 if (atinput
!= (char *) NULL
) {
309 fpin
= freopen(atinput
, "r", stdin
);
311 perr("Cannot open input file");
313 fprintf(fp
, "#! /bin/sh\n# mail %8s %d\n", mailname
, send_mail
);
315 /* Write out the umask at the time of invocation */
316 fprintf(fp
, "umask %lo\n", (unsigned long) cmask
);
319 * Write out the environment. Anything that may look like a special
320 * character to the shell is quoted, except for \n, which is done
321 * with a pair of "'s. Dont't export the no_export list (such as
322 * TERM or DISPLAY) because we don't want these.
324 for (atenv
= environ
; *atenv
!= NULL
; atenv
++) {
328 eqp
= strchr(*atenv
, '=');
334 for (i
= 0;i
< sizeof(no_export
) /
335 sizeof(no_export
[0]); i
++) {
337 && (strncmp(*atenv
, no_export
[i
],
338 (size_t) (eqp
- *atenv
)) != 0);
344 fwrite(*atenv
, sizeof(char), eqp
- *atenv
, fp
);
345 for (ap
= eqp
; *ap
!= '\0'; ap
++) {
347 fprintf(fp
, "\"\n\"");
355 fputs("; export ", fp
);
356 fwrite(*atenv
, sizeof(char), eqp
- *atenv
- 1, fp
);
362 * Cd to the directory at the time and write out all the commands
363 * the user supplies from stdin.
365 fprintf(fp
, "cd %s\n", cwdname());
367 while ((ch
= getchar()) != EOF
)
372 panic("Output error");
375 panic("Input error");
380 * Set the x bit so that we're ready to start executing
382 if (fchmod(fd2
, S_IRUSR
| S_IWUSR
| S_IXUSR
) < 0)
383 perr("Cannot give away file");
386 fprintf(stderr
, "Job %s will be executed using /bin/sh\n", ppos
);
393 * List all a user's jobs in the queue, by looping through
394 * _PATH_ATJOBS, or everybody's if we are root
398 struct dirent
*dirent
;
404 char timestr
[TIMESIZE
];
409 if (chdir(_PATH_ATJOBS
) != 0)
410 perr2("Cannot change to ", _PATH_ATJOBS
);
412 if ((spool
= opendir(".")) == NULL
)
413 perr2("Cannot open ", _PATH_ATJOBS
);
415 /* Loop over every file in the directory */
416 while ((dirent
= readdir(spool
)) != NULL
) {
417 if (stat(dirent
->d_name
, &buf
) != 0)
418 perr2("Cannot stat in ", _PATH_ATJOBS
);
421 * See it's a regular file and has its x bit turned on and
424 if (!S_ISREG(buf
.st_mode
)
425 || ((buf
.st_uid
!= real_uid
) && !(real_uid
== 0))
426 || !(S_IXUSR
& buf
.st_mode
|| atverify
))
429 if (sscanf(dirent
->d_name
, "%c%8lx", &queue
, &ctm
) != 2)
432 if (atqueue
&& (queue
!= atqueue
))
435 runtimer
= 60 * (time_t) ctm
;
436 runtime
= *localtime(&runtimer
);
437 strftime(timestr
, TIMESIZE
, "%X %x", &runtime
);
439 printf("Date\t\t\tOwner\tQueue\tJob#\n");
442 pw
= getpwuid(buf
.st_uid
);
444 printf("%s\t%s\t%c%s\t%s\n",
446 pw
? pw
->pw_name
: "???",
448 (S_IXUSR
& buf
.st_mode
) ? "" : "(done)",
455 delete_jobs(argc
, argv
)
459 /* Delete every argument (job - ID) given */
465 if (chdir(_PATH_ATJOBS
) != 0)
466 perr2("Cannot change to ", _PATH_ATJOBS
);
468 for (i
= optind
; i
< argc
; i
++) {
469 if (stat(argv
[i
], &buf
) != 0)
471 if ((buf
.st_uid
!= real_uid
) && !(real_uid
== 0)) {
472 fprintf(stderr
, "%s: Not owner\n", argv
[i
]);
475 if (unlink(argv
[i
]) != 0)
481 /* Global functions */
494 }; /* what program we want to run */
495 int program
= AT
; /* our default program */
496 char *options
= "q:f:mv"; /* default options for at */
501 /* Eat any leading paths */
502 if ((pgm
= strrchr(argv
[0], '/')) == NULL
)
509 /* find out what this program is supposed to do */
510 if (strcmp(pgm
, "atq") == 0) {
513 } else if (strcmp(pgm
, "atrm") == 0) {
516 } else if (strcmp(pgm
, "batch") == 0) {
521 /* process whatever options we can process */
523 while ((c
= getopt(argc
, argv
, options
)) != EOF
)
525 case 'v': /* verify time settings */
529 case 'm': /* send mail when job is complete */
537 case 'q': /* specify queue */
538 if (strlen(optarg
) > 1)
541 atqueue
= queue
= *optarg
;
542 if ((!islower(queue
)) || (queue
> 'l'))
550 /* end of options eating */
552 /* select our program */
553 if (!check_permission())
555 fprintf(stderr
, "You do not have permission to use %s.\n",namep
);
570 delete_jobs(argc
, argv
);
574 timer
= parsetime(argc
, argv
);
576 struct tm
*tm
= localtime(&timer
);
578 fprintf(stderr
, "%s\n", asctime(tm
));
580 writefile(timer
, queue
);
584 writefile(time(NULL
), 'b');
588 panic("Internal error");