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