1: /* 2: * Copyright (c) 1983 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #ifndef lint 8: char copyright[] = 9: "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 10: All rights reserved.\n"; 11: #endif not lint 12: 13: #ifndef lint 14: static char sccsid[] = "@(#)atrm.c 5.2 (Berkeley) 5/28/86"; 15: #endif not lint 16: 17: /* 18: * synopsis: atrm [-f] [-i] [-] [[job #] [user] ...] 19: * 20: * 21: * Remove files from the directory /usr/spool/at. These files 22: * represent jobs to be run at a later date. 23: * 24: * Author: Steve Wall 25: * Computer Systems Research Group 26: * University of California @ Berkeley 27: * 28: */ 29: 30: #include <stdio.h> 31: #include <pwd.h> 32: #include <ctype.h> 33: #include <sys/types.h> 34: #include <sys/dir.h> 35: #include <sys/file.h> 36: #include <sys/stat.h> 37: 38: #define SUPERUSER 0 /* is user super-user? */ 39: #define MAXENTRIES 1000 /* max # of entries allowed */ 40: #define ATDIR "/usr/spool/at" /* spooling area */ 41: 42: 43: int user; /* person requesting removal */ 44: int fflag = 0; /* suppress announcements? */ 45: int iflag = 0; /* run interactively? */ 46: 47: main(argc,argv) 48: int argc; 49: char **argv; 50: 51: { 52: int i; /* for loop index */ 53: int userid; /* uid of owner of file */ 54: int isuname; /* is a command line argv a user name?*/ 55: int numjobs; /* # of jobs in spooling area */ 56: int usage(); /* print usage info and exit */ 57: int allflag = 0; /* remove all jobs belonging to user? */ 58: int jobexists; /* does a requested job exist? */ 59: int alphasort(); /* sort jobs by date of execution */ 60: int filewanted(); /* should a file be listed in queue? */ 61: char *getname(); /* get a user name from a uid */ 62: struct stat *statptr; /* pointer to file stat structure */ 63: struct stat *stbuf[MAXENTRIES]; /* array of pointers to stat structs */ 64: struct direct **namelist; /* names of jobs in spooling area */ 65: 66: 67: /* 68: * If job number, user name, or "-" is not specified, just print 69: * usage info and exit. 70: */ 71: if (argc < 2) 72: usage(); 73: 74: --argc; ++argv; 75: 76: /* 77: * Process command line flags. 78: * Special case the "-" option so that others may be grouped. 79: */ 80: while (argc > 0 && **argv == '-') { 81: if (*(++(*argv)) == '\0') { 82: ++allflag; 83: } else while (**argv) switch (*(*argv)++) { 84: 85: case 'f': ++fflag; 86: break; 87: 88: case 'i': ++iflag; 89: break; 90: 91: default: usage(); 92: } 93: ++argv; --argc; 94: } 95: 96: /* 97: * If all jobs are to be removed and extra command line arguments 98: * are given, print usage info and exit. 99: */ 100: if (allflag && argc) 101: usage(); 102: 103: /* 104: * If only certain jobs are to be removed and no job #'s or user 105: * names are specified, print usage info and exit. 106: */ 107: if (!allflag && !argc) 108: usage(); 109: 110: /* 111: * If interactive removal and quiet removal are requested, override 112: * quiet removal and run interactively. 113: */ 114: if (iflag && fflag) 115: fflag = 0; 116: 117: /* 118: * Move to spooling area and get user id of person requesting removal. 119: */ 120: if (chdir(ATDIR) == -1) { 121: perror(ATDIR); 122: exit(1); 123: } 124: user = getuid(); 125: 126: /* 127: * Get a list of the files in the spooling area. 128: */ 129: if ((numjobs = scandir(".",&namelist,filewanted,alphasort)) < 0) { 130: perror(ATDIR); 131: exit(1); 132: } 133: 134: /* 135: * Build an array of pointers to the file stats for all jobs in 136: * the spooling area. 137: */ 138: for (i = 0; i < numjobs; ++i) { 139: statptr = (struct stat *) malloc(sizeof(struct stat)); 140: if (statptr == NULL) { 141: perror("malloc"); 142: exit(1); 143: } 144: if (stat(namelist[i]->d_name,statptr) < 0) { 145: perror("stat"); 146: continue; 147: } 148: stbuf[i] = statptr; 149: } 150: 151: /* 152: * If all jobs belonging to the user are to be removed, compare 153: * the user's id to the owner of the file. If they match, remove 154: * the file. If the user is the super-user, don't bother comparing 155: * the id's. After all files are removed, exit (status 0). 156: */ 157: if (allflag) { 158: for (i = 0; i < numjobs; ++i) { 159: if (user == SUPERUSER || isowner(getname(user), 160: namelist[i]->d_name)) 161: (void) removentry(namelist[i]->d_name, 162: (int)stbuf[i]->st_ino, 163: user); 164: } 165: exit(0); 166: } 167: 168: /* 169: * If only certain jobs are to be removed, interpret each command 170: * line argument. A check is done to see if it is a user's name or 171: * a job number (inode #). If it's a user's name, compare the argument 172: * to the files owner. If it's a job number, compare the argument to 173: * the inode number of the file. In either case, if a match occurs, 174: * try to remove the file. (The function "isusername" scans the 175: * argument to see if it is all digits which we will assume means 176: * that it's a job number (a fairly safe assumption?). This is done 177: * because we have to determine whether we are dealing with a user 178: * name or a job number. By assuming that only arguments that are 179: * all digits is a job number, we allow users to have digits in 180: * their login name i.e. "johndoe2"). 181: */ 182: 183: while (argc--) { 184: jobexists = 0; 185: isuname = isusername(*argv); 186: for (i = 0; i < numjobs; ++i) { 187: 188: /* if the inode number is 0, this entry was removed */ 189: if (stbuf[i]->st_ino == 0) 190: continue; 191: 192: /* 193: * if argv is a username, compare his/her uid to 194: * the uid of the owner of the file...... 195: */ 196: if (isuname) { 197: if (!isowner(*argv,namelist[i]->d_name)) 198: continue; 199: 200: /* 201: * otherwise, we assume that the argv is a job # and 202: * thus compare argv to the inode (job #) of the file. 203: */ 204: } else { 205: if (stbuf[i]->st_ino != atoi(*argv)) 206: continue; 207: } 208: ++jobexists; 209: /* 210: * if the entry is ultimately removed, don't 211: * try to remove it again later. 212: */ 213: if (removentry(namelist[i]->d_name, 214: (int)stbuf[i]->st_ino, user)) { 215: stbuf[i]->st_ino = 0; 216: } 217: } 218: 219: /* 220: * If a requested argument doesn't exist, print a message. 221: */ 222: if (!jobexists && !fflag && !isuname) { 223: fprintf(stderr, "%6s: no such job number\n", *argv); 224: } 225: ++argv; 226: } 227: exit(0); 228: } 229: 230: /* 231: * Print usage info and exit. 232: */ 233: usage() 234: { 235: fprintf(stderr,"usage: atrm [-f] [-i] [-] [[job #] [user] ...]\n"); 236: exit(1); 237: } 238: 239: /* 240: * Do we want to include a file in the queue? (used by "scandir") We are looking 241: * for files with following syntax: yy.ddd.hhhh. so the test is made to see if 242: * the file name has three dots in it. This test will suffice since the only 243: * other files in /usr/spool/at don't have any dots in their name. 244: */ 245: filewanted(direntry) 246: struct direct *direntry; 247: { 248: int numdot = 0; /* number of dots in a filename */ 249: char *filename; /* filename we are looking at */ 250: 251: filename = direntry->d_name; 252: while (*filename) 253: numdot += (*(filename++) == '.'); 254: return(numdot == 3); 255: } 256: 257: /* 258: * Is a command line argument a username? As noted above we will assume 259: * that an argument that is all digits means that it's a job number, not 260: * a user's name. We choose to determine whether an argument is a user name 261: * in this manner because then it's ok for someone to have digits in their 262: * user name. 263: */ 264: isusername(string) 265: char *string; 266: { 267: char *ptr; /* pointer used for scanning string */ 268: 269: ptr = string; 270: while (isdigit(*ptr)) 271: ++ptr; 272: return((*ptr == '\0') ? 0 : 1); 273: } 274: 275: /* 276: * Remove an entry from the queue. The access of the file is checked for 277: * write permission (since all jobs are mode 644). If access is granted, 278: * unlink the file. If the fflag (suppress announcements) is not set, 279: * print the job number that we are removing and the result of the access 280: * check (either "permission denied" or "removed"). If we are running 281: * interactively (iflag), prompt the user before we unlink the file. If 282: * the super-user is removing jobs, inform him/her who owns each file before 283: * it is removed. Return TRUE if file removed, else FALSE. 284: */ 285: int 286: removentry(filename,inode,user) 287: char *filename; 288: int inode; 289: int user; 290: { 291: 292: if (!fflag) 293: printf("%6d: ",inode); 294: 295: if (!isowner(getname(user),filename) && user != SUPERUSER) { 296: 297: if (!fflag) { 298: printf("permission denied\n"); 299: } 300: return (0); 301: 302: } else { 303: if (iflag) { 304: if (user == SUPERUSER) { 305: printf("\t(owned by "); 306: powner(filename); 307: printf(") "); 308: } 309: printf("remove it? "); 310: if (!yes()) 311: return (0); 312: } 313: if (unlink(filename) < 0) { 314: if (!fflag) { 315: fputs("could not remove\n", stdout); 316: perror(filename); 317: } 318: return (0); 319: } 320: if (!fflag && !iflag) 321: printf("removed\n"); 322: return (1); 323: } 324: } 325: 326: /* 327: * See if "name" owns "job". 328: */ 329: isowner(name,job) 330: char *name; 331: char *job; 332: { 333: char buf[128]; /* buffer for 1st line of spoolfile 334: header */ 335: FILE *infile; /* I/O stream to spoolfile */ 336: 337: if ((infile = fopen(job,"r")) == NULL) { 338: fprintf(stderr,"Couldn't open spoolfile "); 339: perror(job); 340: return(0); 341: } 342: 343: if (fscanf(infile,"# owner: %127s%*[^\n]\n",buf) != 1) { 344: fclose(infile); 345: return(0); 346: } 347: 348: fclose(infile); 349: return((strcmp(name,buf) == 0) ? 1 : 0); 350: } 351: 352: /* 353: * Print the owner of the job. This is stored on the first line of the 354: * spoolfile. If we run into trouble getting the name, we'll just print "???". 355: */ 356: powner(file) 357: char *file; 358: { 359: char owner[128]; /* the owner */ 360: FILE *infile; /* I/O stream to spoolfile */ 361: 362: /* 363: * Open the job file and grab the first line. 364: */ 365: 366: if ((infile = fopen(file,"r")) == NULL) { 367: printf("%s","???"); 368: perror(file); 369: return; 370: } 371: 372: if (fscanf(infile,"# owner: %127s%*[^\n]\n",owner) != 1) { 373: printf("%s","???"); 374: fclose(infile); 375: return; 376: } 377: 378: fclose(infile); 379: printf("%s",owner); 380: 381: } 382: 383: /* 384: * Get answer to interactive prompts, eating all characters beyond the first 385: * one. If a 'y' is typed, return 1. 386: */ 387: yes() 388: { 389: char ch; /* dummy variable */ 390: char ch1; /* dummy variable */ 391: 392: ch = ch1 = getchar(); 393: while (ch1 != '\n' && ch1 != EOF) 394: ch1 = getchar(); 395: if (isupper(ch)) 396: ch = tolower(ch); 397: return(ch == 'y'); 398: } 399: 400: /* 401: * Get the uid of a person using his/her login name. Return -1 if no 402: * such account name exists. 403: */ 404: getid(name) 405: char *name; 406: { 407: 408: struct passwd *pwdinfo; /* password info structure */ 409: 410: if ((pwdinfo = getpwnam(name)) == 0) 411: return(-1); 412: 413: return(pwdinfo->pw_uid); 414: } 415: 416: /* 417: * Get the full login name of a person using his/her user id. 418: */ 419: char * 420: getname(uid) 421: int uid; 422: { 423: struct passwd *pwdinfo; /* password info structure */ 424: 425: 426: if ((pwdinfo = getpwuid(uid)) == 0) 427: return("???"); 428: return(pwdinfo->pw_name); 429: }