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