1 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 2 /* All Rights Reserved */ 3 4 5 /* 6 * Copyright (c) 1983 Regents of the University of California. 7 * All rights reserved. The Berkeley software License Agreement 8 * specifies the terms and conditions for redistribution. 9 10 * Copyright 1984-2002 Sun Microsystems, Inc. All rights reserved. 11 * Use is subject to license terms. 12 */ 13 14 #pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.5 */ 15 16 /* 17 * synopsis: atrm [-f] [-i] [-a] [[job #] [user] ...] 18 * 19 * 20 * Remove "at" jobs. 21 */ 22 23 #include <stdio.h> 24 #include <pwd.h> 25 #include <ctype.h> 26 #include <sys/types.h> 27 #include <dirent.h> 28 #include <sys/file.h> 29 #include <sys/stat.h> 30 #include <errno.h> 31 #include <unistd.h> 32 #include <locale.h> 33 #include "cron.h" 34 35 extern time_t num(); 36 extern char *errmsg(); 37 extern int errno; 38 39 extern void audit_at_delete(char *, char *, int); 40 41 #define SUPERUSER 0 /* is user super-user? */ 42 #define CANTCD "can't change directory to the at directory" 43 #define NOREADDIR "can't read the at directory" 44 45 uid_t user; /* person requesting removal */ 46 int fflag = 0; /* suppress announcements? */ 47 int iflag = 0; /* run interactively? */ 48 49 char login[UNAMESIZE]; 50 char login_authchk[UNAMESIZE]; /* used for authorization checks */ 51 52 #define INVALIDUSER "you are not a valid user (no entry in /etc/passwd)" 53 #define NOTALLOWED "you are not authorized to use at. Sorry." 54 #define NAMETOOLONG "login name too long" 55 56 main(argc,argv) 57 int argc; 58 char **argv; 59 60 { 61 int i; /* for loop index */ 62 int numjobs; /* # of jobs in spooling area */ 63 int usage(); /* print usage info and exit */ 64 int allflag = 0; /* remove all jobs belonging to user? */ 65 int jobexists; /* does a requested job exist? */ 66 extern int strcmp(); /* sort jobs by date of execution */ 67 char *pp; 68 char *getuser(); 69 struct dirent **namelist; /* names of jobs in spooling area */ 70 struct stat **statlist; 71 struct passwd *pwd; 72 73 /* 74 * If job number, user name, or "-" is not specified, just print 75 * usage info and exit. 76 */ 77 (void)setlocale(LC_ALL, ""); 78 if (argc < 2) 79 usage(); 80 81 --argc; ++argv; 82 83 pp = getuser((user=getuid())); 84 if (pp == NULL) 85 atabort(INVALIDUSER); 86 if (strlcpy(login, pp, sizeof (login)) >= sizeof (login)) 87 atabort(NAMETOOLONG); 88 if (strlcpy(login_authchk, pp, sizeof (login_authchk)) 89 >= sizeof (NAMETOOLONG)) 90 atabort(INVALIDUSER); 91 if (!allowed(login, ATALLOW, ATDENY)) 92 atabort(NOTALLOWED); 93 94 /* 95 * Process command line flags. 96 * Special case the "-" option so that others may be grouped. 97 */ 98 while (argc > 0 && **argv == '-') { 99 *(*argv)++; 100 while (**argv) switch (*(*argv)++) { 101 102 case 'a': ++allflag; 103 break; 104 105 case 'f': ++fflag; 106 break; 107 108 case 'i': ++iflag; 109 break; 110 111 default: usage(); 112 } 113 ++argv; --argc; 114 } 115 116 /* 117 * If all jobs are to be removed and extra command line arguments 118 * are given, print usage info and exit. 119 */ 120 if (allflag && argc) 121 usage(); 122 123 /* 124 * If only certain jobs are to be removed and no job #'s or user 125 * names are specified, print usage info and exit. 126 */ 127 if (!allflag && !argc) 128 usage(); 129 130 /* 131 * If interactive removal and quiet removal are requested, override 132 * quiet removal and run interactively. 133 */ 134 if (iflag && fflag) 135 fflag = 0; 136 137 138 /* 139 * Move to spooling directory and get a list of the files in the 140 * spooling area. 141 */ 142 numjobs = getjoblist(&namelist,&statlist,strcmp); 143 /* 144 * If all jobs belonging to the user are to be removed, compare 145 * the user's id to the owner of the file. If they match, remove 146 * the file. If the user is the super-user, don't bother comparing 147 * the id's. After all files are removed, exit (status 0). 148 */ 149 if (allflag) { 150 for (i = 0; i < numjobs; ++i) { 151 if (chkauthattr(CRONADMIN_AUTH, login_authchk) || 152 user == statlist[i]->st_uid) 153 (void) removentry(namelist[i]->d_name, 154 statlist[i], user); 155 } 156 exit(0); 157 } 158 159 /* 160 * If only certain jobs are to be removed, interpret each command 161 * line argument. A check is done to see if it is a user's name or 162 * a job number (inode #). If it's a user's name, compare the argument 163 * to the files owner. If it's a job number, compare the argument to 164 * the file name. In either case, if a match occurs, try to 165 * remove the file. 166 */ 167 168 while (argc--) { 169 jobexists = 0; 170 for (i = 0; i < numjobs; ++i) { 171 172 /* if the inode number is 0, this entry was removed */ 173 if (statlist[i]->st_ino == 0) 174 continue; 175 176 /* 177 * if argv is a username, compare his/her uid to 178 * the uid of the owner of the file...... 179 */ 180 if (pwd = getpwnam(*argv)) { 181 if (statlist[i]->st_uid != pwd->pw_uid) 182 continue; 183 /* 184 * otherwise, we assume that the argv is a job # and 185 * thus compare argv to the file name. 186 */ 187 } else { 188 if (strcmp(namelist[i]->d_name,*argv)) 189 continue; 190 } 191 ++jobexists; 192 /* 193 * if the entry is ultimately removed, don't 194 * try to remove it again later. 195 */ 196 if (removentry(namelist[i]->d_name, statlist[i], user)) { 197 statlist[i]->st_ino = 0; 198 } 199 } 200 201 /* 202 * If a requested argument doesn't exist, print a message. 203 */ 204 if (!jobexists && !fflag) { 205 fprintf(stderr, "atrm: %s: no such job number\n", *argv); 206 } 207 ++argv; 208 } 209 exit(0); 210 } 211 212 /* 213 * Print usage info and exit. 214 */ 215 usage() 216 { 217 fprintf(stderr,"usage: atrm [-f] [-i] [-a] [[job #] [user] ...]\n"); 218 exit(1); 219 } 220 221 222 /* 223 * Remove an entry from the queue. The access of the file is checked for 224 * write permission (since all jobs are mode 644). If access is granted, 225 * unlink the file. If the fflag (suppress announcements) is not set, 226 * print the job number that we are removing and the result of the access 227 * check (either "permission denied" or "removed"). If we are running 228 * interactively (iflag), prompt the user before we unlink the file. If 229 * the super-user is removing jobs, inform him/her who owns each file before 230 * it is removed. Return TRUE if file removed, else FALSE. 231 */ 232 int 233 removentry(filename,statptr,user) 234 char *filename; 235 register struct stat *statptr; 236 uid_t user; 237 { 238 struct passwd *pwd; 239 char *pp; 240 char *getuser(); 241 int r; 242 243 if (!fflag) 244 printf("%s: ",filename); 245 246 if (user != statptr->st_uid && 247 !chkauthattr(CRONADMIN_AUTH, login_authchk)) { 248 249 if (!fflag) { 250 printf("permission denied\n"); 251 } 252 return (0); 253 254 } else { 255 if (iflag) { 256 if (chkauthattr(CRONADMIN_AUTH, login_authchk)) { 257 printf("\t(owned by "); 258 powner(filename); 259 printf(") "); 260 } 261 printf("remove it? "); 262 if (!yes()) 263 return (0); 264 } 265 266 if (chkauthattr(CRONADMIN_AUTH, login_authchk)) { 267 pp = getuser((uid_t) statptr->st_uid); 268 if (pp == NULL) 269 atabort(INVALIDUSER); 270 if (strlcpy(login, pp, sizeof (login)) >= 271 sizeof (login)) 272 atabort(NAMETOOLONG); 273 } 274 cron_sendmsg(DELETE,login,filename,AT); 275 if ((r = unlink(filename)) < 0) { 276 if (!fflag) { 277 fputs("could not remove\n", stdout); 278 (void) fprintf(stderr, "atrm: %s: %s\n", 279 filename, errmsg(errno)); 280 } 281 audit_at_delete(filename, NULL, r); 282 return (0); 283 } 284 audit_at_delete(filename, NULL, r); 285 if (!fflag && !iflag) 286 printf("removed\n"); 287 return (1); 288 } 289 } 290 291 /* 292 * Print the owner of the job. This is the owner of the spoolfile. 293 * If we run into trouble getting the name, we'll just print "???". 294 */ 295 powner(file) 296 char *file; 297 { 298 struct stat statb; 299 char *getname(); 300 301 if (stat(file,&statb) < 0) { 302 printf("%s","???"); 303 (void) fprintf(stderr,"atrm: Couldn't stat spoolfile %s: %s\n", 304 file, errmsg(errno)); 305 return(0); 306 } 307 308 printf("%s",getname(statb.st_uid)); 309 } 310 311 312 int 313 getjoblist(namelistp, statlistp,sortfunc) 314 struct dirent ***namelistp; 315 struct stat ***statlistp; 316 int (*sortfunc)(); 317 { 318 register int numjobs; 319 register struct dirent **namelist; 320 register int i; 321 register struct stat *statptr; /* pointer to file stat structure */ 322 register struct stat **statlist; 323 extern int alphasort(); /* sort jobs by date of execution */ 324 extern int filewanted(); /* should a file be listed in queue? */ 325 326 if (chdir(ATDIR) < 0) 327 atabortperror(CANTCD); 328 329 /* 330 * Get a list of the files in the spooling area. 331 */ 332 if ((numjobs = ascandir(".",namelistp,filewanted,sortfunc)) < 0) 333 atabortperror(NOREADDIR); 334 335 if ((statlist = (struct stat **) malloc(numjobs * sizeof (struct stat ***))) == NULL) 336 atabort("Out of memory"); 337 338 namelist = *namelistp; 339 340 /* 341 * Build an array of pointers to the file stats for all jobs in 342 * the spooling area. 343 */ 344 for (i = 0; i < numjobs; ++i) { 345 statptr = (struct stat *) malloc(sizeof(struct stat)); 346 if (statptr == NULL) 347 atabort("Out of memory"); 348 if (stat(namelist[i]->d_name, statptr) < 0) { 349 atperror("Can't stat", namelist[i]->d_name); 350 continue; 351 } 352 statlist[i] = statptr; 353 } 354 355 *statlistp = statlist; 356 return (numjobs); 357 } 358 359 360 /* 361 * Get answer to interactive prompts, eating all characters beyond the first 362 * one. If a 'y' is typed, return 1. 363 */ 364 yes() 365 { 366 register int ch; /* dummy variable */ 367 register int ch1; /* dummy variable */ 368 369 ch = ch1 = getchar(); 370 while (ch1 != '\n' && ch1 != EOF) 371 ch1 = getchar(); 372 if (isupper(ch)) 373 ch = tolower(ch); 374 return(ch == 'y'); 375 } 376 377 378 /* 379 * Get the full login name of a person using his/her user id. 380 */ 381 char * 382 getname(uid) 383 uid_t uid; 384 { 385 register struct passwd *pwdinfo; /* password info structure */ 386 387 388 if ((pwdinfo = getpwuid(uid)) == 0) 389 return("???"); 390 return(pwdinfo->pw_name); 391 } 392 393 aterror(msg) 394 char *msg; 395 { 396 fprintf(stderr,"atrm: %s\n",msg); 397 } 398 399 atperror(msg) 400 char *msg; 401 { 402 fprintf(stderr,"atrm: %s: %s\n", msg, errmsg(errno)); 403 } 404 405 atabort(msg) 406 char *msg; 407 { 408 aterror(msg); 409 exit(1); 410 } 411 412 atabortperror(msg) 413 char *msg; 414 { 415 atperror(msg); 416 exit(1); 417 } 418