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. 20ddcf8022SAndrey A. Chernov * IN NO EVENT SHALL THE AUTHOR(S) 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 2981c8c7a4SPhilippe Charnier #ifndef lint 3081c8c7a4SPhilippe Charnier static const char rcsid[] = 3181c8c7a4SPhilippe Charnier "$FreeBSD$"; 3281c8c7a4SPhilippe Charnier #endif /* not lint */ 3381c8c7a4SPhilippe Charnier 34d78e98d2SNate Williams #define _USE_BSD 1 35d78e98d2SNate Williams 36d78e98d2SNate Williams /* System Headers */ 37b89321a5SAndrey A. Chernov 38d78e98d2SNate Williams #include <sys/types.h> 39d78e98d2SNate Williams #include <sys/stat.h> 40d78e98d2SNate Williams #include <sys/wait.h> 41a88368cfSDavid Nugent #include <sys/param.h> 42d78e98d2SNate Williams #include <ctype.h> 43d78e98d2SNate Williams #include <dirent.h> 4481c8c7a4SPhilippe Charnier #include <err.h> 45d78e98d2SNate Williams #include <errno.h> 46d78e98d2SNate Williams #include <fcntl.h> 47d78e98d2SNate Williams #include <pwd.h> 48d78e98d2SNate Williams #include <signal.h> 49d78e98d2SNate Williams #include <stddef.h> 50d78e98d2SNate Williams #include <stdio.h> 51d78e98d2SNate Williams #include <stdlib.h> 52d78e98d2SNate Williams #include <string.h> 53d78e98d2SNate Williams #include <time.h> 54d78e98d2SNate Williams #include <unistd.h> 55a88368cfSDavid Nugent #include <utmp.h> 56b89321a5SAndrey A. Chernov #ifndef __FreeBSD__ 57b89321a5SAndrey A. Chernov #include <getopt.h> 58238d3ffdSAndrey A. Chernov #else 59238d3ffdSAndrey A. Chernov #include <locale.h> 60b89321a5SAndrey A. Chernov #endif 61d78e98d2SNate Williams 6208a77c42SAndrey A. Chernov #if (MAXLOGNAME-1) > UT_NAMESIZE 63a88368cfSDavid Nugent #define LOGNAMESIZE UT_NAMESIZE 64a88368cfSDavid Nugent #else 6508a77c42SAndrey A. Chernov #define LOGNAMESIZE (MAXLOGNAME-1) 66a88368cfSDavid Nugent #endif 67a88368cfSDavid Nugent 68d78e98d2SNate Williams /* Local headers */ 69b89321a5SAndrey A. Chernov 70d78e98d2SNate Williams #include "at.h" 71d78e98d2SNate Williams #include "panic.h" 72d78e98d2SNate Williams #include "parsetime.h" 73b89321a5SAndrey A. Chernov #include "perm.h" 74b89321a5SAndrey A. Chernov 75d78e98d2SNate Williams #define MAIN 76d78e98d2SNate Williams #include "privs.h" 77d78e98d2SNate Williams 78d78e98d2SNate Williams /* Macros */ 79b89321a5SAndrey A. Chernov 80b89321a5SAndrey A. Chernov #ifndef ATJOB_DIR 81b89321a5SAndrey A. Chernov #define ATJOB_DIR "/usr/spool/atjobs/" 82b89321a5SAndrey A. Chernov #endif 83b89321a5SAndrey A. Chernov 84b89321a5SAndrey A. Chernov #ifndef LFILE 85b89321a5SAndrey A. Chernov #define LFILE ATJOB_DIR ".lockfile" 86b89321a5SAndrey A. Chernov #endif 87b89321a5SAndrey A. Chernov 88b89321a5SAndrey A. Chernov #ifndef ATJOB_MX 89b89321a5SAndrey A. Chernov #define ATJOB_MX 255 90b89321a5SAndrey A. Chernov #endif 91b89321a5SAndrey A. Chernov 92d78e98d2SNate Williams #define ALARMC 10 /* Number of seconds to wait for timeout */ 93d78e98d2SNate Williams 94d78e98d2SNate Williams #define SIZE 255 95d78e98d2SNate Williams #define TIMESIZE 50 96d78e98d2SNate Williams 97ddcf8022SAndrey A. Chernov enum { ATQ, ATRM, AT, BATCH, CAT }; /* what program we want to run */ 98ddcf8022SAndrey A. Chernov 99d78e98d2SNate Williams /* File scope variables */ 100b89321a5SAndrey A. Chernov 101d78e98d2SNate Williams char *no_export[] = 102d78e98d2SNate Williams { 103d78e98d2SNate Williams "TERM", "TERMCAP", "DISPLAY", "_" 104d78e98d2SNate Williams } ; 10596846ff6SWarner Losh static int send_mail = 0; 106d78e98d2SNate Williams 107d78e98d2SNate Williams /* External variables */ 108b89321a5SAndrey A. Chernov 109d78e98d2SNate Williams extern char **environ; 110d78e98d2SNate Williams int fcreated; 111b89321a5SAndrey A. Chernov char atfile[] = ATJOB_DIR "12345678901234"; 112d78e98d2SNate Williams 113d78e98d2SNate Williams char *atinput = (char*)0; /* where to get input from */ 114d78e98d2SNate Williams char atqueue = 0; /* which queue to examine for jobs (atq) */ 115d78e98d2SNate Williams char atverify = 0; /* verify time instead of queuing job */ 116d78e98d2SNate Williams 117d78e98d2SNate Williams /* Function declarations */ 118b89321a5SAndrey A. Chernov 119b89321a5SAndrey A. Chernov static void sigc(int signo); 120b89321a5SAndrey A. Chernov static void alarmc(int signo); 121b89321a5SAndrey A. Chernov static char *cwdname(void); 122b89321a5SAndrey A. Chernov static void writefile(time_t runtimer, char queue); 123b89321a5SAndrey A. Chernov static void list_jobs(void); 124d78e98d2SNate Williams 125d78e98d2SNate Williams /* Signal catching functions */ 126d78e98d2SNate Williams 127b89321a5SAndrey A. Chernov static void sigc(int signo) 128d78e98d2SNate Williams { 129d78e98d2SNate Williams /* If the user presses ^C, remove the spool file and exit 130d78e98d2SNate Williams */ 131b89321a5SAndrey A. Chernov if (fcreated) 132b89321a5SAndrey A. Chernov { 133d78e98d2SNate Williams PRIV_START 134d78e98d2SNate Williams unlink(atfile); 135d78e98d2SNate Williams PRIV_END 136d78e98d2SNate Williams } 137d78e98d2SNate Williams 138d78e98d2SNate Williams exit(EXIT_FAILURE); 139d78e98d2SNate Williams } 140d78e98d2SNate Williams 141b89321a5SAndrey A. Chernov static void alarmc(int signo) 142d78e98d2SNate Williams { 143d78e98d2SNate Williams /* Time out after some seconds 144d78e98d2SNate Williams */ 14581c8c7a4SPhilippe Charnier panic("file locking timed out"); 146d78e98d2SNate Williams } 147d78e98d2SNate Williams 148d78e98d2SNate Williams /* Local functions */ 149d78e98d2SNate Williams 150b89321a5SAndrey A. Chernov static char *cwdname(void) 151d78e98d2SNate Williams { 152d78e98d2SNate Williams /* Read in the current directory; the name will be overwritten on 153d78e98d2SNate Williams * subsequent calls. 154d78e98d2SNate Williams */ 155d78e98d2SNate Williams static char *ptr = NULL; 156d78e98d2SNate Williams static size_t size = SIZE; 157d78e98d2SNate Williams 158d78e98d2SNate Williams if (ptr == NULL) 159a9be9be8SDavid E. O'Brien if ((ptr = malloc(size)) == NULL) 160a9be9be8SDavid E. O'Brien errx(EXIT_FAILURE, "virtual memory exhausted"); 161d78e98d2SNate Williams 162b89321a5SAndrey A. Chernov while (1) 163b89321a5SAndrey A. Chernov { 164d78e98d2SNate Williams if (ptr == NULL) 16581c8c7a4SPhilippe Charnier panic("out of memory"); 166d78e98d2SNate Williams 167d78e98d2SNate Williams if (getcwd(ptr, size-1) != NULL) 168d78e98d2SNate Williams return ptr; 169d78e98d2SNate Williams 170d78e98d2SNate Williams if (errno != ERANGE) 17181c8c7a4SPhilippe Charnier perr("cannot get directory"); 172d78e98d2SNate Williams 173d78e98d2SNate Williams free (ptr); 174d78e98d2SNate Williams size += SIZE; 175a9be9be8SDavid E. O'Brien if ((ptr = malloc(size)) == NULL) 176a9be9be8SDavid E. O'Brien errx(EXIT_FAILURE, "virtual memory exhausted"); 177d78e98d2SNate Williams } 178d78e98d2SNate Williams } 179d78e98d2SNate Williams 180ddcf8022SAndrey A. Chernov static long 181ddcf8022SAndrey A. Chernov nextjob() 182ddcf8022SAndrey A. Chernov { 183ddcf8022SAndrey A. Chernov long jobno; 184ddcf8022SAndrey A. Chernov FILE *fid; 185ddcf8022SAndrey A. Chernov 186ddcf8022SAndrey A. Chernov if ((fid = fopen(ATJOB_DIR ".SEQ", "r+")) != (FILE*)0) { 187ddcf8022SAndrey A. Chernov if (fscanf(fid, "%5lx", &jobno) == 1) { 188ddcf8022SAndrey A. Chernov rewind(fid); 189ddcf8022SAndrey A. Chernov jobno = (1+jobno) % 0xfffff; /* 2^20 jobs enough? */ 190ddcf8022SAndrey A. Chernov fprintf(fid, "%05lx\n", jobno); 191ddcf8022SAndrey A. Chernov } 192ddcf8022SAndrey A. Chernov else 193ddcf8022SAndrey A. Chernov jobno = EOF; 194ddcf8022SAndrey A. Chernov fclose(fid); 195ddcf8022SAndrey A. Chernov return jobno; 196ddcf8022SAndrey A. Chernov } 197ddcf8022SAndrey A. Chernov else if ((fid = fopen(ATJOB_DIR ".SEQ", "w")) != (FILE*)0) { 198ddcf8022SAndrey A. Chernov fprintf(fid, "%05lx\n", jobno = 1); 199ddcf8022SAndrey A. Chernov fclose(fid); 200ddcf8022SAndrey A. Chernov return 1; 201ddcf8022SAndrey A. Chernov } 202ddcf8022SAndrey A. Chernov return EOF; 203ddcf8022SAndrey A. Chernov } 204ddcf8022SAndrey A. Chernov 205d78e98d2SNate Williams static void 206b89321a5SAndrey A. Chernov writefile(time_t runtimer, char queue) 207d78e98d2SNate Williams { 208b89321a5SAndrey A. Chernov /* This does most of the work if at or batch are invoked for writing a job. 209d78e98d2SNate Williams */ 210ddcf8022SAndrey A. Chernov long jobno; 211d78e98d2SNate Williams char *ap, *ppos, *mailname; 212d78e98d2SNate Williams struct passwd *pass_entry; 213d78e98d2SNate Williams struct stat statbuf; 214d78e98d2SNate Williams int fdes, lockdes, fd2; 215d78e98d2SNate Williams FILE *fp, *fpin; 216d78e98d2SNate Williams struct sigaction act; 217d78e98d2SNate Williams char **atenv; 218d78e98d2SNate Williams int ch; 219d78e98d2SNate Williams mode_t cmask; 220d78e98d2SNate Williams struct flock lock; 221d78e98d2SNate Williams 222238d3ffdSAndrey A. Chernov #ifdef __FreeBSD__ 223238d3ffdSAndrey A. Chernov (void) setlocale(LC_TIME, ""); 224238d3ffdSAndrey A. Chernov #endif 225238d3ffdSAndrey A. Chernov 226b89321a5SAndrey A. Chernov /* Install the signal handler for SIGINT; terminate after removing the 227d78e98d2SNate Williams * spool file if necessary 228d78e98d2SNate Williams */ 229d78e98d2SNate Williams act.sa_handler = sigc; 230d78e98d2SNate Williams sigemptyset(&(act.sa_mask)); 231d78e98d2SNate Williams act.sa_flags = 0; 232d78e98d2SNate Williams 233d78e98d2SNate Williams sigaction(SIGINT, &act, NULL); 234d78e98d2SNate Williams 235b89321a5SAndrey A. Chernov ppos = atfile + strlen(ATJOB_DIR); 236d78e98d2SNate Williams 237b89321a5SAndrey A. Chernov /* Loop over all possible file names for running something at this 238b89321a5SAndrey A. Chernov * particular time, see if a file is there; the first empty slot at any 239b89321a5SAndrey A. Chernov * particular time is used. Lock the file LFILE first to make sure 240b89321a5SAndrey A. Chernov * we're alone when doing this. 241d78e98d2SNate Williams */ 242d78e98d2SNate Williams 243d78e98d2SNate Williams PRIV_START 244d78e98d2SNate Williams 245b89321a5SAndrey A. Chernov if ((lockdes = open(LFILE, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR)) < 0) 24681c8c7a4SPhilippe Charnier perr("cannot open lockfile " LFILE); 247d78e98d2SNate Williams 248b89321a5SAndrey A. Chernov lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; 249d78e98d2SNate Williams lock.l_len = 0; 250d78e98d2SNate Williams 251d78e98d2SNate Williams act.sa_handler = alarmc; 252d78e98d2SNate Williams sigemptyset(&(act.sa_mask)); 253d78e98d2SNate Williams act.sa_flags = 0; 254d78e98d2SNate Williams 255b89321a5SAndrey A. Chernov /* Set an alarm so a timeout occurs after ALARMC seconds, in case 256d78e98d2SNate Williams * something is seriously broken. 257d78e98d2SNate Williams */ 258d78e98d2SNate Williams sigaction(SIGALRM, &act, NULL); 259d78e98d2SNate Williams alarm(ALARMC); 260d78e98d2SNate Williams fcntl(lockdes, F_SETLKW, &lock); 261d78e98d2SNate Williams alarm(0); 262d78e98d2SNate Williams 263ddcf8022SAndrey A. Chernov if ((jobno = nextjob()) == EOF) 26481c8c7a4SPhilippe Charnier perr("cannot generate job number"); 265ddcf8022SAndrey A. Chernov 266ddcf8022SAndrey A. Chernov sprintf(ppos, "%c%5lx%8lx", queue, 267ddcf8022SAndrey A. Chernov jobno, (unsigned long) (runtimer/60)); 268ddcf8022SAndrey A. Chernov 269d78e98d2SNate Williams for(ap=ppos; *ap != '\0'; ap ++) 270d78e98d2SNate Williams if (*ap == ' ') 271d78e98d2SNate Williams *ap = '0'; 272d78e98d2SNate Williams 273b89321a5SAndrey A. Chernov if (stat(atfile, &statbuf) != 0) 274ddcf8022SAndrey A. Chernov if (errno != ENOENT) 27581c8c7a4SPhilippe Charnier perr("cannot access " ATJOB_DIR); 276d78e98d2SNate Williams 277b89321a5SAndrey A. Chernov /* Create the file. The x bit is only going to be set after it has 278b89321a5SAndrey A. Chernov * been completely written out, to make sure it is not executed in the 279b89321a5SAndrey A. Chernov * meantime. To make sure they do not get deleted, turn off their r 280b89321a5SAndrey A. Chernov * bit. Yes, this is a kluge. 281d78e98d2SNate Williams */ 282d78e98d2SNate Williams cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR); 283d78e98d2SNate Williams if ((fdes = creat(atfile, O_WRONLY)) == -1) 28481c8c7a4SPhilippe Charnier perr("cannot create atjob file"); 285d78e98d2SNate Williams 286d78e98d2SNate Williams if ((fd2 = dup(fdes)) <0) 28781c8c7a4SPhilippe Charnier perr("error in dup() of job file"); 288d78e98d2SNate Williams 289b89321a5SAndrey A. Chernov if(fchown(fd2, real_uid, real_gid) != 0) 29081c8c7a4SPhilippe Charnier perr("cannot give away file"); 291d78e98d2SNate Williams 292d78e98d2SNate Williams PRIV_END 293d78e98d2SNate Williams 294b5c3f5e7SAndrey A. Chernov /* We no longer need suid root; now we just need to be able to write 295b5c3f5e7SAndrey A. Chernov * to the directory, if necessary. 296b5c3f5e7SAndrey A. Chernov */ 297b5c3f5e7SAndrey A. Chernov 298b5c3f5e7SAndrey A. Chernov REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 299b5c3f5e7SAndrey A. Chernov 300b89321a5SAndrey A. Chernov /* We've successfully created the file; let's set the flag so it 301d78e98d2SNate Williams * gets removed in case of an interrupt or error. 302d78e98d2SNate Williams */ 303d78e98d2SNate Williams fcreated = 1; 304d78e98d2SNate Williams 305b89321a5SAndrey A. Chernov /* Now we can release the lock, so other people can access it 306b89321a5SAndrey A. Chernov */ 307b89321a5SAndrey A. Chernov lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; 308d78e98d2SNate Williams lock.l_len = 0; 309d78e98d2SNate Williams fcntl(lockdes, F_SETLKW, &lock); 310d78e98d2SNate Williams close(lockdes); 311d78e98d2SNate Williams 312d78e98d2SNate Williams if((fp = fdopen(fdes, "w")) == NULL) 31381c8c7a4SPhilippe Charnier panic("cannot reopen atjob file"); 314d78e98d2SNate Williams 315b89321a5SAndrey A. Chernov /* Get the userid to mail to, first by trying getlogin(), which reads 316b89321a5SAndrey A. Chernov * /etc/utmp, then from LOGNAME, finally from getpwuid(). 317d78e98d2SNate Williams */ 318d78e98d2SNate Williams mailname = getlogin(); 319d78e98d2SNate Williams if (mailname == NULL) 320d78e98d2SNate Williams mailname = getenv("LOGNAME"); 321d78e98d2SNate Williams 322d78e98d2SNate Williams if ((mailname == NULL) || (mailname[0] == '\0') 323a88368cfSDavid Nugent || (strlen(mailname) > LOGNAMESIZE) || (getpwnam(mailname)==NULL)) 324b89321a5SAndrey A. Chernov { 325ddcf8022SAndrey A. Chernov pass_entry = getpwuid(real_uid); 326d78e98d2SNate Williams if (pass_entry != NULL) 327d78e98d2SNate Williams mailname = pass_entry->pw_name; 328d78e98d2SNate Williams } 329d78e98d2SNate Williams 330b89321a5SAndrey A. Chernov if (atinput != (char *) NULL) 331b89321a5SAndrey A. Chernov { 332d78e98d2SNate Williams fpin = freopen(atinput, "r", stdin); 333d78e98d2SNate Williams if (fpin == NULL) 33481c8c7a4SPhilippe Charnier perr("cannot open input file"); 335d78e98d2SNate Williams } 336a88368cfSDavid Nugent fprintf(fp, "#!/bin/sh\n# atrun uid=%ld gid=%ld\n# mail %*s %d\n", 337a88368cfSDavid Nugent (long) real_uid, (long) real_gid, LOGNAMESIZE, mailname, send_mail); 338d78e98d2SNate Williams 339b89321a5SAndrey A. Chernov /* Write out the umask at the time of invocation 340b89321a5SAndrey A. Chernov */ 341d78e98d2SNate Williams fprintf(fp, "umask %lo\n", (unsigned long) cmask); 342d78e98d2SNate Williams 343b89321a5SAndrey A. Chernov /* Write out the environment. Anything that may look like a 344b89321a5SAndrey A. Chernov * special character to the shell is quoted, except for \n, which is 34581c8c7a4SPhilippe Charnier * done with a pair of "'s. Don't export the no_export list (such 346b89321a5SAndrey A. Chernov * as TERM or DISPLAY) because we don't want these. 347d78e98d2SNate Williams */ 348b89321a5SAndrey A. Chernov for (atenv= environ; *atenv != NULL; atenv++) 349b89321a5SAndrey A. Chernov { 350d78e98d2SNate Williams int export = 1; 351d78e98d2SNate Williams char *eqp; 352d78e98d2SNate Williams 353d78e98d2SNate Williams eqp = strchr(*atenv, '='); 354d78e98d2SNate Williams if (ap == NULL) 355d78e98d2SNate Williams eqp = *atenv; 356b89321a5SAndrey A. Chernov else 357b89321a5SAndrey A. Chernov { 358d78e98d2SNate Williams int i; 359b89321a5SAndrey A. Chernov for (i=0; i<sizeof(no_export)/sizeof(no_export[0]); i++) 360b89321a5SAndrey A. Chernov { 361d78e98d2SNate Williams export = export 362d78e98d2SNate Williams && (strncmp(*atenv, no_export[i], 363d78e98d2SNate Williams (size_t) (eqp-*atenv)) != 0); 364d78e98d2SNate Williams } 365d78e98d2SNate Williams eqp++; 366d78e98d2SNate Williams } 367d78e98d2SNate Williams 368b89321a5SAndrey A. Chernov if (export) 369b89321a5SAndrey A. Chernov { 370d78e98d2SNate Williams fwrite(*atenv, sizeof(char), eqp-*atenv, fp); 371b89321a5SAndrey A. Chernov for(ap = eqp;*ap != '\0'; ap++) 372b89321a5SAndrey A. Chernov { 373d78e98d2SNate Williams if (*ap == '\n') 374d78e98d2SNate Williams fprintf(fp, "\"\n\""); 375b89321a5SAndrey A. Chernov else 376b89321a5SAndrey A. Chernov { 377ddcf8022SAndrey A. Chernov if (!isalnum(*ap)) { 378ddcf8022SAndrey A. Chernov switch (*ap) { 379ddcf8022SAndrey A. Chernov case '%': case '/': case '{': case '[': 380ddcf8022SAndrey A. Chernov case ']': case '=': case '}': case '@': 381ddcf8022SAndrey A. Chernov case '+': case '#': case ',': case '.': 382ddcf8022SAndrey A. Chernov case ':': case '-': case '_': 383ddcf8022SAndrey A. Chernov break; 384ddcf8022SAndrey A. Chernov default: 385d78e98d2SNate Williams fputc('\\', fp); 386ddcf8022SAndrey A. Chernov break; 387ddcf8022SAndrey A. Chernov } 388ddcf8022SAndrey A. Chernov } 389d78e98d2SNate Williams fputc(*ap, fp); 390d78e98d2SNate Williams } 391d78e98d2SNate Williams } 392d78e98d2SNate Williams fputs("; export ", fp); 393d78e98d2SNate Williams fwrite(*atenv, sizeof(char), eqp-*atenv -1, fp); 394d78e98d2SNate Williams fputc('\n', fp); 395d78e98d2SNate Williams 396d78e98d2SNate Williams } 397d78e98d2SNate Williams } 398b89321a5SAndrey A. Chernov /* Cd to the directory at the time and write out all the 399b89321a5SAndrey A. Chernov * commands the user supplies from stdin. 400d78e98d2SNate Williams */ 401b89321a5SAndrey A. Chernov fprintf(fp, "cd "); 402b89321a5SAndrey A. Chernov for (ap = cwdname(); *ap != '\0'; ap++) 403b89321a5SAndrey A. Chernov { 404b89321a5SAndrey A. Chernov if (*ap == '\n') 405b89321a5SAndrey A. Chernov fprintf(fp, "\"\n\""); 406b89321a5SAndrey A. Chernov else 407b89321a5SAndrey A. Chernov { 408b89321a5SAndrey A. Chernov if (*ap != '/' && !isalnum(*ap)) 409b89321a5SAndrey A. Chernov fputc('\\', fp); 410b89321a5SAndrey A. Chernov 411b89321a5SAndrey A. Chernov fputc(*ap, fp); 412b89321a5SAndrey A. Chernov } 413b89321a5SAndrey A. Chernov } 414b89321a5SAndrey A. Chernov /* Test cd's exit status: die if the original directory has been 415b89321a5SAndrey A. Chernov * removed, become unreadable or whatever 416b89321a5SAndrey A. Chernov */ 417b89321a5SAndrey A. Chernov fprintf(fp, " || {\n\t echo 'Execution directory " 418b89321a5SAndrey A. Chernov "inaccessible' >&2\n\t exit 1\n}\n"); 419d78e98d2SNate Williams 420d78e98d2SNate Williams while((ch = getchar()) != EOF) 421d78e98d2SNate Williams fputc(ch, fp); 422d78e98d2SNate Williams 423d78e98d2SNate Williams fprintf(fp, "\n"); 424d78e98d2SNate Williams if (ferror(fp)) 42581c8c7a4SPhilippe Charnier panic("output error"); 426d78e98d2SNate Williams 427d78e98d2SNate Williams if (ferror(stdin)) 42881c8c7a4SPhilippe Charnier panic("input error"); 429d78e98d2SNate Williams 430d78e98d2SNate Williams fclose(fp); 431d78e98d2SNate Williams 432b89321a5SAndrey A. Chernov /* Set the x bit so that we're ready to start executing 433d78e98d2SNate Williams */ 434b89321a5SAndrey A. Chernov 435d78e98d2SNate Williams if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0) 43681c8c7a4SPhilippe Charnier perr("cannot give away file"); 437d78e98d2SNate Williams 438d78e98d2SNate Williams close(fd2); 439ddcf8022SAndrey A. Chernov fprintf(stderr, "Job %ld will be executed using /bin/sh\n", jobno); 440d78e98d2SNate Williams } 441d78e98d2SNate Williams 442d78e98d2SNate Williams static void 443d78e98d2SNate Williams list_jobs() 444d78e98d2SNate Williams { 445b89321a5SAndrey A. Chernov /* List all a user's jobs in the queue, by looping through ATJOB_DIR, 446b89321a5SAndrey A. Chernov * or everybody's if we are root 447d78e98d2SNate Williams */ 448d78e98d2SNate Williams struct passwd *pw; 449d78e98d2SNate Williams DIR *spool; 450d78e98d2SNate Williams struct dirent *dirent; 451d78e98d2SNate Williams struct stat buf; 452d78e98d2SNate Williams struct tm runtime; 453d78e98d2SNate Williams unsigned long ctm; 454d78e98d2SNate Williams char queue; 455ddcf8022SAndrey A. Chernov long jobno; 456d78e98d2SNate Williams time_t runtimer; 457d78e98d2SNate Williams char timestr[TIMESIZE]; 458d78e98d2SNate Williams int first=1; 459d78e98d2SNate Williams 460f4a747a1SStephen McKay #ifdef __FreeBSD__ 461f4a747a1SStephen McKay (void) setlocale(LC_TIME, ""); 462f4a747a1SStephen McKay #endif 463f4a747a1SStephen McKay 464d78e98d2SNate Williams PRIV_START 465d78e98d2SNate Williams 466b89321a5SAndrey A. Chernov if (chdir(ATJOB_DIR) != 0) 46781c8c7a4SPhilippe Charnier perr("cannot change to " ATJOB_DIR); 468d78e98d2SNate Williams 469d78e98d2SNate Williams if ((spool = opendir(".")) == NULL) 47081c8c7a4SPhilippe Charnier perr("cannot open " ATJOB_DIR); 471d78e98d2SNate Williams 472b89321a5SAndrey A. Chernov /* Loop over every file in the directory 473b89321a5SAndrey A. Chernov */ 474d78e98d2SNate Williams while((dirent = readdir(spool)) != NULL) { 475d78e98d2SNate Williams if (stat(dirent->d_name, &buf) != 0) 47681c8c7a4SPhilippe Charnier perr("cannot stat in " ATJOB_DIR); 477d78e98d2SNate Williams 478b89321a5SAndrey A. Chernov /* See it's a regular file and has its x bit turned on and 479d78e98d2SNate Williams * is the user's 480d78e98d2SNate Williams */ 481d78e98d2SNate Williams if (!S_ISREG(buf.st_mode) 482d78e98d2SNate Williams || ((buf.st_uid != real_uid) && ! (real_uid == 0)) 483d78e98d2SNate Williams || !(S_IXUSR & buf.st_mode || atverify)) 484d78e98d2SNate Williams continue; 485d78e98d2SNate Williams 486ddcf8022SAndrey A. Chernov if(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3) 487d78e98d2SNate Williams continue; 488d78e98d2SNate Williams 489d78e98d2SNate Williams if (atqueue && (queue != atqueue)) 490d78e98d2SNate Williams continue; 491d78e98d2SNate Williams 492d78e98d2SNate Williams runtimer = 60*(time_t) ctm; 493d78e98d2SNate Williams runtime = *localtime(&runtimer); 494d78e98d2SNate Williams strftime(timestr, TIMESIZE, "%X %x", &runtime); 495d78e98d2SNate Williams if (first) { 496d78e98d2SNate Williams printf("Date\t\t\tOwner\tQueue\tJob#\n"); 497d78e98d2SNate Williams first=0; 498d78e98d2SNate Williams } 499d78e98d2SNate Williams pw = getpwuid(buf.st_uid); 500d78e98d2SNate Williams 501ddcf8022SAndrey A. Chernov printf("%s\t%s\t%c%s\t%ld\n", 502d78e98d2SNate Williams timestr, 503d78e98d2SNate Williams pw ? pw->pw_name : "???", 504d78e98d2SNate Williams queue, 505d78e98d2SNate Williams (S_IXUSR & buf.st_mode) ? "":"(done)", 506ddcf8022SAndrey A. Chernov jobno); 507d78e98d2SNate Williams } 508d78e98d2SNate Williams PRIV_END 509d78e98d2SNate Williams } 510d78e98d2SNate Williams 511d78e98d2SNate Williams static void 512ddcf8022SAndrey A. Chernov process_jobs(int argc, char **argv, int what) 513d78e98d2SNate Williams { 514b89321a5SAndrey A. Chernov /* Delete every argument (job - ID) given 515b89321a5SAndrey A. Chernov */ 516d78e98d2SNate Williams int i; 517d78e98d2SNate Williams struct stat buf; 518ddcf8022SAndrey A. Chernov DIR *spool; 519ddcf8022SAndrey A. Chernov struct dirent *dirent; 520ddcf8022SAndrey A. Chernov unsigned long ctm; 521ddcf8022SAndrey A. Chernov char queue; 522ddcf8022SAndrey A. Chernov long jobno; 523d78e98d2SNate Williams 524d78e98d2SNate Williams PRIV_START 525d78e98d2SNate Williams 526b89321a5SAndrey A. Chernov if (chdir(ATJOB_DIR) != 0) 52781c8c7a4SPhilippe Charnier perr("cannot change to " ATJOB_DIR); 528d78e98d2SNate Williams 529ddcf8022SAndrey A. Chernov if ((spool = opendir(".")) == NULL) 53081c8c7a4SPhilippe Charnier perr("cannot open " ATJOB_DIR); 531ddcf8022SAndrey A. Chernov 532ddcf8022SAndrey A. Chernov PRIV_END 533ddcf8022SAndrey A. Chernov 534ddcf8022SAndrey A. Chernov /* Loop over every file in the directory 535ddcf8022SAndrey A. Chernov */ 536ddcf8022SAndrey A. Chernov while((dirent = readdir(spool)) != NULL) { 537ddcf8022SAndrey A. Chernov 538ddcf8022SAndrey A. Chernov PRIV_START 539ddcf8022SAndrey A. Chernov if (stat(dirent->d_name, &buf) != 0) 54081c8c7a4SPhilippe Charnier perr("cannot stat in " ATJOB_DIR); 541ddcf8022SAndrey A. Chernov PRIV_END 542ddcf8022SAndrey A. Chernov 543ddcf8022SAndrey A. Chernov if(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3) 5449cb939b4SAndrey A. Chernov continue; 545ddcf8022SAndrey A. Chernov 546ddcf8022SAndrey A. Chernov for (i=optind; i < argc; i++) { 547ddcf8022SAndrey A. Chernov if (atoi(argv[i]) == jobno) { 54881c8c7a4SPhilippe Charnier if ((buf.st_uid != real_uid) && !(real_uid == 0)) 54981c8c7a4SPhilippe Charnier errx(EXIT_FAILURE, "%s: not owner", argv[i]); 550ddcf8022SAndrey A. Chernov switch (what) { 551ddcf8022SAndrey A. Chernov case ATRM: 552ddcf8022SAndrey A. Chernov 553ddcf8022SAndrey A. Chernov PRIV_START 554ddcf8022SAndrey A. Chernov 555ddcf8022SAndrey A. Chernov if (unlink(dirent->d_name) != 0) 556ddcf8022SAndrey A. Chernov perr(dirent->d_name); 557ddcf8022SAndrey A. Chernov 558d78e98d2SNate Williams PRIV_END 559ddcf8022SAndrey A. Chernov 560ddcf8022SAndrey A. Chernov break; 561ddcf8022SAndrey A. Chernov 562ddcf8022SAndrey A. Chernov case CAT: 563ddcf8022SAndrey A. Chernov { 564ddcf8022SAndrey A. Chernov FILE *fp; 565ddcf8022SAndrey A. Chernov int ch; 566ddcf8022SAndrey A. Chernov 567ddcf8022SAndrey A. Chernov PRIV_START 568ddcf8022SAndrey A. Chernov 569ddcf8022SAndrey A. Chernov fp = fopen(dirent->d_name,"r"); 570ddcf8022SAndrey A. Chernov 571ddcf8022SAndrey A. Chernov PRIV_END 572ddcf8022SAndrey A. Chernov 573ddcf8022SAndrey A. Chernov if (!fp) { 57481c8c7a4SPhilippe Charnier perr("cannot open file"); 575ddcf8022SAndrey A. Chernov } 576ddcf8022SAndrey A. Chernov while((ch = getc(fp)) != EOF) { 577ddcf8022SAndrey A. Chernov putchar(ch); 578ddcf8022SAndrey A. Chernov } 579ddcf8022SAndrey A. Chernov } 580ddcf8022SAndrey A. Chernov break; 581ddcf8022SAndrey A. Chernov 582ddcf8022SAndrey A. Chernov default: 58381c8c7a4SPhilippe Charnier errx(EXIT_FAILURE, "internal error, process_jobs = %d", 58481c8c7a4SPhilippe Charnier what); 585ddcf8022SAndrey A. Chernov } 586ddcf8022SAndrey A. Chernov } 587ddcf8022SAndrey A. Chernov } 588ddcf8022SAndrey A. Chernov } 589d78e98d2SNate Williams } /* delete_jobs */ 590d78e98d2SNate Williams 591d78e98d2SNate Williams int 592b89321a5SAndrey A. Chernov main(int argc, char **argv) 593d78e98d2SNate Williams { 594d78e98d2SNate Williams int c; 595b89321a5SAndrey A. Chernov char queue = DEFAULT_AT_QUEUE; 596b89321a5SAndrey A. Chernov char queue_set = 0; 597d78e98d2SNate Williams char *pgm; 598d78e98d2SNate Williams 599ddcf8022SAndrey A. Chernov enum { ATQ, ATRM, AT, BATCH, CAT }; /* what program we want to run */ 600d78e98d2SNate Williams int program = AT; /* our default program */ 601ddcf8022SAndrey A. Chernov char *options = "q:f:mvldbVc"; /* default options for at */ 602b89321a5SAndrey A. Chernov int disp_version = 0; 603d78e98d2SNate Williams time_t timer; 604d78e98d2SNate Williams 605d78e98d2SNate Williams RELINQUISH_PRIVS 606d78e98d2SNate Williams 607b89321a5SAndrey A. Chernov /* Eat any leading paths 608b89321a5SAndrey A. Chernov */ 609d78e98d2SNate Williams if ((pgm = strrchr(argv[0], '/')) == NULL) 610d78e98d2SNate Williams pgm = argv[0]; 611d78e98d2SNate Williams else 612d78e98d2SNate Williams pgm++; 613d78e98d2SNate Williams 614b89321a5SAndrey A. Chernov /* find out what this program is supposed to do 615b89321a5SAndrey A. Chernov */ 616d78e98d2SNate Williams if (strcmp(pgm, "atq") == 0) { 617d78e98d2SNate Williams program = ATQ; 618b89321a5SAndrey A. Chernov options = "q:vV"; 619b89321a5SAndrey A. Chernov } 620b89321a5SAndrey A. Chernov else if (strcmp(pgm, "atrm") == 0) { 621d78e98d2SNate Williams program = ATRM; 622b89321a5SAndrey A. Chernov options = "V"; 623b89321a5SAndrey A. Chernov } 624b89321a5SAndrey A. Chernov else if (strcmp(pgm, "batch") == 0) { 625d78e98d2SNate Williams program = BATCH; 626b89321a5SAndrey A. Chernov options = "f:q:mvV"; 627d78e98d2SNate Williams } 628d78e98d2SNate Williams 629b89321a5SAndrey A. Chernov /* process whatever options we can process 630b89321a5SAndrey A. Chernov */ 631d78e98d2SNate Williams opterr=1; 6321c8af878SWarner Losh while ((c=getopt(argc, argv, options)) != -1) 633d78e98d2SNate Williams switch (c) { 634d78e98d2SNate Williams case 'v': /* verify time settings */ 635d78e98d2SNate Williams atverify = 1; 636d78e98d2SNate Williams break; 637d78e98d2SNate Williams 638d78e98d2SNate Williams case 'm': /* send mail when job is complete */ 639d78e98d2SNate Williams send_mail = 1; 640d78e98d2SNate Williams break; 641d78e98d2SNate Williams 642d78e98d2SNate Williams case 'f': 643d78e98d2SNate Williams atinput = optarg; 644d78e98d2SNate Williams break; 645d78e98d2SNate Williams 646d78e98d2SNate Williams case 'q': /* specify queue */ 647d78e98d2SNate Williams if (strlen(optarg) > 1) 648d78e98d2SNate Williams usage(); 649d78e98d2SNate Williams 650d78e98d2SNate Williams atqueue = queue = *optarg; 651b89321a5SAndrey A. Chernov if (!(islower(queue)||isupper(queue))) 652d78e98d2SNate Williams usage(); 653b89321a5SAndrey A. Chernov 654b89321a5SAndrey A. Chernov queue_set = 1; 655b89321a5SAndrey A. Chernov break; 656b89321a5SAndrey A. Chernov 657b89321a5SAndrey A. Chernov case 'd': 658b89321a5SAndrey A. Chernov if (program != AT) 659b89321a5SAndrey A. Chernov usage(); 660b89321a5SAndrey A. Chernov 661b89321a5SAndrey A. Chernov program = ATRM; 662b89321a5SAndrey A. Chernov options = "V"; 663b89321a5SAndrey A. Chernov break; 664b89321a5SAndrey A. Chernov 665b89321a5SAndrey A. Chernov case 'l': 666b89321a5SAndrey A. Chernov if (program != AT) 667b89321a5SAndrey A. Chernov usage(); 668b89321a5SAndrey A. Chernov 669b89321a5SAndrey A. Chernov program = ATQ; 670b89321a5SAndrey A. Chernov options = "q:vV"; 671b89321a5SAndrey A. Chernov break; 672b89321a5SAndrey A. Chernov 673b89321a5SAndrey A. Chernov case 'b': 674b89321a5SAndrey A. Chernov if (program != AT) 675b89321a5SAndrey A. Chernov usage(); 676b89321a5SAndrey A. Chernov 677b89321a5SAndrey A. Chernov program = BATCH; 678b89321a5SAndrey A. Chernov options = "f:q:mvV"; 679b89321a5SAndrey A. Chernov break; 680b89321a5SAndrey A. Chernov 681b89321a5SAndrey A. Chernov case 'V': 682b89321a5SAndrey A. Chernov disp_version = 1; 683d78e98d2SNate Williams break; 684d78e98d2SNate Williams 685ddcf8022SAndrey A. Chernov case 'c': 686ddcf8022SAndrey A. Chernov program = CAT; 687ddcf8022SAndrey A. Chernov options = ""; 688ddcf8022SAndrey A. Chernov break; 689ddcf8022SAndrey A. Chernov 690d78e98d2SNate Williams default: 691d78e98d2SNate Williams usage(); 692d78e98d2SNate Williams break; 693d78e98d2SNate Williams } 694b89321a5SAndrey A. Chernov /* end of options eating 695b89321a5SAndrey A. Chernov */ 696d78e98d2SNate Williams 697b89321a5SAndrey A. Chernov if (disp_version) 698b89321a5SAndrey A. Chernov fprintf(stderr, "at version " VERSION "\n" 699b89321a5SAndrey A. Chernov "Bug reports to: ig25@rz.uni-karlsruhe.de (Thomas Koenig)\n"); 700b89321a5SAndrey A. Chernov 701b89321a5SAndrey A. Chernov /* select our program 702b89321a5SAndrey A. Chernov */ 703b89321a5SAndrey A. Chernov if(!check_permission()) 70481c8c7a4SPhilippe Charnier errx(EXIT_FAILURE, "you do not have permission to use this program"); 705d78e98d2SNate Williams switch (program) { 706d78e98d2SNate Williams case ATQ: 707d78e98d2SNate Williams 708b89321a5SAndrey A. Chernov REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 709d78e98d2SNate Williams 710d78e98d2SNate Williams list_jobs(); 711d78e98d2SNate Williams break; 712d78e98d2SNate Williams 713d78e98d2SNate Williams case ATRM: 714d78e98d2SNate Williams 715b89321a5SAndrey A. Chernov REDUCE_PRIV(DAEMON_UID, DAEMON_GID) 716d78e98d2SNate Williams 717ddcf8022SAndrey A. Chernov process_jobs(argc, argv, ATRM); 718ddcf8022SAndrey A. Chernov break; 719ddcf8022SAndrey A. Chernov 720ddcf8022SAndrey A. Chernov case CAT: 721ddcf8022SAndrey A. Chernov 722ddcf8022SAndrey A. Chernov process_jobs(argc, argv, CAT); 723d78e98d2SNate Williams break; 724d78e98d2SNate Williams 725d78e98d2SNate Williams case AT: 726d78e98d2SNate Williams timer = parsetime(argc, argv); 727b89321a5SAndrey A. Chernov if (atverify) 728b89321a5SAndrey A. Chernov { 729d78e98d2SNate Williams struct tm *tm = localtime(&timer); 730d78e98d2SNate Williams fprintf(stderr, "%s\n", asctime(tm)); 731d78e98d2SNate Williams } 732d78e98d2SNate Williams writefile(timer, queue); 733d78e98d2SNate Williams break; 734d78e98d2SNate Williams 735d78e98d2SNate Williams case BATCH: 736b89321a5SAndrey A. Chernov if (queue_set) 737b89321a5SAndrey A. Chernov queue = toupper(queue); 738b89321a5SAndrey A. Chernov else 739b89321a5SAndrey A. Chernov queue = DEFAULT_BATCH_QUEUE; 740b89321a5SAndrey A. Chernov 741b89321a5SAndrey A. Chernov if (argc > optind) 742b89321a5SAndrey A. Chernov timer = parsetime(argc, argv); 743b89321a5SAndrey A. Chernov else 744b89321a5SAndrey A. Chernov timer = time(NULL); 745b89321a5SAndrey A. Chernov 746b89321a5SAndrey A. Chernov if (atverify) 747b89321a5SAndrey A. Chernov { 748b89321a5SAndrey A. Chernov struct tm *tm = localtime(&timer); 749b89321a5SAndrey A. Chernov fprintf(stderr, "%s\n", asctime(tm)); 750b89321a5SAndrey A. Chernov } 751b89321a5SAndrey A. Chernov 752b89321a5SAndrey A. Chernov writefile(timer, queue); 753d78e98d2SNate Williams break; 754d78e98d2SNate Williams 755d78e98d2SNate Williams default: 75681c8c7a4SPhilippe Charnier panic("internal error"); 757d78e98d2SNate Williams break; 758d78e98d2SNate Williams } 759d78e98d2SNate Williams exit(EXIT_SUCCESS); 760d78e98d2SNate Williams } 761