1 /* 2 * Copyright 2009 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 /* 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 <strings.h> 34 #include <stdlib.h> 35 #include <libintl.h> 36 #include "cron.h" 37 #include "getresponse.h" 38 39 extern time_t num(); 40 extern char *errmsg(); 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 getjoblist(struct dirent ***, struct stat ***, int (*)()); 67 int removentry(char *, struct stat *, uid_t); 68 69 int 70 main(int argc, char **argv) 71 { 72 int i; /* for loop index */ 73 int numjobs; /* # of jobs in spooling area */ 74 int allflag = 0; /* remove all jobs belonging to user? */ 75 int jobexists; /* does a requested job exist? */ 76 char *pp; 77 struct dirent **namelist; /* names of jobs in spooling area */ 78 struct stat **statlist; 79 struct passwd *pwd; 80 81 /* 82 * If job number, user name, or "-" is not specified, just print 83 * usage info and exit. 84 */ 85 (void) setlocale(LC_ALL, ""); 86 (void) textdomain(TEXT_DOMAIN); 87 if (argc < 2) 88 usage(); 89 90 --argc; ++argv; 91 92 pp = getuser((user = getuid())); 93 if (pp == NULL) 94 atabort(INVALIDUSER); 95 if (strlcpy(login, pp, sizeof (login)) >= sizeof (login)) 96 atabort(NAMETOOLONG); 97 if (strlcpy(login_authchk, pp, sizeof (login_authchk)) 98 >= sizeof (NAMETOOLONG)) 99 atabort(INVALIDUSER); 100 if (!allowed(login, ATALLOW, ATDENY)) 101 atabort(NOTALLOWED); 102 103 /* 104 * Process command line flags. 105 * Special case the "-" option so that others may be grouped. 106 */ 107 while (argc > 0 && **argv == '-') { 108 *(*argv)++; 109 while (**argv) { 110 switch (*(*argv)++) { 111 112 case 'a': ++allflag; 113 break; 114 115 case 'f': ++fflag; 116 break; 117 118 case 'i': ++iflag; 119 break; 120 121 default: usage(); 122 } 123 } 124 ++argv; --argc; 125 } 126 127 /* 128 * If all jobs are to be removed and extra command line arguments 129 * are given, print usage info and exit. 130 */ 131 if (allflag && argc) 132 usage(); 133 134 /* 135 * If only certain jobs are to be removed and no job #'s or user 136 * names are specified, print usage info and exit. 137 */ 138 if (!allflag && !argc) 139 usage(); 140 141 /* 142 * If interactive removal and quiet removal are requested, override 143 * quiet removal and run interactively. 144 */ 145 if (iflag && fflag) 146 fflag = 0; 147 148 149 /* 150 * Move to spooling directory and get a list of the files in the 151 * spooling area. 152 */ 153 numjobs = getjoblist(&namelist, &statlist, strcmp); 154 /* 155 * If all jobs belonging to the user are to be removed, compare 156 * the user's id to the owner of the file. If they match, remove 157 * the file. If the user is the super-user, don't bother comparing 158 * the id's. After all files are removed, exit (status 0). 159 */ 160 if (allflag) { 161 for (i = 0; i < numjobs; ++i) { 162 if (cron_admin(login_authchk) || 163 user == statlist[i]->st_uid) 164 (void) removentry(namelist[i]->d_name, 165 statlist[i], user); 166 } 167 exit(0); 168 } 169 170 /* 171 * If only certain jobs are to be removed, interpret each command 172 * line argument. A check is done to see if it is a user's name or 173 * a job number (inode #). If it's a user's name, compare the argument 174 * to the files owner. If it's a job number, compare the argument to 175 * the file name. In either case, if a match occurs, try to 176 * remove the file. 177 */ 178 179 while (argc--) { 180 jobexists = 0; 181 for (i = 0; i < numjobs; ++i) { 182 183 /* if the inode number is 0, this entry was removed */ 184 if (statlist[i]->st_ino == 0) 185 continue; 186 187 /* 188 * if argv is a username, compare his/her uid to 189 * the uid of the owner of the file...... 190 */ 191 if (pwd = getpwnam(*argv)) { 192 if (statlist[i]->st_uid != pwd->pw_uid) 193 continue; 194 /* 195 * otherwise, we assume that the argv is a job # and 196 * thus compare argv to the file name. 197 */ 198 } else { 199 if (strcmp(namelist[i]->d_name, *argv)) 200 continue; 201 } 202 ++jobexists; 203 /* 204 * if the entry is ultimately removed, don't 205 * try to remove it again later. 206 */ 207 if (removentry(namelist[i]->d_name, statlist[i], 208 user)) { 209 statlist[i]->st_ino = 0; 210 } 211 } 212 213 /* 214 * If a requested argument doesn't exist, print a message. 215 */ 216 if (!jobexists && !fflag) { 217 fprintf(stderr, "atrm: %s: no such job number\n", 218 *argv); 219 } 220 ++argv; 221 } 222 return (0); 223 } 224 225 /* 226 * Print usage info and exit. 227 */ 228 static void 229 usage(void) 230 { 231 fprintf(stderr, "usage: atrm [-f] [-i] [-a] [[job #] [user] ...]\n"); 232 exit(1); 233 } 234 235 236 /* 237 * Remove an entry from the queue. The access of the file is checked for 238 * write permission (since all jobs are mode 644). If access is granted, 239 * unlink the file. If the fflag (suppress announcements) is not set, 240 * print the job number that we are removing and the result of the access 241 * check (either "permission denied" or "removed"). If we are running 242 * interactively (iflag), prompt the user before we unlink the file. If 243 * the super-user is removing jobs, inform him/her who owns each file before 244 * it is removed. Return TRUE if file removed, else FALSE. 245 */ 246 int 247 removentry(char *filename, struct stat *statptr, uid_t user) 248 { 249 struct passwd *pwd; 250 char *pp; 251 int r; 252 253 if (init_yes() < 0) { 254 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES), 255 strerror(errno)); 256 exit(1); 257 } 258 259 if (!fflag) 260 printf("%s: ", filename); 261 262 if (user != statptr->st_uid && !cron_admin(login_authchk)) { 263 264 if (!fflag) { 265 printf("permission denied\n"); 266 } 267 return (0); 268 269 } else { 270 if (iflag) { 271 if (cron_admin(login_authchk)) { 272 printf("\t(owned by "); 273 powner(filename); 274 printf(") "); 275 } 276 printf(gettext("remove it? ")); 277 if (yes() == 0) 278 return (0); 279 } 280 281 if (cron_admin(login_authchk)) { 282 pp = getuser((uid_t)statptr->st_uid); 283 if (pp == NULL) 284 atabort(INVALIDUSER); 285 if (strlcpy(login, pp, sizeof (login)) >= 286 sizeof (login)) 287 atabort(NAMETOOLONG); 288 } 289 cron_sendmsg(DELETE, login, filename, AT); 290 if ((r = unlink(filename)) < 0) { 291 if (!fflag) { 292 fputs("could not remove\n", stdout); 293 (void) fprintf(stderr, "atrm: %s: %s\n", 294 filename, errmsg(errno)); 295 } 296 audit_at_delete(filename, NULL, r); 297 return (0); 298 } 299 audit_at_delete(filename, NULL, r); 300 if (!fflag && !iflag) 301 printf("removed\n"); 302 return (1); 303 } 304 } 305 306 /* 307 * Print the owner of the job. This is the owner of the spoolfile. 308 * If we run into trouble getting the name, we'll just print "???". 309 */ 310 static void 311 powner(char *file) 312 { 313 struct stat statb; 314 char *getname(); 315 316 if (stat(file, &statb) < 0) { 317 printf("%s", "???"); 318 (void) fprintf(stderr, "atrm: Couldn't stat spoolfile %s: %s\n", 319 file, errmsg(errno)); 320 return; 321 } 322 323 printf("%s", getname(statb.st_uid)); 324 } 325 326 327 int 328 getjoblist(struct dirent ***namelistp, struct stat ***statlistp, 329 int (*sortfunc)()) 330 { 331 int numjobs; 332 struct dirent **namelist; 333 int i; 334 struct stat *statptr; /* pointer to file stat structure */ 335 struct stat **statlist; 336 extern int filewanted(); /* should a file be listed in queue? */ 337 if (chdir(ATDIR) < 0) 338 atabortperror(CANTCD); 339 340 /* 341 * Get a list of the files in the spooling area. 342 */ 343 if ((numjobs = scandir(".", namelistp, filewanted, sortfunc)) < 0) 344 atabortperror(NOREADDIR); 345 346 if ((statlist = 347 (struct stat **)malloc(numjobs * sizeof (struct stat ***))) 348 == NULL) 349 atabort("Out of memory"); 350 351 namelist = *namelistp; 352 353 /* 354 * Build an array of pointers to the file stats for all jobs in 355 * the spooling area. 356 */ 357 for (i = 0; i < numjobs; ++i) { 358 statptr = (struct stat *)malloc(sizeof (struct stat)); 359 if (statptr == NULL) 360 atabort("Out of memory"); 361 if (stat(namelist[i]->d_name, statptr) < 0) { 362 atperror2("Can't stat", namelist[i]->d_name); 363 continue; 364 } 365 statlist[i] = statptr; 366 } 367 368 *statlistp = statlist; 369 return (numjobs); 370 } 371 372 /* 373 * Get the full login name of a person using his/her user id. 374 */ 375 char * 376 getname(uid_t uid) 377 { 378 struct passwd *pwdinfo; /* password info structure */ 379 380 381 if ((pwdinfo = getpwuid(uid)) == 0) 382 return ("???"); 383 return (pwdinfo->pw_name); 384 } 385 386 static void 387 aterror(char *msg) 388 { 389 fprintf(stderr, "atrm: %s\n", msg); 390 } 391 392 static void 393 atperror(char *msg) 394 { 395 fprintf(stderr, "atrm: %s: %s\n", msg, errmsg(errno)); 396 } 397 398 static void 399 atperror2(char *msg, char *name) 400 { 401 fprintf(stderr, "atrm: %s %s: %s\n", msg, name, errmsg(errno)); 402 } 403 404 static void 405 atabort(char *msg) 406 { 407 aterror(msg); 408 exit(1); 409 } 410 411 static void 412 atabortperror(char *msg) 413 { 414 atperror(msg); 415 exit(1); 416 } 417