1d78e98d2SNate Williams /* 2d78e98d2SNate Williams * at.c : Put file into atrun queue 3b89321a5SAndrey A. Chernov * Copyright (C) 1993, 1994 Thomas Koenig 4d78e98d2SNate Williams * 5d78e98d2SNate Williams * Atrun & Atq modifications 6d78e98d2SNate Williams * Copyright (C) 1993 David Parsons 7d78e98d2SNate Williams * 8d78e98d2SNate Williams * Redistribution and use in source and binary forms, with or without 9d78e98d2SNate Williams * modification, are permitted provided that the following conditions 10d78e98d2SNate Williams * are met: 11d78e98d2SNate Williams * 1. Redistributions of source code must retain the above copyright 12d78e98d2SNate Williams * notice, this list of conditions and the following disclaimer. 13d78e98d2SNate Williams * 2. The name of the author(s) may not be used to endorse or promote 14d78e98d2SNate Williams * products derived from this software without specific prior written 15d78e98d2SNate Williams * permission. 16d78e98d2SNate Williams * 17d78e98d2SNate Williams * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 18d78e98d2SNate Williams * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19d78e98d2SNate Williams * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20d78e98d2SNate Williams * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21d78e98d2SNate Williams * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22d78e98d2SNate Williams * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23d78e98d2SNate Williams * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24d78e98d2SNate Williams * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25d78e98d2SNate Williams * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26d78e98d2SNate Williams * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27d78e98d2SNate Williams */ 28d78e98d2SNate Williams 29d78e98d2SNate Williams #define _USE_BSD 1 30d78e98d2SNate Williams 31d78e98d2SNate Williams /* System Headers */ 32b89321a5SAndrey A. Chernov 33d78e98d2SNate Williams #include <sys/types.h> 34d78e98d2SNate Williams #include <sys/stat.h> 35d78e98d2SNate Williams #include <sys/wait.h> 36d78e98d2SNate Williams #include <ctype.h> 37d78e98d2SNate Williams #include <dirent.h> 38d78e98d2SNate Williams #include <errno.h> 39d78e98d2SNate Williams #include <fcntl.h> 40d78e98d2SNate Williams #include <pwd.h> 41d78e98d2SNate Williams #include <signal.h> 42d78e98d2SNate Williams #include <stddef.h> 43d78e98d2SNate Williams #include <stdio.h> 44d78e98d2SNate Williams #include <stdlib.h> 45d78e98d2SNate Williams #include <string.h> 46d78e98d2SNate Williams #include <time.h> 47d78e98d2SNate Williams #include <unistd.h> 48b89321a5SAndrey A. Chernov #ifndef __FreeBSD__ 49b89321a5SAndrey A. Chernov #include <getopt.h> 50b89321a5SAndrey A. Chernov #endif 51d78e98d2SNate Williams 52d78e98d2SNate Williams /* Local headers */ 53b89321a5SAndrey A. Chernov 54d78e98d2SNate Williams #include "at.h" 55d78e98d2SNate Williams #include "panic.h" 56d78e98d2SNate Williams #include "parsetime.h" 57b89321a5SAndrey A. Chernov #include "perm.h" 58b89321a5SAndrey A. Chernov 59d78e98d2SNate Williams #define MAIN 60d78e98d2SNate Williams #include "privs.h" 61d78e98d2SNate Williams 62d78e98d2SNate Williams /* Macros */ 63b89321a5SAndrey A. Chernov 64b89321a5SAndrey A. Chernov #ifndef ATJOB_DIR 65b89321a5SAndrey A. Chernov #define ATJOB_DIR "/usr/spool/atjobs/" 66b89321a5SAndrey A. Chernov #endif 67b89321a5SAndrey A. Chernov 68b89321a5SAndrey A. Chernov #ifndef LFILE 69b89321a5SAndrey A. Chernov #define LFILE ATJOB_DIR ".lockfile" 70b89321a5SAndrey A. Chernov #endif 71b89321a5SAndrey A. Chernov 72b89321a5SAndrey A. Chernov #ifndef ATJOB_MX 73b89321a5SAndrey A. Chernov #define ATJOB_MX 255 74b89321a5SAndrey A. Chernov #endif 75b89321a5SAndrey A. Chernov 76d78e98d2SNate Williams #define ALARMC 10 /* Number of seconds to wait for timeout */ 77d78e98d2SNate Williams 78d78e98d2SNate Williams #define SIZE 255 79d78e98d2SNate Williams #define TIMESIZE 50 80d78e98d2SNate Williams 81d78e98d2SNate Williams /* File scope variables */ 82b89321a5SAndrey A. Chernov 83b89321a5SAndrey A. Chernov static char rcsid[] = "$Id: at.c,v 1.2 1994/06/08 18:19:43 kernel Exp $"; 84d78e98d2SNate Williams char *no_export[] = 85d78e98d2SNate Williams { 86d78e98d2SNate Williams "TERM", "TERMCAP", "DISPLAY", "_" 87d78e98d2SNate Williams } ; 88d78e98d2SNate Williams static send_mail = 0; 89d78e98d2SNate Williams 90d78e98d2SNate Williams /* External variables */ 91b89321a5SAndrey A. Chernov 92d78e98d2SNate Williams extern char **environ; 93d78e98d2SNate Williams int fcreated; 94d78e98d2SNate Williams char *namep; 95b89321a5SAndrey A. Chernov char atfile[] = ATJOB_DIR "12345678901234"; 96d78e98d2SNate Williams 97d78e98d2SNate Williams char *atinput = (char*)0; /* where to get input from */ 98d78e98d2SNate Williams char atqueue = 0; /* which queue to examine for jobs (atq) */ 99d78e98d2SNate Williams char atverify = 0; /* verify time instead of queuing job */ 100d78e98d2SNate Williams 101d78e98d2SNate Williams /* Function declarations */ 102b89321a5SAndrey A. Chernov 103b89321a5SAndrey A. Chernov static void sigc(int signo); 104b89321a5SAndrey A. Chernov static void alarmc(int signo); 105b89321a5SAndrey A. Chernov static char *cwdname(void); 106b89321a5SAndrey A. Chernov static void writefile(time_t runtimer, char queue); 107b89321a5SAndrey A. Chernov static void list_jobs(void); 108d78e98d2SNate Williams 109d78e98d2SNate Williams /* Signal catching functions */ 110d78e98d2SNate Williams 111b89321a5SAndrey A. Chernov static void sigc(int signo) 112d78e98d2SNate Williams { 113d78e98d2SNate Williams /* If the user presses ^C, remove the spool file and exit 114d78e98d2SNate Williams */ 115b89321a5SAndrey A. Chernov if (fcreated) 116b89321a5SAndrey A. Chernov { 117d78e98d2SNate Williams PRIV_START 118d78e98d2SNate Williams unlink(atfile); 119d78e98d2SNate Williams PRIV_END 120d78e98d2SNate Williams } 121d78e98d2SNate Williams 122d78e98d2SNate Williams exit(EXIT_FAILURE); 123d78e98d2SNate Williams } 124d78e98d2SNate Williams 125b89321a5SAndrey A. Chernov static void alarmc(int signo) 126d78e98d2SNate Williams { 127d78e98d2SNate Williams /* Time out after some seconds 128d78e98d2SNate Williams */ 129d78e98d2SNate Williams panic("File locking timed out"); 130d78e98d2SNate Williams } 131d78e98d2SNate Williams 132d78e98d2SNate Williams /* Local functions */ 133d78e98d2SNate Williams 134b89321a5SAndrey A. Chernov static char *cwdname(void) 135d78e98d2SNate Williams { 136d78e98d2SNate Williams /* Read in the current directory; the name will be overwritten on 137d78e98d2SNate Williams * subsequent calls. 138d78e98d2SNate Williams */ 139d78e98d2SNate Williams static char *ptr = NULL; 140d78e98d2SNate Williams static size_t size = SIZE; 141d78e98d2SNate Williams 142d78e98d2SNate Williams if (ptr == NULL) 143b89321a5SAndrey A. Chernov ptr = (char *) mymalloc(size); 144d78e98d2SNate Williams 145b89321a5SAndrey A. Chernov while (1) 146b89321a5SAndrey A. Chernov { 147d78e98d2SNate Williams if (ptr == NULL) 148d78e98d2SNate Williams panic("Out of memory"); 149d78e98d2SNate Williams 150d78e98d2SNate Williams if (getcwd(ptr, size-1) != NULL) 151d78e98d2SNate Williams return ptr; 152d78e98d2SNate Williams 153d78e98d2SNate Williams if (errno != ERANGE) 154d78e98d2SNate Williams perr("Cannot get directory"); 155d78e98d2SNate Williams 156d78e98d2SNate Williams free (ptr); 157d78e98d2SNate Williams size += SIZE; 158b89321a5SAndrey A. Chernov ptr = (char *) mymalloc(size); 159d78e98d2SNate Williams } 160d78e98d2SNate Williams } 161d78e98d2SNate Williams 162d78e98d2SNate Williams static void 163b89321a5SAndrey A. Chernov writefile(time_t runtimer, char queue) 164d78e98d2SNate Williams { 165b89321a5SAndrey A. Chernov /* This does most of the work if at or batch are invoked for writing a job. 166d78e98d2SNate Williams */ 167d78e98d2SNate Williams int i; 168d78e98d2SNate Williams char *ap, *ppos, *mailname; 169d78e98d2SNate Williams struct passwd *pass_entry; 170d78e98d2SNate Williams struct stat statbuf; 171d78e98d2SNate Williams int fdes, lockdes, fd2; 172d78e98d2SNate Williams FILE *fp, *fpin; 173d78e98d2SNate Williams struct sigaction act; 174d78e98d2SNate Williams char **atenv; 175d78e98d2SNate Williams int ch; 176d78e98d2SNate Williams mode_t cmask; 177d78e98d2SNate Williams struct flock lock; 178d78e98d2SNate Williams 179b89321a5SAndrey A. Chernov /* Install the signal handler for SIGINT; terminate after removing the 180d78e98d2SNate Williams * spool file if necessary 181d78e98d2SNate Williams */ 182d78e98d2SNate Williams act.sa_handler = sigc; 183d78e98d2SNate Williams sigemptyset(&(act.sa_mask)); 184d78e98d2SNate Williams act.sa_flags = 0; 185d78e98d2SNate Williams 186d78e98d2SNate Williams sigaction(SIGINT, &act, NULL); 187d78e98d2SNate Williams 188b89321a5SAndrey A. Chernov ppos = atfile + strlen(ATJOB_DIR); 189d78e98d2SNate Williams 190b89321a5SAndrey A. Chernov /* Loop over all possible file names for running something at this 191b89321a5SAndrey A. Chernov * particular time, see if a file is there; the first empty slot at any 192b89321a5SAndrey A. Chernov * particular time is used. Lock the file LFILE first to make sure 193b89321a5SAndrey A. Chernov * we're alone when doing this. 194d78e98d2SNate Williams */ 195d78e98d2SNate Williams 196d78e98d2SNate Williams PRIV_START 197d78e98d2SNate Williams 198b89321a5SAndrey A. Chernov if ((lockdes = open(LFILE, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR)) < 0) 199b89321a5SAndrey A. Chernov perr("Cannot open lockfile " LFILE); 200d78e98d2SNate Williams 201b89321a5SAndrey A. Chernov lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; 202d78e98d2SNate Williams lock.l_len = 0; 203d78e98d2SNate Williams 204d78e98d2SNate Williams act.sa_handler = alarmc; 205d78e98d2SNate Williams sigemptyset(&(act.sa_mask)); 206d78e98d2SNate Williams act.sa_flags = 0; 207d78e98d2SNate Williams 208b89321a5SAndrey A. Chernov /* Set an alarm so a timeout occurs after ALARMC seconds, in case 209d78e98d2SNate Williams * something is seriously broken. 210d78e98d2SNate Williams */ 211d78e98d2SNate Williams sigaction(SIGALRM, &act, NULL); 212d78e98d2SNate Williams alarm(ALARMC); 213d78e98d2SNate Williams fcntl(lockdes, F_SETLKW, &lock); 214d78e98d2SNate Williams alarm(0); 215d78e98d2SNate Williams 216b89321a5SAndrey A. Chernov for(i=0; i<ATJOB_MX; i++) 217b89321a5SAndrey A. Chernov { 218b89321a5SAndrey A. Chernov sprintf(ppos, "%c%8lx.%2x", queue, 219d78e98d2SNate Williams (unsigned long) (runtimer/60), i); 220d78e98d2SNate Williams for(ap=ppos; *ap != '\0'; ap ++) 221d78e98d2SNate Williams if (*ap == ' ') 222d78e98d2SNate Williams *ap = '0'; 223d78e98d2SNate Williams 224b89321a5SAndrey A. Chernov if (stat(atfile, &statbuf) != 0) 225b89321a5SAndrey A. Chernov { 226d78e98d2SNate Williams if (errno == ENOENT) 227d78e98d2SNate Williams break; 228d78e98d2SNate Williams else 229b89321a5SAndrey A. Chernov perr("Cannot access " ATJOB_DIR); 230d78e98d2SNate Williams } 231d78e98d2SNate Williams } /* for */ 232d78e98d2SNate Williams 233b89321a5SAndrey A. Chernov if (i >= ATJOB_MX) 234d78e98d2SNate Williams panic("Too many jobs already"); 235d78e98d2SNate Williams 236b89321a5SAndrey A. Chernov /* Create the file. The x bit is only going to be set after it has 237b89321a5SAndrey A. Chernov * been completely written out, to make sure it is not executed in the 238b89321a5SAndrey A. Chernov * meantime. To make sure they do not get deleted, turn off their r 239b89321a5SAndrey A. Chernov * bit. Yes, this is a kluge. 240d78e98d2SNate Williams */ 241d78e98d2SNate Williams cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR); 242d78e98d2SNate Williams if ((fdes = creat(atfile, O_WRONLY)) == -1) 243d78e98d2SNate Williams perr("Cannot create atjob file"); 244d78e98d2SNate Williams 245d78e98d2SNate Williams if ((fd2 = dup(fdes)) <0) 246d78e98d2SNate Williams perr("Error in dup() of job file"); 247d78e98d2SNate Williams 248b89321a5SAndrey A. Chernov if(fchown(fd2, real_uid, real_gid) != 0) 249d78e98d2SNate Williams perr("Cannot give away file"); 250d78e98d2SNate Williams 251d78e98d2SNate Williams PRIV_END 252d78e98d2SNate Williams 253b89321a5SAndrey A. Chernov /* We no longer need suid root; now we just need to be able to write 254b89321a5SAndrey A. Chernov * to the directory, if necessary. 255d78e98d2SNate Williams */ 256d78e98d2SNate Williams 257b89321a5SAndrey A. Chernov REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 258d78e98d2SNate Williams 259b89321a5SAndrey A. Chernov /* We've successfully created the file; let's set the flag so it 260d78e98d2SNate Williams * gets removed in case of an interrupt or error. 261d78e98d2SNate Williams */ 262d78e98d2SNate Williams fcreated = 1; 263d78e98d2SNate Williams 264b89321a5SAndrey A. Chernov /* Now we can release the lock, so other people can access it 265b89321a5SAndrey A. Chernov */ 266b89321a5SAndrey A. Chernov lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; 267d78e98d2SNate Williams lock.l_len = 0; 268d78e98d2SNate Williams fcntl(lockdes, F_SETLKW, &lock); 269d78e98d2SNate Williams close(lockdes); 270d78e98d2SNate Williams 271d78e98d2SNate Williams if((fp = fdopen(fdes, "w")) == NULL) 272d78e98d2SNate Williams panic("Cannot reopen atjob file"); 273d78e98d2SNate Williams 274b89321a5SAndrey A. Chernov /* Get the userid to mail to, first by trying getlogin(), which reads 275b89321a5SAndrey A. Chernov * /etc/utmp, then from LOGNAME, finally from getpwuid(). 276d78e98d2SNate Williams */ 277d78e98d2SNate Williams mailname = getlogin(); 278d78e98d2SNate Williams if (mailname == NULL) 279d78e98d2SNate Williams mailname = getenv("LOGNAME"); 280d78e98d2SNate Williams 281d78e98d2SNate Williams if ((mailname == NULL) || (mailname[0] == '\0') 282b89321a5SAndrey A. Chernov || (strlen(mailname) > 8) || (getpwnam(mailname)==NULL)) 283b89321a5SAndrey A. Chernov { 284d78e98d2SNate Williams pass_entry = getpwuid(getuid()); 285d78e98d2SNate Williams if (pass_entry != NULL) 286d78e98d2SNate Williams mailname = pass_entry->pw_name; 287d78e98d2SNate Williams } 288d78e98d2SNate Williams 289b89321a5SAndrey A. Chernov if (atinput != (char *) NULL) 290b89321a5SAndrey A. Chernov { 291d78e98d2SNate Williams fpin = freopen(atinput, "r", stdin); 292d78e98d2SNate Williams if (fpin == NULL) 293d78e98d2SNate Williams perr("Cannot open input file"); 294d78e98d2SNate Williams } 295d78e98d2SNate Williams fprintf(fp, "#! /bin/sh\n# mail %8s %d\n", mailname, send_mail); 296d78e98d2SNate Williams 297b89321a5SAndrey A. Chernov /* Write out the umask at the time of invocation 298b89321a5SAndrey A. Chernov */ 299d78e98d2SNate Williams fprintf(fp, "umask %lo\n", (unsigned long) cmask); 300d78e98d2SNate Williams 301b89321a5SAndrey A. Chernov /* Write out the environment. Anything that may look like a 302b89321a5SAndrey A. Chernov * special character to the shell is quoted, except for \n, which is 303b89321a5SAndrey A. Chernov * done with a pair of "'s. Dont't export the no_export list (such 304b89321a5SAndrey A. Chernov * as TERM or DISPLAY) because we don't want these. 305d78e98d2SNate Williams */ 306b89321a5SAndrey A. Chernov for (atenv= environ; *atenv != NULL; atenv++) 307b89321a5SAndrey A. Chernov { 308d78e98d2SNate Williams int export = 1; 309d78e98d2SNate Williams char *eqp; 310d78e98d2SNate Williams 311d78e98d2SNate Williams eqp = strchr(*atenv, '='); 312d78e98d2SNate Williams if (ap == NULL) 313d78e98d2SNate Williams eqp = *atenv; 314b89321a5SAndrey A. Chernov else 315b89321a5SAndrey A. Chernov { 316d78e98d2SNate Williams int i; 317b89321a5SAndrey A. Chernov for (i=0; i<sizeof(no_export)/sizeof(no_export[0]); i++) 318b89321a5SAndrey A. Chernov { 319d78e98d2SNate Williams export = export 320d78e98d2SNate Williams && (strncmp(*atenv, no_export[i], 321d78e98d2SNate Williams (size_t) (eqp-*atenv)) != 0); 322d78e98d2SNate Williams } 323d78e98d2SNate Williams eqp++; 324d78e98d2SNate Williams } 325d78e98d2SNate Williams 326b89321a5SAndrey A. Chernov if (export) 327b89321a5SAndrey A. Chernov { 328d78e98d2SNate Williams fwrite(*atenv, sizeof(char), eqp-*atenv, fp); 329b89321a5SAndrey A. Chernov for(ap = eqp;*ap != '\0'; ap++) 330b89321a5SAndrey A. Chernov { 331d78e98d2SNate Williams if (*ap == '\n') 332d78e98d2SNate Williams fprintf(fp, "\"\n\""); 333b89321a5SAndrey A. Chernov else 334b89321a5SAndrey A. Chernov { 335b89321a5SAndrey A. Chernov if (*ap != '/' && !isalnum(*ap)) 336d78e98d2SNate Williams fputc('\\', fp); 337d78e98d2SNate Williams 338d78e98d2SNate Williams fputc(*ap, fp); 339d78e98d2SNate Williams } 340d78e98d2SNate Williams } 341d78e98d2SNate Williams fputs("; export ", fp); 342d78e98d2SNate Williams fwrite(*atenv, sizeof(char), eqp-*atenv -1, fp); 343d78e98d2SNate Williams fputc('\n', fp); 344d78e98d2SNate Williams 345d78e98d2SNate Williams } 346d78e98d2SNate Williams } 347b89321a5SAndrey A. Chernov /* Cd to the directory at the time and write out all the 348b89321a5SAndrey A. Chernov * commands the user supplies from stdin. 349d78e98d2SNate Williams */ 350b89321a5SAndrey A. Chernov fprintf(fp, "cd "); 351b89321a5SAndrey A. Chernov for (ap = cwdname(); *ap != '\0'; ap++) 352b89321a5SAndrey A. Chernov { 353b89321a5SAndrey A. Chernov if (*ap == '\n') 354b89321a5SAndrey A. Chernov fprintf(fp, "\"\n\""); 355b89321a5SAndrey A. Chernov else 356b89321a5SAndrey A. Chernov { 357b89321a5SAndrey A. Chernov if (*ap != '/' && !isalnum(*ap)) 358b89321a5SAndrey A. Chernov fputc('\\', fp); 359b89321a5SAndrey A. Chernov 360b89321a5SAndrey A. Chernov fputc(*ap, fp); 361b89321a5SAndrey A. Chernov } 362b89321a5SAndrey A. Chernov } 363b89321a5SAndrey A. Chernov /* Test cd's exit status: die if the original directory has been 364b89321a5SAndrey A. Chernov * removed, become unreadable or whatever 365b89321a5SAndrey A. Chernov */ 366b89321a5SAndrey A. Chernov fprintf(fp, " || {\n\t echo 'Execution directory " 367b89321a5SAndrey A. Chernov "inaccessible' >&2\n\t exit 1\n}\n"); 368d78e98d2SNate Williams 369d78e98d2SNate Williams while((ch = getchar()) != EOF) 370d78e98d2SNate Williams fputc(ch, fp); 371d78e98d2SNate Williams 372d78e98d2SNate Williams fprintf(fp, "\n"); 373d78e98d2SNate Williams if (ferror(fp)) 374d78e98d2SNate Williams panic("Output error"); 375d78e98d2SNate Williams 376d78e98d2SNate Williams if (ferror(stdin)) 377d78e98d2SNate Williams panic("Input error"); 378d78e98d2SNate Williams 379d78e98d2SNate Williams fclose(fp); 380d78e98d2SNate Williams 381b89321a5SAndrey A. Chernov /* Set the x bit so that we're ready to start executing 382d78e98d2SNate Williams */ 383b89321a5SAndrey A. Chernov 384d78e98d2SNate Williams if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0) 385d78e98d2SNate Williams perr("Cannot give away file"); 386d78e98d2SNate Williams 387d78e98d2SNate Williams close(fd2); 388d78e98d2SNate Williams fprintf(stderr, "Job %s will be executed using /bin/sh\n", ppos); 389d78e98d2SNate Williams } 390d78e98d2SNate Williams 391d78e98d2SNate Williams static void 392d78e98d2SNate Williams list_jobs() 393d78e98d2SNate Williams { 394b89321a5SAndrey A. Chernov /* List all a user's jobs in the queue, by looping through ATJOB_DIR, 395b89321a5SAndrey A. Chernov * or everybody's if we are root 396d78e98d2SNate Williams */ 397d78e98d2SNate Williams struct passwd *pw; 398d78e98d2SNate Williams DIR *spool; 399d78e98d2SNate Williams struct dirent *dirent; 400d78e98d2SNate Williams struct stat buf; 401d78e98d2SNate Williams struct tm runtime; 402d78e98d2SNate Williams unsigned long ctm; 403d78e98d2SNate Williams char queue; 404d78e98d2SNate Williams time_t runtimer; 405d78e98d2SNate Williams char timestr[TIMESIZE]; 406d78e98d2SNate Williams int first=1; 407d78e98d2SNate Williams 408d78e98d2SNate Williams PRIV_START 409d78e98d2SNate Williams 410b89321a5SAndrey A. Chernov if (chdir(ATJOB_DIR) != 0) 411b89321a5SAndrey A. Chernov perr("Cannot change to " ATJOB_DIR); 412d78e98d2SNate Williams 413d78e98d2SNate Williams if ((spool = opendir(".")) == NULL) 414b89321a5SAndrey A. Chernov perr("Cannot open " ATJOB_DIR); 415d78e98d2SNate Williams 416b89321a5SAndrey A. Chernov /* Loop over every file in the directory 417b89321a5SAndrey A. Chernov */ 418d78e98d2SNate Williams while((dirent = readdir(spool)) != NULL) { 419d78e98d2SNate Williams if (stat(dirent->d_name, &buf) != 0) 420b89321a5SAndrey A. Chernov perr("Cannot stat in " ATJOB_DIR); 421d78e98d2SNate Williams 422b89321a5SAndrey A. Chernov /* See it's a regular file and has its x bit turned on and 423d78e98d2SNate Williams * is the user's 424d78e98d2SNate Williams */ 425d78e98d2SNate Williams if (!S_ISREG(buf.st_mode) 426d78e98d2SNate Williams || ((buf.st_uid != real_uid) && ! (real_uid == 0)) 427d78e98d2SNate Williams || !(S_IXUSR & buf.st_mode || atverify)) 428d78e98d2SNate Williams continue; 429d78e98d2SNate Williams 430d78e98d2SNate Williams if(sscanf(dirent->d_name, "%c%8lx", &queue, &ctm)!=2) 431d78e98d2SNate Williams continue; 432d78e98d2SNate Williams 433d78e98d2SNate Williams if (atqueue && (queue != atqueue)) 434d78e98d2SNate Williams continue; 435d78e98d2SNate Williams 436d78e98d2SNate Williams runtimer = 60*(time_t) ctm; 437d78e98d2SNate Williams runtime = *localtime(&runtimer); 438d78e98d2SNate Williams strftime(timestr, TIMESIZE, "%X %x", &runtime); 439d78e98d2SNate Williams if (first) { 440d78e98d2SNate Williams printf("Date\t\t\tOwner\tQueue\tJob#\n"); 441d78e98d2SNate Williams first=0; 442d78e98d2SNate Williams } 443d78e98d2SNate Williams pw = getpwuid(buf.st_uid); 444d78e98d2SNate Williams 445d78e98d2SNate Williams printf("%s\t%s\t%c%s\t%s\n", 446d78e98d2SNate Williams timestr, 447d78e98d2SNate Williams pw ? pw->pw_name : "???", 448d78e98d2SNate Williams queue, 449d78e98d2SNate Williams (S_IXUSR & buf.st_mode) ? "":"(done)", 450d78e98d2SNate Williams dirent->d_name); 451d78e98d2SNate Williams } 452d78e98d2SNate Williams PRIV_END 453d78e98d2SNate Williams } 454d78e98d2SNate Williams 455d78e98d2SNate Williams static void 456b89321a5SAndrey A. Chernov delete_jobs(int argc, char **argv) 457d78e98d2SNate Williams { 458b89321a5SAndrey A. Chernov /* Delete every argument (job - ID) given 459b89321a5SAndrey A. Chernov */ 460d78e98d2SNate Williams int i; 461d78e98d2SNate Williams struct stat buf; 462d78e98d2SNate Williams 463d78e98d2SNate Williams PRIV_START 464d78e98d2SNate Williams 465b89321a5SAndrey A. Chernov if (chdir(ATJOB_DIR) != 0) 466b89321a5SAndrey A. Chernov perr("Cannot change to " ATJOB_DIR); 467d78e98d2SNate Williams 468d78e98d2SNate Williams for (i=optind; i < argc; i++) { 469d78e98d2SNate Williams if (stat(argv[i], &buf) != 0) 470d78e98d2SNate Williams perr(argv[i]); 471d78e98d2SNate Williams if ((buf.st_uid != real_uid) && !(real_uid == 0)) { 472d78e98d2SNate Williams fprintf(stderr, "%s: Not owner\n", argv[i]); 473d78e98d2SNate Williams exit(EXIT_FAILURE); 474d78e98d2SNate Williams } 475d78e98d2SNate Williams if (unlink(argv[i]) != 0) 476d78e98d2SNate Williams perr(argv[i]); 477d78e98d2SNate Williams } 478d78e98d2SNate Williams PRIV_END 479d78e98d2SNate Williams } /* delete_jobs */ 480d78e98d2SNate Williams 481d78e98d2SNate Williams /* Global functions */ 482d78e98d2SNate Williams 483b89321a5SAndrey A. Chernov void * 484b89321a5SAndrey A. Chernov mymalloc(size_t n) 485b89321a5SAndrey A. Chernov { 486b89321a5SAndrey A. Chernov void *p; 487b89321a5SAndrey A. Chernov if ((p=malloc(n))==(void *)0) 488b89321a5SAndrey A. Chernov { 489b89321a5SAndrey A. Chernov fprintf(stderr,"Virtual memory exhausted\n"); 490b89321a5SAndrey A. Chernov exit(EXIT_FAILURE); 491b89321a5SAndrey A. Chernov } 492b89321a5SAndrey A. Chernov return p; 493b89321a5SAndrey A. Chernov } 494b89321a5SAndrey A. Chernov 495d78e98d2SNate Williams int 496b89321a5SAndrey A. Chernov main(int argc, char **argv) 497d78e98d2SNate Williams { 498d78e98d2SNate Williams int c; 499b89321a5SAndrey A. Chernov char queue = DEFAULT_AT_QUEUE; 500b89321a5SAndrey A. Chernov char queue_set = 0; 501d78e98d2SNate Williams char *pgm; 502d78e98d2SNate Williams 503b89321a5SAndrey A. Chernov enum { ATQ, ATRM, AT, BATCH }; /* what program we want to run */ 504d78e98d2SNate Williams int program = AT; /* our default program */ 505b89321a5SAndrey A. Chernov char *options = "q:f:mvldbV"; /* default options for at */ 506b89321a5SAndrey A. Chernov int disp_version = 0; 507d78e98d2SNate Williams time_t timer; 508d78e98d2SNate Williams 509d78e98d2SNate Williams RELINQUISH_PRIVS 510d78e98d2SNate Williams 511b89321a5SAndrey A. Chernov /* Eat any leading paths 512b89321a5SAndrey A. Chernov */ 513d78e98d2SNate Williams if ((pgm = strrchr(argv[0], '/')) == NULL) 514d78e98d2SNate Williams pgm = argv[0]; 515d78e98d2SNate Williams else 516d78e98d2SNate Williams pgm++; 517d78e98d2SNate Williams 518d78e98d2SNate Williams namep = pgm; 519d78e98d2SNate Williams 520b89321a5SAndrey A. Chernov /* find out what this program is supposed to do 521b89321a5SAndrey A. Chernov */ 522d78e98d2SNate Williams if (strcmp(pgm, "atq") == 0) { 523d78e98d2SNate Williams program = ATQ; 524b89321a5SAndrey A. Chernov options = "q:vV"; 525b89321a5SAndrey A. Chernov } 526b89321a5SAndrey A. Chernov else if (strcmp(pgm, "atrm") == 0) { 527d78e98d2SNate Williams program = ATRM; 528b89321a5SAndrey A. Chernov options = "V"; 529b89321a5SAndrey A. Chernov } 530b89321a5SAndrey A. Chernov else if (strcmp(pgm, "batch") == 0) { 531d78e98d2SNate Williams program = BATCH; 532b89321a5SAndrey A. Chernov options = "f:q:mvV"; 533d78e98d2SNate Williams } 534d78e98d2SNate Williams 535b89321a5SAndrey A. Chernov /* process whatever options we can process 536b89321a5SAndrey A. Chernov */ 537d78e98d2SNate Williams opterr=1; 538d78e98d2SNate Williams while ((c=getopt(argc, argv, options)) != EOF) 539d78e98d2SNate Williams switch (c) { 540d78e98d2SNate Williams case 'v': /* verify time settings */ 541d78e98d2SNate Williams atverify = 1; 542d78e98d2SNate Williams break; 543d78e98d2SNate Williams 544d78e98d2SNate Williams case 'm': /* send mail when job is complete */ 545d78e98d2SNate Williams send_mail = 1; 546d78e98d2SNate Williams break; 547d78e98d2SNate Williams 548d78e98d2SNate Williams case 'f': 549d78e98d2SNate Williams atinput = optarg; 550d78e98d2SNate Williams break; 551d78e98d2SNate Williams 552d78e98d2SNate Williams case 'q': /* specify queue */ 553d78e98d2SNate Williams if (strlen(optarg) > 1) 554d78e98d2SNate Williams usage(); 555d78e98d2SNate Williams 556d78e98d2SNate Williams atqueue = queue = *optarg; 557b89321a5SAndrey A. Chernov if (!(islower(queue)||isupper(queue))) 558d78e98d2SNate Williams usage(); 559b89321a5SAndrey A. Chernov 560b89321a5SAndrey A. Chernov queue_set = 1; 561b89321a5SAndrey A. Chernov break; 562b89321a5SAndrey A. Chernov 563b89321a5SAndrey A. Chernov case 'd': 564b89321a5SAndrey A. Chernov if (program != AT) 565b89321a5SAndrey A. Chernov usage(); 566b89321a5SAndrey A. Chernov 567b89321a5SAndrey A. Chernov program = ATRM; 568b89321a5SAndrey A. Chernov options = "V"; 569b89321a5SAndrey A. Chernov break; 570b89321a5SAndrey A. Chernov 571b89321a5SAndrey A. Chernov case 'l': 572b89321a5SAndrey A. Chernov if (program != AT) 573b89321a5SAndrey A. Chernov usage(); 574b89321a5SAndrey A. Chernov 575b89321a5SAndrey A. Chernov program = ATQ; 576b89321a5SAndrey A. Chernov options = "q:vV"; 577b89321a5SAndrey A. Chernov break; 578b89321a5SAndrey A. Chernov 579b89321a5SAndrey A. Chernov case 'b': 580b89321a5SAndrey A. Chernov if (program != AT) 581b89321a5SAndrey A. Chernov usage(); 582b89321a5SAndrey A. Chernov 583b89321a5SAndrey A. Chernov program = BATCH; 584b89321a5SAndrey A. Chernov options = "f:q:mvV"; 585b89321a5SAndrey A. Chernov break; 586b89321a5SAndrey A. Chernov 587b89321a5SAndrey A. Chernov case 'V': 588b89321a5SAndrey A. Chernov disp_version = 1; 589d78e98d2SNate Williams break; 590d78e98d2SNate Williams 591d78e98d2SNate Williams default: 592d78e98d2SNate Williams usage(); 593d78e98d2SNate Williams break; 594d78e98d2SNate Williams } 595b89321a5SAndrey A. Chernov /* end of options eating 596b89321a5SAndrey A. Chernov */ 597d78e98d2SNate Williams 598b89321a5SAndrey A. Chernov if (disp_version) 599b89321a5SAndrey A. Chernov fprintf(stderr, "at version " VERSION "\n" 600b89321a5SAndrey A. Chernov "Bug reports to: ig25@rz.uni-karlsruhe.de (Thomas Koenig)\n"); 601b89321a5SAndrey A. Chernov 602b89321a5SAndrey A. Chernov /* select our program 603b89321a5SAndrey A. Chernov */ 604b89321a5SAndrey A. Chernov if(!check_permission()) 605b89321a5SAndrey A. Chernov { 606b89321a5SAndrey A. Chernov fprintf(stderr, "You do not have permission to use %s.\n",namep); 607b89321a5SAndrey A. Chernov exit(EXIT_FAILURE); 608b89321a5SAndrey A. Chernov } 609d78e98d2SNate Williams switch (program) { 610d78e98d2SNate Williams case ATQ: 611d78e98d2SNate Williams 612b89321a5SAndrey A. Chernov REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 613d78e98d2SNate Williams 614d78e98d2SNate Williams list_jobs(); 615d78e98d2SNate Williams break; 616d78e98d2SNate Williams 617d78e98d2SNate Williams case ATRM: 618d78e98d2SNate Williams 619b89321a5SAndrey A. Chernov REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 620d78e98d2SNate Williams 621d78e98d2SNate Williams delete_jobs(argc, argv); 622d78e98d2SNate Williams break; 623d78e98d2SNate Williams 624d78e98d2SNate Williams case AT: 625d78e98d2SNate Williams timer = parsetime(argc, argv); 626b89321a5SAndrey A. Chernov if (atverify) 627b89321a5SAndrey A. Chernov { 628d78e98d2SNate Williams struct tm *tm = localtime(&timer); 629d78e98d2SNate Williams fprintf(stderr, "%s\n", asctime(tm)); 630d78e98d2SNate Williams } 631d78e98d2SNate Williams writefile(timer, queue); 632d78e98d2SNate Williams break; 633d78e98d2SNate Williams 634d78e98d2SNate Williams case BATCH: 635b89321a5SAndrey A. Chernov if (queue_set) 636b89321a5SAndrey A. Chernov queue = toupper(queue); 637b89321a5SAndrey A. Chernov else 638b89321a5SAndrey A. Chernov queue = DEFAULT_BATCH_QUEUE; 639b89321a5SAndrey A. Chernov 640b89321a5SAndrey A. Chernov if (argc > optind) 641b89321a5SAndrey A. Chernov timer = parsetime(argc, argv); 642b89321a5SAndrey A. Chernov else 643b89321a5SAndrey A. Chernov timer = time(NULL); 644b89321a5SAndrey A. Chernov 645b89321a5SAndrey A. Chernov if (atverify) 646b89321a5SAndrey A. Chernov { 647b89321a5SAndrey A. Chernov struct tm *tm = localtime(&timer); 648b89321a5SAndrey A. Chernov fprintf(stderr, "%s\n", asctime(tm)); 649b89321a5SAndrey A. Chernov } 650b89321a5SAndrey A. Chernov 651b89321a5SAndrey A. Chernov writefile(timer, queue); 652d78e98d2SNate Williams break; 653d78e98d2SNate Williams 654d78e98d2SNate Williams default: 655d78e98d2SNate Williams panic("Internal error"); 656d78e98d2SNate Williams break; 657d78e98d2SNate Williams } 658d78e98d2SNate Williams exit(EXIT_SUCCESS); 659d78e98d2SNate Williams } 660