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