1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 23*0Sstevel@tonic-gate /* All Rights Reserved */ 24*0Sstevel@tonic-gate 25*0Sstevel@tonic-gate 26*0Sstevel@tonic-gate /* 27*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 28*0Sstevel@tonic-gate * Use is subject to license terms. 29*0Sstevel@tonic-gate */ 30*0Sstevel@tonic-gate 31*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 32*0Sstevel@tonic-gate 33*0Sstevel@tonic-gate #include <sys/resource.h> 34*0Sstevel@tonic-gate #include <sys/stat.h> 35*0Sstevel@tonic-gate #include <sys/types.h> 36*0Sstevel@tonic-gate 37*0Sstevel@tonic-gate #include <dirent.h> 38*0Sstevel@tonic-gate #include <string.h> 39*0Sstevel@tonic-gate #include <stdlib.h> 40*0Sstevel@tonic-gate #include <fcntl.h> 41*0Sstevel@tonic-gate #include <pwd.h> 42*0Sstevel@tonic-gate #include <stdio.h> 43*0Sstevel@tonic-gate #include <ctype.h> 44*0Sstevel@tonic-gate #include <time.h> 45*0Sstevel@tonic-gate #include <signal.h> 46*0Sstevel@tonic-gate #include <errno.h> 47*0Sstevel@tonic-gate #include <limits.h> 48*0Sstevel@tonic-gate #include <ulimit.h> 49*0Sstevel@tonic-gate #include <unistd.h> 50*0Sstevel@tonic-gate #include <locale.h> 51*0Sstevel@tonic-gate #include <libintl.h> 52*0Sstevel@tonic-gate #include <tzfile.h> 53*0Sstevel@tonic-gate #include <project.h> 54*0Sstevel@tonic-gate 55*0Sstevel@tonic-gate #include "cron.h" 56*0Sstevel@tonic-gate 57*0Sstevel@tonic-gate #define TMPFILE "_at" /* prefix for temporary files */ 58*0Sstevel@tonic-gate /* 59*0Sstevel@tonic-gate * Mode for creating files in ATDIR. 60*0Sstevel@tonic-gate * Setuid bit on so that if an owner of a file gives that file 61*0Sstevel@tonic-gate * away to someone else, the setuid bit will no longer be set. 62*0Sstevel@tonic-gate * If this happens, atrun will not execute the file 63*0Sstevel@tonic-gate */ 64*0Sstevel@tonic-gate #define ATMODE (S_ISUID | S_IRUSR | S_IRGRP | S_IROTH) 65*0Sstevel@tonic-gate #define ROOT 0 /* user-id of super-user */ 66*0Sstevel@tonic-gate #define MAXTRYS 100 /* max trys to create at job file */ 67*0Sstevel@tonic-gate 68*0Sstevel@tonic-gate #define BADTIME "bad time specification" 69*0Sstevel@tonic-gate #define BADQUEUE "queue name must be a single character a-z" 70*0Sstevel@tonic-gate #define NOTCQUEUE "queue c is reserved for cron entries" 71*0Sstevel@tonic-gate #define BADSHELL "because your login shell isn't /usr/bin/sh,"\ 72*0Sstevel@tonic-gate "you can't use at" 73*0Sstevel@tonic-gate #define WARNSHELL "commands will be executed using %s\n" 74*0Sstevel@tonic-gate #define CANTCD "can't change directory to the at directory" 75*0Sstevel@tonic-gate #define CANTCHOWN "can't change the owner of your job to you" 76*0Sstevel@tonic-gate #define CANTCREATE "can't create a job for you" 77*0Sstevel@tonic-gate #define INVALIDUSER "you are not a valid user (no entry in /etc/passwd)" 78*0Sstevel@tonic-gate #define NOOPENDIR "can't open the at directory" 79*0Sstevel@tonic-gate #define NOTALLOWED "you are not authorized to use at. Sorry." 80*0Sstevel@tonic-gate #define USAGE\ 81*0Sstevel@tonic-gate "usage: at [-c|-k|-s] [-m] [-f file] [-p project] [-q queuename] "\ 82*0Sstevel@tonic-gate "-t time\n"\ 83*0Sstevel@tonic-gate " at [-c|-k|-s] [-m] [-f file] [-p project] [-q queuename] "\ 84*0Sstevel@tonic-gate "timespec\n"\ 85*0Sstevel@tonic-gate " at -l [-p project] [-q queuename] [at_job_id...]\n"\ 86*0Sstevel@tonic-gate " at -r at_job_id ...\n" 87*0Sstevel@tonic-gate 88*0Sstevel@tonic-gate #define FORMAT "%a %b %e %H:%M:%S %Y" 89*0Sstevel@tonic-gate 90*0Sstevel@tonic-gate /* Macros used in format for fscanf */ 91*0Sstevel@tonic-gate #define AQ(x) #x 92*0Sstevel@tonic-gate #define BUFFMT(p) AQ(p) 93*0Sstevel@tonic-gate 94*0Sstevel@tonic-gate static int leap(int); 95*0Sstevel@tonic-gate static int atoi_for2(char *); 96*0Sstevel@tonic-gate static int check_queue(char *, int); 97*0Sstevel@tonic-gate static int list_jobs(int, char **, int, int); 98*0Sstevel@tonic-gate static int remove_jobs(int, char **, char *); 99*0Sstevel@tonic-gate static void usage(void); 100*0Sstevel@tonic-gate static void catch(int); 101*0Sstevel@tonic-gate static void copy(char *, FILE *, int); 102*0Sstevel@tonic-gate static void atime(struct tm *, struct tm *); 103*0Sstevel@tonic-gate static int not_this_project(char *); 104*0Sstevel@tonic-gate static char *mkjobname(time_t); 105*0Sstevel@tonic-gate static time_t parse_time(char *); 106*0Sstevel@tonic-gate static time_t gtime(struct tm *); 107*0Sstevel@tonic-gate void atabort(char *); 108*0Sstevel@tonic-gate void yyerror(void); 109*0Sstevel@tonic-gate extern int yyparse(void); 110*0Sstevel@tonic-gate 111*0Sstevel@tonic-gate extern void audit_at_delete(char *, char *, int); 112*0Sstevel@tonic-gate extern int audit_at_create(char *, int); 113*0Sstevel@tonic-gate extern int audit_cron_is_anc_name(char *); 114*0Sstevel@tonic-gate extern int audit_cron_delete_anc_file(char *, char *); 115*0Sstevel@tonic-gate 116*0Sstevel@tonic-gate /* 117*0Sstevel@tonic-gate * Error in getdate(3G) 118*0Sstevel@tonic-gate */ 119*0Sstevel@tonic-gate static char *errlist[] = { 120*0Sstevel@tonic-gate /* 0 */ "", 121*0Sstevel@tonic-gate /* 1 */ "getdate: The DATEMSK environment variable is not set", 122*0Sstevel@tonic-gate /* 2 */ "getdate: Error on \"open\" of the template file", 123*0Sstevel@tonic-gate /* 3 */ "getdate: Error on \"stat\" of the template file", 124*0Sstevel@tonic-gate /* 4 */ "getdate: The template file is not a regular file", 125*0Sstevel@tonic-gate /* 5 */ "getdate: An error is encountered while reading the template", 126*0Sstevel@tonic-gate /* 6 */ "getdate: Malloc(3C) failed", 127*0Sstevel@tonic-gate /* 7 */ "getdate: There is no line in the template that matches the input", 128*0Sstevel@tonic-gate /* 8 */ "getdate: Invalid input specification" 129*0Sstevel@tonic-gate }; 130*0Sstevel@tonic-gate 131*0Sstevel@tonic-gate int gmtflag = 0; 132*0Sstevel@tonic-gate int mday[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 133*0Sstevel@tonic-gate uid_t user; 134*0Sstevel@tonic-gate struct tm *tp, at, rt; 135*0Sstevel@tonic-gate static int cshflag = 0; 136*0Sstevel@tonic-gate static int kshflag = 0; 137*0Sstevel@tonic-gate static int shflag = 0; 138*0Sstevel@tonic-gate static int mflag = 0; 139*0Sstevel@tonic-gate static int pflag = 0; 140*0Sstevel@tonic-gate static char *Shell; 141*0Sstevel@tonic-gate static char *tfname; 142*0Sstevel@tonic-gate static char pname[80]; 143*0Sstevel@tonic-gate static char pname1[80]; 144*0Sstevel@tonic-gate static short jobtype = ATEVENT; /* set to 1 if batch job */ 145*0Sstevel@tonic-gate extern char *argp; 146*0Sstevel@tonic-gate extern int per_errno; 147*0Sstevel@tonic-gate static projid_t project; 148*0Sstevel@tonic-gate 149*0Sstevel@tonic-gate main(argc, argv) 150*0Sstevel@tonic-gate int argc; 151*0Sstevel@tonic-gate char **argv; 152*0Sstevel@tonic-gate { 153*0Sstevel@tonic-gate FILE *inputfile; 154*0Sstevel@tonic-gate int i, fd; 155*0Sstevel@tonic-gate int try = 0; 156*0Sstevel@tonic-gate int fflag = 0; 157*0Sstevel@tonic-gate int lflag = 0; 158*0Sstevel@tonic-gate int qflag = 0; 159*0Sstevel@tonic-gate int rflag = 0; 160*0Sstevel@tonic-gate int tflag = 0; 161*0Sstevel@tonic-gate int c; 162*0Sstevel@tonic-gate int tflen; 163*0Sstevel@tonic-gate char *file; 164*0Sstevel@tonic-gate char *login; 165*0Sstevel@tonic-gate char *job; 166*0Sstevel@tonic-gate char *jobfile = NULL; /* file containing job to be run */ 167*0Sstevel@tonic-gate char argpbuf[LINE_MAX], timebuf[80]; 168*0Sstevel@tonic-gate time_t now; 169*0Sstevel@tonic-gate time_t when = 0; 170*0Sstevel@tonic-gate struct tm *ct; 171*0Sstevel@tonic-gate char *proj; 172*0Sstevel@tonic-gate struct project prj, *pprj; 173*0Sstevel@tonic-gate char mybuf[PROJECT_BUFSZ]; 174*0Sstevel@tonic-gate char ipbuf[PROJECT_BUFSZ]; 175*0Sstevel@tonic-gate 176*0Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 177*0Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 178*0Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 179*0Sstevel@tonic-gate #endif 180*0Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 181*0Sstevel@tonic-gate 182*0Sstevel@tonic-gate user = getuid(); 183*0Sstevel@tonic-gate login = getuser(user); 184*0Sstevel@tonic-gate if (login == NULL) { 185*0Sstevel@tonic-gate if (per_errno == 2) 186*0Sstevel@tonic-gate atabort(BADSHELL); 187*0Sstevel@tonic-gate else 188*0Sstevel@tonic-gate atabort(INVALIDUSER); 189*0Sstevel@tonic-gate } 190*0Sstevel@tonic-gate 191*0Sstevel@tonic-gate if (!allowed(login, ATALLOW, ATDENY)) 192*0Sstevel@tonic-gate atabort(NOTALLOWED); 193*0Sstevel@tonic-gate 194*0Sstevel@tonic-gate while ((c = getopt(argc, argv, "cklmsrf:p:q:t:")) != EOF) 195*0Sstevel@tonic-gate switch (c) { 196*0Sstevel@tonic-gate case 'c': 197*0Sstevel@tonic-gate cshflag++; 198*0Sstevel@tonic-gate break; 199*0Sstevel@tonic-gate case 'f': 200*0Sstevel@tonic-gate fflag++; 201*0Sstevel@tonic-gate jobfile = optarg; 202*0Sstevel@tonic-gate break; 203*0Sstevel@tonic-gate case 'k': 204*0Sstevel@tonic-gate kshflag++; 205*0Sstevel@tonic-gate break; 206*0Sstevel@tonic-gate case 'l': 207*0Sstevel@tonic-gate lflag++; 208*0Sstevel@tonic-gate break; 209*0Sstevel@tonic-gate case 'm': 210*0Sstevel@tonic-gate mflag++; 211*0Sstevel@tonic-gate break; 212*0Sstevel@tonic-gate case 'p': 213*0Sstevel@tonic-gate proj = optarg; 214*0Sstevel@tonic-gate pprj = &prj; 215*0Sstevel@tonic-gate if ((pprj = getprojbyname(proj, pprj, 216*0Sstevel@tonic-gate (void *)&mybuf, sizeof (mybuf))) != NULL) { 217*0Sstevel@tonic-gate project = pprj->pj_projid; 218*0Sstevel@tonic-gate if (inproj(login, pprj->pj_name, 219*0Sstevel@tonic-gate (void *)&ipbuf, sizeof (ipbuf))) 220*0Sstevel@tonic-gate pflag++; 221*0Sstevel@tonic-gate else { 222*0Sstevel@tonic-gate (void) fprintf(stderr, 223*0Sstevel@tonic-gate gettext("at: user %s is " 224*0Sstevel@tonic-gate "not a member of " 225*0Sstevel@tonic-gate "project %s (%d)\n"), 226*0Sstevel@tonic-gate login, pprj->pj_name, 227*0Sstevel@tonic-gate project); 228*0Sstevel@tonic-gate exit(2); 229*0Sstevel@tonic-gate } 230*0Sstevel@tonic-gate break; 231*0Sstevel@tonic-gate } 232*0Sstevel@tonic-gate pprj = &prj; 233*0Sstevel@tonic-gate if (isdigit(proj[0]) && 234*0Sstevel@tonic-gate (pprj = getprojbyid(atoi(proj), pprj, 235*0Sstevel@tonic-gate (void *)&mybuf, sizeof (mybuf))) != NULL) { 236*0Sstevel@tonic-gate project = pprj->pj_projid; 237*0Sstevel@tonic-gate if (inproj(login, pprj->pj_name, 238*0Sstevel@tonic-gate (void *)&ipbuf, sizeof (ipbuf))) 239*0Sstevel@tonic-gate pflag++; 240*0Sstevel@tonic-gate else { 241*0Sstevel@tonic-gate (void) fprintf(stderr, 242*0Sstevel@tonic-gate gettext("at: user %s is " 243*0Sstevel@tonic-gate "not a member of " 244*0Sstevel@tonic-gate "project %s (%d)\n"), 245*0Sstevel@tonic-gate login, pprj->pj_name, 246*0Sstevel@tonic-gate project); 247*0Sstevel@tonic-gate exit(2); 248*0Sstevel@tonic-gate } 249*0Sstevel@tonic-gate break; 250*0Sstevel@tonic-gate } 251*0Sstevel@tonic-gate (void) fprintf(stderr, gettext("at: project " 252*0Sstevel@tonic-gate "%s not found.\n"), proj); 253*0Sstevel@tonic-gate exit(2); 254*0Sstevel@tonic-gate break; 255*0Sstevel@tonic-gate case 'q': 256*0Sstevel@tonic-gate qflag++; 257*0Sstevel@tonic-gate if (optarg[1] != '\0') 258*0Sstevel@tonic-gate atabort(BADQUEUE); 259*0Sstevel@tonic-gate jobtype = *optarg - 'a'; 260*0Sstevel@tonic-gate if ((jobtype < 0) || (jobtype > 25)) 261*0Sstevel@tonic-gate atabort(BADQUEUE); 262*0Sstevel@tonic-gate if (jobtype == 2) 263*0Sstevel@tonic-gate atabort(NOTCQUEUE); 264*0Sstevel@tonic-gate break; 265*0Sstevel@tonic-gate case 'r': 266*0Sstevel@tonic-gate rflag++; 267*0Sstevel@tonic-gate break; 268*0Sstevel@tonic-gate case 's': 269*0Sstevel@tonic-gate shflag++; 270*0Sstevel@tonic-gate break; 271*0Sstevel@tonic-gate case 't': 272*0Sstevel@tonic-gate tflag++; 273*0Sstevel@tonic-gate when = parse_time(optarg); 274*0Sstevel@tonic-gate break; 275*0Sstevel@tonic-gate default: 276*0Sstevel@tonic-gate usage(); 277*0Sstevel@tonic-gate } 278*0Sstevel@tonic-gate 279*0Sstevel@tonic-gate argc -= optind; 280*0Sstevel@tonic-gate argv += optind; 281*0Sstevel@tonic-gate 282*0Sstevel@tonic-gate if (lflag + rflag > 1) 283*0Sstevel@tonic-gate usage(); 284*0Sstevel@tonic-gate 285*0Sstevel@tonic-gate if (lflag) { 286*0Sstevel@tonic-gate if (cshflag || kshflag || shflag || mflag || 287*0Sstevel@tonic-gate fflag || tflag || rflag) 288*0Sstevel@tonic-gate usage(); 289*0Sstevel@tonic-gate return (list_jobs(argc, argv, qflag, jobtype)); 290*0Sstevel@tonic-gate } 291*0Sstevel@tonic-gate 292*0Sstevel@tonic-gate if (rflag) { 293*0Sstevel@tonic-gate if (cshflag || kshflag || shflag || mflag || 294*0Sstevel@tonic-gate fflag || tflag || qflag) 295*0Sstevel@tonic-gate usage(); 296*0Sstevel@tonic-gate return (remove_jobs(argc, argv, login)); 297*0Sstevel@tonic-gate } 298*0Sstevel@tonic-gate 299*0Sstevel@tonic-gate if ((argc + tflag == 0) && (jobtype != BATCHEVENT)) 300*0Sstevel@tonic-gate usage(); 301*0Sstevel@tonic-gate 302*0Sstevel@tonic-gate if (cshflag + kshflag + shflag > 1) 303*0Sstevel@tonic-gate atabort("ambiguous shell request"); 304*0Sstevel@tonic-gate 305*0Sstevel@tonic-gate time(&now); 306*0Sstevel@tonic-gate 307*0Sstevel@tonic-gate if (jobtype == BATCHEVENT) 308*0Sstevel@tonic-gate when = now; 309*0Sstevel@tonic-gate 310*0Sstevel@tonic-gate if (when == 0) { /* figure out what time to run the job */ 311*0Sstevel@tonic-gate int argplen = sizeof (argpbuf) - 1; 312*0Sstevel@tonic-gate 313*0Sstevel@tonic-gate argpbuf[0] = '\0'; 314*0Sstevel@tonic-gate argp = argpbuf; 315*0Sstevel@tonic-gate i = 0; 316*0Sstevel@tonic-gate while (i < argc) { 317*0Sstevel@tonic-gate /* guard against buffer overflow */ 318*0Sstevel@tonic-gate argplen -= strlen(argv[i]) + 1; 319*0Sstevel@tonic-gate if (argplen < 0) 320*0Sstevel@tonic-gate atabort(BADTIME); 321*0Sstevel@tonic-gate 322*0Sstevel@tonic-gate strcat(argp, argv[i]); 323*0Sstevel@tonic-gate strcat(argp, " "); 324*0Sstevel@tonic-gate i++; 325*0Sstevel@tonic-gate } 326*0Sstevel@tonic-gate if ((file = getenv("DATEMSK")) == 0 || file[0] == '\0') { 327*0Sstevel@tonic-gate tp = localtime(&now); 328*0Sstevel@tonic-gate /* 329*0Sstevel@tonic-gate * Fix for 1047182 - we have to let yyparse 330*0Sstevel@tonic-gate * check bounds on mday[] first, then fixup 331*0Sstevel@tonic-gate * the leap year case. 332*0Sstevel@tonic-gate */ 333*0Sstevel@tonic-gate yyparse(); 334*0Sstevel@tonic-gate 335*0Sstevel@tonic-gate mday[1] = 28 + leap(at.tm_year); 336*0Sstevel@tonic-gate 337*0Sstevel@tonic-gate if (at.tm_mday > mday[at.tm_mon]) 338*0Sstevel@tonic-gate atabort("bad date"); 339*0Sstevel@tonic-gate 340*0Sstevel@tonic-gate atime(&at, &rt); 341*0Sstevel@tonic-gate when = gtime(&at); 342*0Sstevel@tonic-gate if (!gmtflag) { 343*0Sstevel@tonic-gate when += timezone; 344*0Sstevel@tonic-gate if (localtime(&when)->tm_isdst) 345*0Sstevel@tonic-gate when -= (timezone-altzone); 346*0Sstevel@tonic-gate } 347*0Sstevel@tonic-gate } else { /* DATEMSK is set */ 348*0Sstevel@tonic-gate if ((ct = getdate(argpbuf)) == NULL) 349*0Sstevel@tonic-gate atabort(errlist[getdate_err]); 350*0Sstevel@tonic-gate else 351*0Sstevel@tonic-gate when = mktime(ct); 352*0Sstevel@tonic-gate } 353*0Sstevel@tonic-gate } 354*0Sstevel@tonic-gate 355*0Sstevel@tonic-gate if (when < now) /* time has already past */ 356*0Sstevel@tonic-gate atabort("too late"); 357*0Sstevel@tonic-gate 358*0Sstevel@tonic-gate tflen = strlen(ATDIR) + 1 + strlen(TMPFILE) + 359*0Sstevel@tonic-gate 10 + 1; /* 10 for an INT_MAX pid */ 360*0Sstevel@tonic-gate tfname = xmalloc(tflen); 361*0Sstevel@tonic-gate snprintf(tfname, tflen, "%s/%s%d", ATDIR, TMPFILE, getpid()); 362*0Sstevel@tonic-gate 363*0Sstevel@tonic-gate /* catch INT, HUP, TERM and QUIT signals */ 364*0Sstevel@tonic-gate if (signal(SIGINT, catch) == SIG_IGN) 365*0Sstevel@tonic-gate signal(SIGINT, SIG_IGN); 366*0Sstevel@tonic-gate if (signal(SIGHUP, catch) == SIG_IGN) 367*0Sstevel@tonic-gate signal(SIGHUP, SIG_IGN); 368*0Sstevel@tonic-gate if (signal(SIGQUIT, catch) == SIG_IGN) 369*0Sstevel@tonic-gate signal(SIGQUIT, SIG_IGN); 370*0Sstevel@tonic-gate if (signal(SIGTERM, catch) == SIG_IGN) 371*0Sstevel@tonic-gate signal(SIGTERM, SIG_IGN); 372*0Sstevel@tonic-gate if ((fd = open(tfname, O_CREAT|O_EXCL|O_WRONLY, ATMODE)) < 0) 373*0Sstevel@tonic-gate atabort(CANTCREATE); 374*0Sstevel@tonic-gate if (chown(tfname, user, getgid()) == -1) { 375*0Sstevel@tonic-gate unlink(tfname); 376*0Sstevel@tonic-gate atabort(CANTCHOWN); 377*0Sstevel@tonic-gate } 378*0Sstevel@tonic-gate close(1); 379*0Sstevel@tonic-gate dup(fd); 380*0Sstevel@tonic-gate close(fd); 381*0Sstevel@tonic-gate sprintf(pname, "%s", PROTO); 382*0Sstevel@tonic-gate sprintf(pname1, "%s.%c", PROTO, 'a'+jobtype); 383*0Sstevel@tonic-gate 384*0Sstevel@tonic-gate /* 385*0Sstevel@tonic-gate * Open the input file with the user's permissions. 386*0Sstevel@tonic-gate */ 387*0Sstevel@tonic-gate if (jobfile != NULL) { 388*0Sstevel@tonic-gate if ((seteuid(user) < 0) || 389*0Sstevel@tonic-gate (inputfile = fopen(jobfile, "r")) == NULL) { 390*0Sstevel@tonic-gate unlink(tfname); 391*0Sstevel@tonic-gate fprintf(stderr, "at: %s: %s\n", jobfile, errmsg(errno)); 392*0Sstevel@tonic-gate exit(1); 393*0Sstevel@tonic-gate } 394*0Sstevel@tonic-gate else 395*0Sstevel@tonic-gate seteuid(0); 396*0Sstevel@tonic-gate } else 397*0Sstevel@tonic-gate inputfile = stdin; 398*0Sstevel@tonic-gate 399*0Sstevel@tonic-gate copy(jobfile, inputfile, when); 400*0Sstevel@tonic-gate while (rename(tfname, job = mkjobname(when)) == -1) { 401*0Sstevel@tonic-gate sleep(1); 402*0Sstevel@tonic-gate if (++try > MAXTRYS / 10) { 403*0Sstevel@tonic-gate unlink(tfname); 404*0Sstevel@tonic-gate atabort(CANTCREATE); 405*0Sstevel@tonic-gate } 406*0Sstevel@tonic-gate } 407*0Sstevel@tonic-gate unlink(tfname); 408*0Sstevel@tonic-gate if (audit_at_create(job, 0)) 409*0Sstevel@tonic-gate atabort(CANTCREATE); 410*0Sstevel@tonic-gate 411*0Sstevel@tonic-gate cron_sendmsg(ADD, login, strrchr(job, '/')+1, AT); 412*0Sstevel@tonic-gate if (per_errno == 2) 413*0Sstevel@tonic-gate fprintf(stderr, gettext(WARNSHELL), Shell); 414*0Sstevel@tonic-gate cftime(timebuf, FORMAT, &when); 415*0Sstevel@tonic-gate fprintf(stderr, gettext("job %s at %s\n"), 416*0Sstevel@tonic-gate strrchr(job, '/')+1, timebuf); 417*0Sstevel@tonic-gate if (when - MINUTE < HOUR) 418*0Sstevel@tonic-gate fprintf(stderr, gettext( 419*0Sstevel@tonic-gate "at: this job may not be executed at the proper time.\n")); 420*0Sstevel@tonic-gate return (0); 421*0Sstevel@tonic-gate } 422*0Sstevel@tonic-gate 423*0Sstevel@tonic-gate 424*0Sstevel@tonic-gate static char * 425*0Sstevel@tonic-gate mkjobname(t) 426*0Sstevel@tonic-gate time_t t; 427*0Sstevel@tonic-gate { 428*0Sstevel@tonic-gate int i, fd; 429*0Sstevel@tonic-gate char *name; 430*0Sstevel@tonic-gate 431*0Sstevel@tonic-gate name = xmalloc(200); 432*0Sstevel@tonic-gate for (i = 0; i < MAXTRYS; i++) { 433*0Sstevel@tonic-gate sprintf(name, "%s/%ld.%c", ATDIR, t, 'a'+jobtype); 434*0Sstevel@tonic-gate /* fix for 1099183, 1116833 - create file here, avoid race */ 435*0Sstevel@tonic-gate if ((fd = open(name, O_CREAT | O_EXCL, ATMODE)) > 0) { 436*0Sstevel@tonic-gate close(fd); 437*0Sstevel@tonic-gate return (name); 438*0Sstevel@tonic-gate } 439*0Sstevel@tonic-gate t += 1; 440*0Sstevel@tonic-gate } 441*0Sstevel@tonic-gate atabort("queue full"); 442*0Sstevel@tonic-gate } 443*0Sstevel@tonic-gate 444*0Sstevel@tonic-gate 445*0Sstevel@tonic-gate static void 446*0Sstevel@tonic-gate catch(int x) 447*0Sstevel@tonic-gate { 448*0Sstevel@tonic-gate unlink(tfname); 449*0Sstevel@tonic-gate exit(1); 450*0Sstevel@tonic-gate } 451*0Sstevel@tonic-gate 452*0Sstevel@tonic-gate 453*0Sstevel@tonic-gate void 454*0Sstevel@tonic-gate atabort(msg) 455*0Sstevel@tonic-gate char *msg; 456*0Sstevel@tonic-gate { 457*0Sstevel@tonic-gate fprintf(stderr, "at: %s\n", gettext(msg)); 458*0Sstevel@tonic-gate 459*0Sstevel@tonic-gate exit(1); 460*0Sstevel@tonic-gate } 461*0Sstevel@tonic-gate 462*0Sstevel@tonic-gate yywrap(void) 463*0Sstevel@tonic-gate { 464*0Sstevel@tonic-gate return (1); 465*0Sstevel@tonic-gate } 466*0Sstevel@tonic-gate 467*0Sstevel@tonic-gate void 468*0Sstevel@tonic-gate yyerror(void) 469*0Sstevel@tonic-gate { 470*0Sstevel@tonic-gate atabort(BADTIME); 471*0Sstevel@tonic-gate } 472*0Sstevel@tonic-gate 473*0Sstevel@tonic-gate /* 474*0Sstevel@tonic-gate * add time structures logically 475*0Sstevel@tonic-gate */ 476*0Sstevel@tonic-gate static void 477*0Sstevel@tonic-gate atime(struct tm *a, struct tm *b) 478*0Sstevel@tonic-gate { 479*0Sstevel@tonic-gate if ((a->tm_sec += b->tm_sec) >= 60) { 480*0Sstevel@tonic-gate b->tm_min += a->tm_sec / 60; 481*0Sstevel@tonic-gate a->tm_sec %= 60; 482*0Sstevel@tonic-gate } 483*0Sstevel@tonic-gate if ((a->tm_min += b->tm_min) >= 60) { 484*0Sstevel@tonic-gate b->tm_hour += a->tm_min / 60; 485*0Sstevel@tonic-gate a->tm_min %= 60; 486*0Sstevel@tonic-gate } 487*0Sstevel@tonic-gate if ((a->tm_hour += b->tm_hour) >= 24) { 488*0Sstevel@tonic-gate b->tm_mday += a->tm_hour / 24; 489*0Sstevel@tonic-gate a->tm_hour %= 24; 490*0Sstevel@tonic-gate } 491*0Sstevel@tonic-gate a->tm_year += b->tm_year; 492*0Sstevel@tonic-gate if ((a->tm_mon += b->tm_mon) >= 12) { 493*0Sstevel@tonic-gate a->tm_year += a->tm_mon / 12; 494*0Sstevel@tonic-gate a->tm_mon %= 12; 495*0Sstevel@tonic-gate } 496*0Sstevel@tonic-gate a->tm_mday += b->tm_mday; 497*0Sstevel@tonic-gate mday[1] = 28 + leap(a->tm_year); 498*0Sstevel@tonic-gate while (a->tm_mday > mday[a->tm_mon]) { 499*0Sstevel@tonic-gate a->tm_mday -= mday[a->tm_mon++]; 500*0Sstevel@tonic-gate if (a->tm_mon > 11) { 501*0Sstevel@tonic-gate a->tm_mon = 0; 502*0Sstevel@tonic-gate mday[1] = 28 + leap(++a->tm_year); 503*0Sstevel@tonic-gate } 504*0Sstevel@tonic-gate } 505*0Sstevel@tonic-gate 506*0Sstevel@tonic-gate } 507*0Sstevel@tonic-gate 508*0Sstevel@tonic-gate static int 509*0Sstevel@tonic-gate leap(int year) 510*0Sstevel@tonic-gate { 511*0Sstevel@tonic-gate return (isleap(year + TM_YEAR_BASE)); 512*0Sstevel@tonic-gate } 513*0Sstevel@tonic-gate 514*0Sstevel@tonic-gate /* 515*0Sstevel@tonic-gate * return time from time structure 516*0Sstevel@tonic-gate */ 517*0Sstevel@tonic-gate static time_t 518*0Sstevel@tonic-gate gtime(tptr) 519*0Sstevel@tonic-gate struct tm *tptr; 520*0Sstevel@tonic-gate { 521*0Sstevel@tonic-gate register i; 522*0Sstevel@tonic-gate long tv; 523*0Sstevel@tonic-gate int dmsize[12] = 524*0Sstevel@tonic-gate {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 525*0Sstevel@tonic-gate 526*0Sstevel@tonic-gate 527*0Sstevel@tonic-gate tv = 0; 528*0Sstevel@tonic-gate for (i = 1970; i != tptr->tm_year+TM_YEAR_BASE; i++) 529*0Sstevel@tonic-gate tv += (365 + isleap(i)); 530*0Sstevel@tonic-gate /* 531*0Sstevel@tonic-gate * We call isleap since leap() adds 532*0Sstevel@tonic-gate * 1900 onto any value passed 533*0Sstevel@tonic-gate */ 534*0Sstevel@tonic-gate 535*0Sstevel@tonic-gate if (!leap(tptr->tm_year) && at.tm_mday == 29 && at.tm_mon == 1) 536*0Sstevel@tonic-gate atabort("bad date - not a leap year"); 537*0Sstevel@tonic-gate 538*0Sstevel@tonic-gate if ((leap(tptr->tm_year)) && tptr->tm_mon >= 2) 539*0Sstevel@tonic-gate ++tv; 540*0Sstevel@tonic-gate 541*0Sstevel@tonic-gate for (i = 0; i < tptr->tm_mon; ++i) 542*0Sstevel@tonic-gate tv += dmsize[i]; 543*0Sstevel@tonic-gate tv += tptr->tm_mday - 1; 544*0Sstevel@tonic-gate tv = 24 * tv + tptr->tm_hour; 545*0Sstevel@tonic-gate tv = 60 * tv + tptr->tm_min; 546*0Sstevel@tonic-gate tv = 60 * tv + tptr->tm_sec; 547*0Sstevel@tonic-gate return (tv); 548*0Sstevel@tonic-gate } 549*0Sstevel@tonic-gate 550*0Sstevel@tonic-gate /* 551*0Sstevel@tonic-gate * make job file from proto + stdin 552*0Sstevel@tonic-gate */ 553*0Sstevel@tonic-gate static void 554*0Sstevel@tonic-gate copy(char *jobfile, FILE *inputfile, int when) 555*0Sstevel@tonic-gate { 556*0Sstevel@tonic-gate register c; 557*0Sstevel@tonic-gate register FILE *pfp; 558*0Sstevel@tonic-gate register FILE *xfp; 559*0Sstevel@tonic-gate char *shell; 560*0Sstevel@tonic-gate char dirbuf[PATH_MAX + 1]; 561*0Sstevel@tonic-gate char line[LINE_MAX]; 562*0Sstevel@tonic-gate register char **ep; 563*0Sstevel@tonic-gate mode_t um; 564*0Sstevel@tonic-gate char *val; 565*0Sstevel@tonic-gate extern char **environ; 566*0Sstevel@tonic-gate int pfd[2]; 567*0Sstevel@tonic-gate pid_t pid; 568*0Sstevel@tonic-gate uid_t realusr; 569*0Sstevel@tonic-gate int ttyinput; 570*0Sstevel@tonic-gate int ulimit_flag = 0; 571*0Sstevel@tonic-gate struct rlimit rlp; 572*0Sstevel@tonic-gate struct project prj, *pprj; 573*0Sstevel@tonic-gate char pbuf[PROJECT_BUFSZ]; 574*0Sstevel@tonic-gate char pbuf2[PROJECT_BUFSZ]; 575*0Sstevel@tonic-gate char *user; 576*0Sstevel@tonic-gate 577*0Sstevel@tonic-gate /* 578*0Sstevel@tonic-gate * Fix for 1099381: 579*0Sstevel@tonic-gate * If the inputfile is from a tty, then turn on prompting, and 580*0Sstevel@tonic-gate * put out a prompt now, instead of waiting for a lot of file 581*0Sstevel@tonic-gate * activity to complete. 582*0Sstevel@tonic-gate */ 583*0Sstevel@tonic-gate ttyinput = isatty(fileno(inputfile)); 584*0Sstevel@tonic-gate if (ttyinput) { 585*0Sstevel@tonic-gate fputs("at> ", stderr); 586*0Sstevel@tonic-gate fflush(stderr); 587*0Sstevel@tonic-gate } 588*0Sstevel@tonic-gate 589*0Sstevel@tonic-gate /* 590*0Sstevel@tonic-gate * Fix for 1053807: 591*0Sstevel@tonic-gate * Determine what shell we should use to run the job. If the user 592*0Sstevel@tonic-gate * didn't explicitly request that his/her current shell be over- 593*0Sstevel@tonic-gate * ridden (shflag or cshflag), then we use the current shell. 594*0Sstevel@tonic-gate */ 595*0Sstevel@tonic-gate if (cshflag) 596*0Sstevel@tonic-gate Shell = shell = "/bin/csh"; 597*0Sstevel@tonic-gate else if (kshflag) { 598*0Sstevel@tonic-gate Shell = shell = "/bin/ksh"; 599*0Sstevel@tonic-gate ulimit_flag = 1; 600*0Sstevel@tonic-gate } else if (shflag) { 601*0Sstevel@tonic-gate Shell = shell = "/bin/sh"; 602*0Sstevel@tonic-gate ulimit_flag = 1; 603*0Sstevel@tonic-gate } else if (((Shell = val = getenv("SHELL")) != NULL) && 604*0Sstevel@tonic-gate (*val != '\0')) { 605*0Sstevel@tonic-gate shell = "$SHELL"; 606*0Sstevel@tonic-gate if ((strstr(val, "/sh") != NULL) || 607*0Sstevel@tonic-gate (strstr(val, "/ksh") != NULL)) 608*0Sstevel@tonic-gate ulimit_flag = 1; 609*0Sstevel@tonic-gate } else { 610*0Sstevel@tonic-gate /* SHELL is NULL or unset, therefore use default */ 611*0Sstevel@tonic-gate #ifdef XPG4 612*0Sstevel@tonic-gate Shell = shell = "/usr/xpg4/bin/sh"; 613*0Sstevel@tonic-gate #else 614*0Sstevel@tonic-gate Shell = shell = "/bin/sh"; 615*0Sstevel@tonic-gate #endif /* XPG4 */ 616*0Sstevel@tonic-gate ulimit_flag = 1; 617*0Sstevel@tonic-gate } 618*0Sstevel@tonic-gate 619*0Sstevel@tonic-gate printf(": %s job\n", jobtype ? "batch" : "at"); 620*0Sstevel@tonic-gate printf(": jobname: %.127s\n", (jobfile == NULL) ? "stdin" : jobfile); 621*0Sstevel@tonic-gate printf(": notify by mail: %s\n", (mflag) ? "yes" : "no"); 622*0Sstevel@tonic-gate 623*0Sstevel@tonic-gate if (pflag) { 624*0Sstevel@tonic-gate (void) printf(": project: %d\n", project); 625*0Sstevel@tonic-gate } else { 626*0Sstevel@tonic-gate /* 627*0Sstevel@tonic-gate * Check if current user is a member of current project. 628*0Sstevel@tonic-gate * This check is done here to avoid setproject() failure 629*0Sstevel@tonic-gate * later when the job gets executed. If current user does 630*0Sstevel@tonic-gate * not belong to current project, user's default project 631*0Sstevel@tonic-gate * will be used instead. This is achieved by not specifying 632*0Sstevel@tonic-gate * the project (": project: <project>\n") in the job file. 633*0Sstevel@tonic-gate */ 634*0Sstevel@tonic-gate if ((user = getuser(getuid())) == NULL) 635*0Sstevel@tonic-gate atabort(INVALIDUSER); 636*0Sstevel@tonic-gate project = getprojid(); 637*0Sstevel@tonic-gate pprj = getprojbyid(project, &prj, pbuf, sizeof (pbuf)); 638*0Sstevel@tonic-gate if (pprj != NULL) { 639*0Sstevel@tonic-gate if (inproj(user, pprj->pj_name, pbuf2, sizeof (pbuf2))) 640*0Sstevel@tonic-gate (void) printf(": project: %d\n", project); 641*0Sstevel@tonic-gate } 642*0Sstevel@tonic-gate } 643*0Sstevel@tonic-gate 644*0Sstevel@tonic-gate for (ep = environ; *ep; ep++) { 645*0Sstevel@tonic-gate if (strchr(*ep, '\'') != NULL) 646*0Sstevel@tonic-gate continue; 647*0Sstevel@tonic-gate if ((val = strchr(*ep, '=')) == NULL) 648*0Sstevel@tonic-gate continue; 649*0Sstevel@tonic-gate *val++ = '\0'; 650*0Sstevel@tonic-gate printf("export %s; %s='%s'\n", *ep, *ep, val); 651*0Sstevel@tonic-gate *--val = '='; 652*0Sstevel@tonic-gate } 653*0Sstevel@tonic-gate if ((pfp = fopen(pname1, "r")) == NULL && 654*0Sstevel@tonic-gate (pfp = fopen(pname, "r")) == NULL) 655*0Sstevel@tonic-gate atabort("no prototype"); 656*0Sstevel@tonic-gate /* 657*0Sstevel@tonic-gate * Put in a line to run the proper shell using the rest of 658*0Sstevel@tonic-gate * the file as input. Note that 'exec'ing the shell will 659*0Sstevel@tonic-gate * cause sh() to leave a /tmp/sh### file around. (1053807) 660*0Sstevel@tonic-gate */ 661*0Sstevel@tonic-gate printf("%s << '...the rest of this file is shell input'\n", shell); 662*0Sstevel@tonic-gate 663*0Sstevel@tonic-gate um = umask(0); 664*0Sstevel@tonic-gate while ((c = getc(pfp)) != EOF) { 665*0Sstevel@tonic-gate if (c != '$') 666*0Sstevel@tonic-gate putchar(c); 667*0Sstevel@tonic-gate else switch (c = getc(pfp)) { 668*0Sstevel@tonic-gate case EOF: 669*0Sstevel@tonic-gate goto out; 670*0Sstevel@tonic-gate case 'd': 671*0Sstevel@tonic-gate /* 672*0Sstevel@tonic-gate * fork off a child with submitter's permissions, 673*0Sstevel@tonic-gate * otherwise, when IFS=/, /usr/bin/pwd would be parsed 674*0Sstevel@tonic-gate * by the shell as file "bin". The shell would 675*0Sstevel@tonic-gate * then search according to the submitter's PATH 676*0Sstevel@tonic-gate * and run the file bin with root permission 677*0Sstevel@tonic-gate */ 678*0Sstevel@tonic-gate 679*0Sstevel@tonic-gate (void) fflush(stdout); 680*0Sstevel@tonic-gate dirbuf[0] = NULL; 681*0Sstevel@tonic-gate if (pipe(pfd) != 0) 682*0Sstevel@tonic-gate atabort("pipe open failed"); 683*0Sstevel@tonic-gate realusr = getuid(); /* get realusr before the fork */ 684*0Sstevel@tonic-gate if ((pid = fork()) == (pid_t)-1) 685*0Sstevel@tonic-gate atabort("fork failed"); 686*0Sstevel@tonic-gate if (pid == 0) { /* child process */ 687*0Sstevel@tonic-gate (void) close(pfd[0]); 688*0Sstevel@tonic-gate /* remove setuid for pwd */ 689*0Sstevel@tonic-gate (void) setuid(realusr); 690*0Sstevel@tonic-gate if ((xfp = popen("/usr/bin/pwd", "r")) 691*0Sstevel@tonic-gate != NULL) { 692*0Sstevel@tonic-gate fscanf(xfp, "%" BUFFMT(PATH_MAX) "s", 693*0Sstevel@tonic-gate dirbuf); 694*0Sstevel@tonic-gate (void) pclose(xfp); 695*0Sstevel@tonic-gate xfp = fdopen(pfd[1], "w"); 696*0Sstevel@tonic-gate fprintf(xfp, "%s", dirbuf); 697*0Sstevel@tonic-gate (void) fclose(xfp); 698*0Sstevel@tonic-gate } 699*0Sstevel@tonic-gate _exit(0); 700*0Sstevel@tonic-gate } 701*0Sstevel@tonic-gate (void) close(pfd[1]); /* parent process */ 702*0Sstevel@tonic-gate xfp = fdopen(pfd[0], "r"); 703*0Sstevel@tonic-gate fscanf(xfp, "%" BUFFMT(PATH_MAX) "s", dirbuf); 704*0Sstevel@tonic-gate printf("%s", dirbuf); 705*0Sstevel@tonic-gate (void) fclose(xfp); 706*0Sstevel@tonic-gate break; 707*0Sstevel@tonic-gate case 'm': 708*0Sstevel@tonic-gate printf("%o", um); 709*0Sstevel@tonic-gate break; 710*0Sstevel@tonic-gate case '<': 711*0Sstevel@tonic-gate if (ulimit_flag) { 712*0Sstevel@tonic-gate if (getrlimit(RLIMIT_FSIZE, &rlp) == 0) { 713*0Sstevel@tonic-gate if (rlp.rlim_cur == RLIM_INFINITY) 714*0Sstevel@tonic-gate printf("ulimit unlimited\n"); 715*0Sstevel@tonic-gate else 716*0Sstevel@tonic-gate printf("ulimit %lld\n", 717*0Sstevel@tonic-gate rlp.rlim_cur / 512); 718*0Sstevel@tonic-gate } 719*0Sstevel@tonic-gate } 720*0Sstevel@tonic-gate /* 721*0Sstevel@tonic-gate * fix for 1113572 - use fputs() so that a 722*0Sstevel@tonic-gate * newline isn't appended to the one returned 723*0Sstevel@tonic-gate * with fgets(); 1099381 - prompt for input. 724*0Sstevel@tonic-gate */ 725*0Sstevel@tonic-gate while (fgets(line, LINE_MAX, inputfile) != NULL) { 726*0Sstevel@tonic-gate fputs(line, stdout); 727*0Sstevel@tonic-gate if (ttyinput) 728*0Sstevel@tonic-gate fputs("at> ", stderr); 729*0Sstevel@tonic-gate } 730*0Sstevel@tonic-gate if (ttyinput) /* clean up the final output */ 731*0Sstevel@tonic-gate fputs("<EOT>\n", stderr); 732*0Sstevel@tonic-gate break; 733*0Sstevel@tonic-gate case 't': 734*0Sstevel@tonic-gate printf(":%lu", when); 735*0Sstevel@tonic-gate break; 736*0Sstevel@tonic-gate default: 737*0Sstevel@tonic-gate putchar(c); 738*0Sstevel@tonic-gate } 739*0Sstevel@tonic-gate } 740*0Sstevel@tonic-gate out: 741*0Sstevel@tonic-gate fclose(pfp); 742*0Sstevel@tonic-gate fflush(NULL); 743*0Sstevel@tonic-gate } 744*0Sstevel@tonic-gate 745*0Sstevel@tonic-gate static int 746*0Sstevel@tonic-gate remove_jobs(int argc, char **argv, char *login) 747*0Sstevel@tonic-gate /* remove jobs that are specified */ 748*0Sstevel@tonic-gate { 749*0Sstevel@tonic-gate int i, r; 750*0Sstevel@tonic-gate int error = 0; 751*0Sstevel@tonic-gate struct stat buf; 752*0Sstevel@tonic-gate struct passwd *pw; 753*0Sstevel@tonic-gate 754*0Sstevel@tonic-gate pw = getpwuid(user); 755*0Sstevel@tonic-gate if (pw == NULL) { 756*0Sstevel@tonic-gate atabort("Invalid user.\n"); 757*0Sstevel@tonic-gate } 758*0Sstevel@tonic-gate 759*0Sstevel@tonic-gate if (argc == 0) 760*0Sstevel@tonic-gate usage(); 761*0Sstevel@tonic-gate if (chdir(ATDIR) == -1) 762*0Sstevel@tonic-gate atabort(CANTCD); 763*0Sstevel@tonic-gate for (i = 0; i < argc; i++) 764*0Sstevel@tonic-gate if (strchr(argv[i], '/') != NULL) { 765*0Sstevel@tonic-gate fprintf(stderr, "at: %s: not a valid job-id\n", 766*0Sstevel@tonic-gate argv[i]); 767*0Sstevel@tonic-gate } else if (stat(argv[i], &buf)) { 768*0Sstevel@tonic-gate fprintf(stderr, "at: %s: ", argv[i]); 769*0Sstevel@tonic-gate perror(""); 770*0Sstevel@tonic-gate } else if ((user != buf.st_uid) && 771*0Sstevel@tonic-gate (!chkauthattr(CRONADMIN_AUTH, pw->pw_name))) { 772*0Sstevel@tonic-gate fprintf(stderr, "at: you don't own %s\n", 773*0Sstevel@tonic-gate argv[i]); 774*0Sstevel@tonic-gate error = 1; 775*0Sstevel@tonic-gate } else { 776*0Sstevel@tonic-gate if (chkauthattr(CRONADMIN_AUTH, pw->pw_name)) { 777*0Sstevel@tonic-gate login = getuser((uid_t)buf.st_uid); 778*0Sstevel@tonic-gate if (login == NULL) { 779*0Sstevel@tonic-gate if (per_errno == 2) 780*0Sstevel@tonic-gate atabort(BADSHELL); 781*0Sstevel@tonic-gate else 782*0Sstevel@tonic-gate atabort(INVALIDUSER); 783*0Sstevel@tonic-gate } 784*0Sstevel@tonic-gate } 785*0Sstevel@tonic-gate cron_sendmsg(DELETE, login, argv[i], AT); 786*0Sstevel@tonic-gate r = unlink(argv[i]); 787*0Sstevel@tonic-gate audit_at_delete(argv[i], ATDIR, r); 788*0Sstevel@tonic-gate } 789*0Sstevel@tonic-gate return (error); 790*0Sstevel@tonic-gate } 791*0Sstevel@tonic-gate 792*0Sstevel@tonic-gate 793*0Sstevel@tonic-gate 794*0Sstevel@tonic-gate static int 795*0Sstevel@tonic-gate list_jobs(int argc, char **argv, int qflag, int queue) 796*0Sstevel@tonic-gate { 797*0Sstevel@tonic-gate DIR *dir; 798*0Sstevel@tonic-gate int i; 799*0Sstevel@tonic-gate int error = 0; 800*0Sstevel@tonic-gate char *patdir, *atdir, *ptr; 801*0Sstevel@tonic-gate char timebuf[80]; 802*0Sstevel@tonic-gate time_t t; 803*0Sstevel@tonic-gate struct stat buf, st1, st2; 804*0Sstevel@tonic-gate struct dirent *dentry; 805*0Sstevel@tonic-gate struct passwd *pw; 806*0Sstevel@tonic-gate unsigned int atdirlen; 807*0Sstevel@tonic-gate int r; 808*0Sstevel@tonic-gate struct passwd *pwd, pwds; 809*0Sstevel@tonic-gate char buf_pwd[1024]; 810*0Sstevel@tonic-gate char job_file[PATH_MAX]; 811*0Sstevel@tonic-gate 812*0Sstevel@tonic-gate pwd = getpwuid_r(user, &pwds, buf_pwd, sizeof (buf_pwd)); 813*0Sstevel@tonic-gate if (pwd == NULL) { 814*0Sstevel@tonic-gate atabort("Invalid user.\n"); 815*0Sstevel@tonic-gate } 816*0Sstevel@tonic-gate 817*0Sstevel@tonic-gate /* list jobs for user */ 818*0Sstevel@tonic-gate if (chdir(ATDIR) == -1) 819*0Sstevel@tonic-gate atabort(CANTCD); 820*0Sstevel@tonic-gate 821*0Sstevel@tonic-gate atdirlen = strlen(ATDIR); 822*0Sstevel@tonic-gate atdir = xmalloc(atdirlen + 1); 823*0Sstevel@tonic-gate strcpy(atdir, ATDIR); 824*0Sstevel@tonic-gate patdir = strrchr(atdir, '/'); 825*0Sstevel@tonic-gate *patdir = '\0'; 826*0Sstevel@tonic-gate if (argc == 0) { 827*0Sstevel@tonic-gate /* list all jobs for a user */ 828*0Sstevel@tonic-gate if (stat(ATDIR, &st1) != 0 || stat(atdir, &st2) != 0) 829*0Sstevel@tonic-gate atabort("Can not get status of spooling" 830*0Sstevel@tonic-gate "directory for at"); 831*0Sstevel@tonic-gate if ((dir = opendir(ATDIR)) == NULL) 832*0Sstevel@tonic-gate atabort(NOOPENDIR); 833*0Sstevel@tonic-gate while (1) { 834*0Sstevel@tonic-gate if ((dentry = readdir(dir)) == NULL) 835*0Sstevel@tonic-gate break; 836*0Sstevel@tonic-gate if ((dentry->d_ino == st1.st_ino) || 837*0Sstevel@tonic-gate (dentry->d_ino == st2.st_ino)) 838*0Sstevel@tonic-gate continue; 839*0Sstevel@tonic-gate if ((r = audit_cron_is_anc_name(dentry->d_name)) == 1) 840*0Sstevel@tonic-gate continue; 841*0Sstevel@tonic-gate if (stat(dentry->d_name, &buf)) { 842*0Sstevel@tonic-gate unlink(dentry->d_name); 843*0Sstevel@tonic-gate audit_cron_delete_anc_file(dentry->d_name, 844*0Sstevel@tonic-gate NULL); 845*0Sstevel@tonic-gate continue; 846*0Sstevel@tonic-gate } 847*0Sstevel@tonic-gate if ((!chkauthattr(CRONADMIN_AUTH, pwd->pw_name)) && 848*0Sstevel@tonic-gate (buf.st_uid != user)) 849*0Sstevel@tonic-gate continue; 850*0Sstevel@tonic-gate ptr = dentry->d_name; 851*0Sstevel@tonic-gate if (((t = num(&ptr)) == 0) || (*ptr != '.')) 852*0Sstevel@tonic-gate continue; 853*0Sstevel@tonic-gate strcpy(job_file, patdir); 854*0Sstevel@tonic-gate strcat(job_file, dentry->d_name); 855*0Sstevel@tonic-gate if (pflag && not_this_project(job_file)) 856*0Sstevel@tonic-gate continue; 857*0Sstevel@tonic-gate ascftime(timebuf, FORMAT, localtime(&t)); 858*0Sstevel@tonic-gate if ((chkauthattr(CRONADMIN_AUTH, pwd->pw_name)) && 859*0Sstevel@tonic-gate ((pw = getpwuid(buf.st_uid)) != NULL)) { 860*0Sstevel@tonic-gate if (!qflag || (qflag && 861*0Sstevel@tonic-gate check_queue(ptr, queue))) 862*0Sstevel@tonic-gate printf("user = %s\t%s\t%s\n", 863*0Sstevel@tonic-gate pw->pw_name, dentry->d_name, 864*0Sstevel@tonic-gate timebuf); 865*0Sstevel@tonic-gate } else 866*0Sstevel@tonic-gate if (!qflag || (qflag && 867*0Sstevel@tonic-gate check_queue(ptr, queue))) 868*0Sstevel@tonic-gate printf("%s\t%s\n", 869*0Sstevel@tonic-gate dentry->d_name, timebuf); 870*0Sstevel@tonic-gate } 871*0Sstevel@tonic-gate (void) closedir(dir); 872*0Sstevel@tonic-gate } else /* list particular jobs for user */ 873*0Sstevel@tonic-gate for (i = 0; i < argc; i++) { 874*0Sstevel@tonic-gate ptr = argv[i]; 875*0Sstevel@tonic-gate strlcpy(job_file, patdir, PATH_MAX); 876*0Sstevel@tonic-gate strlcat(job_file, ptr, PATH_MAX); 877*0Sstevel@tonic-gate if (((t = num(&ptr)) == 0) || (*ptr != '.')) { 878*0Sstevel@tonic-gate fprintf(stderr, gettext( 879*0Sstevel@tonic-gate "at: invalid job name %s\n"), argv[i]); 880*0Sstevel@tonic-gate error = 1; 881*0Sstevel@tonic-gate } else if (stat(argv[i], &buf)) { 882*0Sstevel@tonic-gate fprintf(stderr, "at: %s: ", argv[i]); 883*0Sstevel@tonic-gate perror(""); 884*0Sstevel@tonic-gate error = 1; 885*0Sstevel@tonic-gate } else if ((user != buf.st_uid) && 886*0Sstevel@tonic-gate (!chkauthattr(CRONADMIN_AUTH, pwd->pw_name))) { 887*0Sstevel@tonic-gate fprintf(stderr, gettext( 888*0Sstevel@tonic-gate "at: you don't own %s\n"), argv[i]); 889*0Sstevel@tonic-gate error = 1; 890*0Sstevel@tonic-gate } else if (pflag && not_this_project(job_file)) { 891*0Sstevel@tonic-gate continue; 892*0Sstevel@tonic-gate } else { 893*0Sstevel@tonic-gate if (!qflag || (qflag && 894*0Sstevel@tonic-gate check_queue(ptr, queue))) { 895*0Sstevel@tonic-gate ascftime(timebuf, FORMAT, 896*0Sstevel@tonic-gate localtime(&t)); 897*0Sstevel@tonic-gate printf("%s\t%s\n", argv[i], timebuf); 898*0Sstevel@tonic-gate } 899*0Sstevel@tonic-gate } 900*0Sstevel@tonic-gate } 901*0Sstevel@tonic-gate return (error); 902*0Sstevel@tonic-gate } 903*0Sstevel@tonic-gate 904*0Sstevel@tonic-gate /* 905*0Sstevel@tonic-gate * open the command file and read the project id line 906*0Sstevel@tonic-gate * compare to the project number provided via -p on the command line 907*0Sstevel@tonic-gate * return 0 if they match, 1 if they don't match or an error occurs. 908*0Sstevel@tonic-gate */ 909*0Sstevel@tonic-gate #define SKIPCOUNT 3 /* lines to skip to get to project line in file */ 910*0Sstevel@tonic-gate 911*0Sstevel@tonic-gate static int 912*0Sstevel@tonic-gate not_this_project(char *filename) 913*0Sstevel@tonic-gate { 914*0Sstevel@tonic-gate FILE *fp; 915*0Sstevel@tonic-gate projid_t sproj; 916*0Sstevel@tonic-gate int i; 917*0Sstevel@tonic-gate 918*0Sstevel@tonic-gate if ((fp = fopen(filename, "r")) == NULL) 919*0Sstevel@tonic-gate return (1); 920*0Sstevel@tonic-gate 921*0Sstevel@tonic-gate for (i = 0; i < SKIPCOUNT; i++) 922*0Sstevel@tonic-gate fscanf(fp, "%*[^\n]\n"); 923*0Sstevel@tonic-gate 924*0Sstevel@tonic-gate fscanf(fp, ": project: %d\n", &sproj); 925*0Sstevel@tonic-gate fclose(fp); 926*0Sstevel@tonic-gate 927*0Sstevel@tonic-gate return (sproj == project ? 0 : 1); 928*0Sstevel@tonic-gate } 929*0Sstevel@tonic-gate 930*0Sstevel@tonic-gate static int 931*0Sstevel@tonic-gate check_queue(char *name, int queue) 932*0Sstevel@tonic-gate { 933*0Sstevel@tonic-gate if ((name[strlen(name) - 1] - 'a') == queue) 934*0Sstevel@tonic-gate return (1); 935*0Sstevel@tonic-gate else 936*0Sstevel@tonic-gate return (0); 937*0Sstevel@tonic-gate } 938*0Sstevel@tonic-gate 939*0Sstevel@tonic-gate static time_t 940*0Sstevel@tonic-gate parse_time(char *t) 941*0Sstevel@tonic-gate { 942*0Sstevel@tonic-gate int century = 0; 943*0Sstevel@tonic-gate int seconds = 0; 944*0Sstevel@tonic-gate char *p; 945*0Sstevel@tonic-gate time_t when = 0; 946*0Sstevel@tonic-gate struct tm tm; 947*0Sstevel@tonic-gate 948*0Sstevel@tonic-gate /* 949*0Sstevel@tonic-gate * time in the following format (defined by the touch(1) spec): 950*0Sstevel@tonic-gate * [[CC]YY]MMDDhhmm[.SS] 951*0Sstevel@tonic-gate */ 952*0Sstevel@tonic-gate if ((p = strchr(t, '.')) != NULL) { 953*0Sstevel@tonic-gate if (strchr(p+1, '.') != NULL) 954*0Sstevel@tonic-gate atabort(BADTIME); 955*0Sstevel@tonic-gate seconds = atoi_for2(p+1); 956*0Sstevel@tonic-gate *p = '\0'; 957*0Sstevel@tonic-gate } 958*0Sstevel@tonic-gate 959*0Sstevel@tonic-gate memset(&tm, 0, sizeof (struct tm)); 960*0Sstevel@tonic-gate when = time(0); 961*0Sstevel@tonic-gate tm.tm_year = localtime(&when)->tm_year; 962*0Sstevel@tonic-gate 963*0Sstevel@tonic-gate switch (strlen(t)) { 964*0Sstevel@tonic-gate case 12: /* CCYYMMDDhhmm */ 965*0Sstevel@tonic-gate century = atoi_for2(t); 966*0Sstevel@tonic-gate t += 2; 967*0Sstevel@tonic-gate case 10: /* YYMMDDhhmm */ 968*0Sstevel@tonic-gate tm.tm_year = atoi_for2(t); 969*0Sstevel@tonic-gate t += 2; 970*0Sstevel@tonic-gate if (century == 0) { 971*0Sstevel@tonic-gate if (tm.tm_year < 69) 972*0Sstevel@tonic-gate tm.tm_year += 100; 973*0Sstevel@tonic-gate } else 974*0Sstevel@tonic-gate tm.tm_year += (century - 19) * 100; 975*0Sstevel@tonic-gate case 8: /* MMDDhhmm */ 976*0Sstevel@tonic-gate tm.tm_mon = atoi_for2(t) - 1; 977*0Sstevel@tonic-gate t += 2; 978*0Sstevel@tonic-gate tm.tm_mday = atoi_for2(t); 979*0Sstevel@tonic-gate t += 2; 980*0Sstevel@tonic-gate tm.tm_hour = atoi_for2(t); 981*0Sstevel@tonic-gate t += 2; 982*0Sstevel@tonic-gate tm.tm_min = atoi_for2(t); 983*0Sstevel@tonic-gate t += 2; 984*0Sstevel@tonic-gate tm.tm_sec = seconds; 985*0Sstevel@tonic-gate break; 986*0Sstevel@tonic-gate default: 987*0Sstevel@tonic-gate atabort(BADTIME); 988*0Sstevel@tonic-gate } 989*0Sstevel@tonic-gate 990*0Sstevel@tonic-gate if ((when = mktime(&tm)) == -1) 991*0Sstevel@tonic-gate atabort(BADTIME); 992*0Sstevel@tonic-gate if (tm.tm_isdst) 993*0Sstevel@tonic-gate when -= (timezone-altzone); 994*0Sstevel@tonic-gate return (when); 995*0Sstevel@tonic-gate } 996*0Sstevel@tonic-gate 997*0Sstevel@tonic-gate static int 998*0Sstevel@tonic-gate atoi_for2(char *p) { 999*0Sstevel@tonic-gate int value; 1000*0Sstevel@tonic-gate 1001*0Sstevel@tonic-gate value = (*p - '0') * 10 + *(p+1) - '0'; 1002*0Sstevel@tonic-gate if ((value < 0) || (value > 99)) 1003*0Sstevel@tonic-gate atabort(BADTIME); 1004*0Sstevel@tonic-gate return (value); 1005*0Sstevel@tonic-gate } 1006*0Sstevel@tonic-gate 1007*0Sstevel@tonic-gate static void 1008*0Sstevel@tonic-gate usage(void) 1009*0Sstevel@tonic-gate { 1010*0Sstevel@tonic-gate fprintf(stderr, USAGE); 1011*0Sstevel@tonic-gate exit(1); 1012*0Sstevel@tonic-gate } 1013