1 /* $OpenBSD: rmjob.c,v 1.17 2007/04/07 21:12:12 stevesk Exp $ */ 2 /* $NetBSD: rmjob.c,v 1.16 2000/04/16 14:43:58 mrg Exp $ */ 3 4 /* 5 * Copyright (c) 1983, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #ifndef lint 34 #if 0 35 static const char sccsid[] = "@(#)rmjob.c 8.2 (Berkeley) 4/28/95"; 36 #else 37 static const char rcsid[] = "$OpenBSD: rmjob.c,v 1.17 2007/04/07 21:12:12 stevesk Exp $"; 38 #endif 39 #endif /* not lint */ 40 41 #include <sys/param.h> 42 43 #include <signal.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <dirent.h> 47 #include <unistd.h> 48 #include <stdlib.h> 49 #include <stdio.h> 50 #include <string.h> 51 #include <ctype.h> 52 #include "lp.h" 53 #include "lp.local.h" 54 #include "pathnames.h" 55 56 /* 57 * rmjob - remove the specified jobs from the queue. 58 */ 59 60 /* 61 * Stuff for handling lprm specifications 62 */ 63 extern char *user[]; /* users to process */ 64 extern int users; /* # of users in user array */ 65 extern int requ[]; /* job number of spool entries */ 66 extern int requests; /* # of spool requests */ 67 extern char *person; /* name of person doing lprm */ 68 69 static char root[] = "root"; 70 static int all = 0; /* eliminate all files (root only) */ 71 static int cur_daemon; /* daemon's pid */ 72 static char current[NAME_MAX]; /* active control file name */ 73 74 static void do_unlink(char *); 75 static void alarmer(int); 76 static int lockchk(char *); 77 78 void 79 rmjob(void) 80 { 81 int i, nitems; 82 int assasinated = 0; 83 struct dirent **files; 84 char *cp; 85 86 if ((i = cgetent(&bp, printcapdb, printer)) == -2) 87 fatal("can't open printer description file"); 88 else if (i == -1) 89 fatal("unknown printer"); 90 else if (i == -3) 91 fatal("potential reference loop detected in printcap file"); 92 if (cgetstr(bp, DEFLP, &LP) < 0) 93 LP = _PATH_DEFDEVLP; 94 if (cgetstr(bp, "rp", &RP) < 0) 95 RP = DEFLP; 96 if (cgetstr(bp, "sd", &SD) < 0) 97 SD = _PATH_DEFSPOOL; 98 if (cgetstr(bp,"lo", &LO) < 0) 99 LO = DEFLOCK; 100 cgetstr(bp, "rm", &RM); 101 if ((cp = checkremote()) != NULL) 102 printf("Warning: %s\n", cp); 103 104 /* 105 * If the format was `lprm -' and the user isn't the super-user, 106 * then fake things to look like he said `lprm user'. 107 */ 108 if (users < 0) { 109 if (getuid() == 0) 110 all = 1; /* all files in local queue */ 111 else { 112 user[0] = person; 113 users = 1; 114 } 115 } 116 if (!strcmp(person, "-all")) { 117 if (from == host) 118 fatal("The login name \"-all\" is reserved"); 119 all = 1; /* all those from 'from' */ 120 person = root; 121 } 122 123 PRIV_START; 124 if (chdir(SD) < 0) 125 fatal("cannot chdir to spool directory"); 126 if ((nitems = scandir(".", &files, iscf, NULL)) < 0) 127 fatal("cannot access spool directory"); 128 PRIV_END; 129 130 if (nitems) { 131 /* 132 * Check for an active printer daemon. If one is running 133 * and it is reading our file, kill it, then remove stuff. 134 * Lastly, restart the daemon if it is not (or no longer) 135 * running. 136 */ 137 if (lockchk(LO) && chk(current)) { 138 PRIV_START; 139 assasinated = kill(cur_daemon, SIGINT) == 0; 140 PRIV_END; 141 if (!assasinated) 142 fatal("cannot kill printer daemon"); 143 } 144 /* 145 * process the files 146 */ 147 for (i = 0; i < nitems; i++) 148 process(files[i]->d_name); 149 } 150 rmremote(); 151 /* 152 * Restart the printer daemon if it was killed 153 */ 154 if (assasinated && !startdaemon(printer)) 155 fatal("cannot restart printer daemon"); 156 exit(0); 157 } 158 159 /* 160 * Process a lock file: collect the pid of the active 161 * daemon and the file name of the active spool entry. 162 * Return boolean indicating existence of a lock file. 163 */ 164 static int 165 lockchk(char *s) 166 { 167 FILE *fp = NULL; 168 int fd, i, n; 169 170 /* NOTE: lock file is owned by root, not the user. */ 171 PRIV_START; 172 fd = safe_open(s, O_RDONLY|O_NOFOLLOW, 0); 173 PRIV_END; 174 if (fd < 0 || (fp = fdopen(fd, "r")) == NULL) { 175 if (fd >= 0) 176 close(fd); 177 if (errno == EACCES) 178 fatal("can't access lock file"); 179 else 180 return(0); 181 } 182 if (!getline(fp)) { 183 (void)fclose(fp); 184 return(0); /* no daemon present */ 185 } 186 cur_daemon = atoi(line); 187 if (kill(cur_daemon, 0) < 0 && errno != EPERM) { 188 (void)fclose(fp); 189 return(0); /* no daemon present */ 190 } 191 for (i = 1; (n = fread(current, sizeof(char), sizeof(current), fp)) <= 0; i++) { 192 if (i > 5) { 193 n = 1; 194 break; 195 } 196 sleep(i); 197 } 198 current[n-1] = '\0'; 199 (void)fclose(fp); 200 return(1); 201 } 202 203 /* 204 * Process a control file. 205 */ 206 void 207 process(char *file) 208 { 209 FILE *cfp = NULL; 210 int fd; 211 212 if (!chk(file)) 213 return; 214 PRIV_START; 215 fd = safe_open(file, O_RDONLY|O_NOFOLLOW, 0); 216 PRIV_END; 217 if (fd < 0 || (cfp = fdopen(fd, "r")) == NULL) { 218 if (fd >= 0) 219 close(fd); 220 fatal("cannot open %s", file); 221 } 222 while (getline(cfp)) { 223 switch (line[0]) { 224 case 'U': /* unlink associated files */ 225 if (strchr(line+1, '/') || strncmp(line+1, "df", 2)) 226 break; 227 do_unlink(line+1); 228 } 229 } 230 (void)fclose(cfp); 231 do_unlink(file); 232 } 233 234 static void 235 do_unlink(char *file) 236 { 237 int ret; 238 239 if (from != host) 240 printf("%s: ", host); 241 PRIV_START; 242 ret = unlink(file); 243 PRIV_END; 244 printf(ret ? "cannot dequeue %s\n" : "%s dequeued\n", file); 245 } 246 247 /* 248 * Do the dirty work in checking 249 */ 250 int 251 chk(char *file) 252 { 253 int *r, n, fd; 254 char **u, *cp; 255 FILE *cfp = NULL; 256 257 /* 258 * Check for valid cf file name (mostly checking current). 259 */ 260 if (strlen(file) < 7 || file[0] != 'c' || file[1] != 'f') 261 return(0); 262 263 if (all && (from == host || !strcmp(from, file+6))) 264 return(1); 265 266 /* 267 * get the owner's name from the control file. 268 */ 269 PRIV_START; 270 fd = safe_open(file, O_RDONLY|O_NOFOLLOW, 0); 271 PRIV_END; 272 if (fd < 0 || (cfp = fdopen(fd, "r")) == NULL) { 273 if (fd >= 0) 274 close(fd); 275 return(0); 276 } 277 while (getline(cfp)) { 278 if (line[0] == 'P') 279 break; 280 } 281 (void)fclose(cfp); 282 if (line[0] != 'P') 283 return(0); 284 285 if (users == 0 && requests == 0) 286 return(!strcmp(file, current) && isowner(line+1, file)); 287 /* 288 * Check the request list 289 */ 290 for (n = 0, cp = file+3; isdigit(*cp); ) 291 n = n * 10 + (*cp++ - '0'); 292 for (r = requ; r < &requ[requests]; r++) 293 if (*r == n && isowner(line+1, file)) 294 return(1); 295 /* 296 * Check to see if it's in the user list 297 */ 298 for (u = user; u < &user[users]; u++) 299 if (!strcmp(*u, line+1) && isowner(line+1, file)) 300 return(1); 301 return(0); 302 } 303 304 /* 305 * If root is removing a file on the local machine, allow it. 306 * If root is removing a file from a remote machine, only allow 307 * files sent from the remote machine to be removed. 308 * Normal users can only remove the file from where it was sent. 309 */ 310 int 311 isowner(char *owner, char *file) 312 { 313 if (!strcmp(person, root) && (from == host || !strcmp(from, file+6))) 314 return(1); 315 if (!strcmp(person, owner) && !strcmp(from, file+6)) 316 return(1); 317 if (from != host) 318 printf("%s: ", host); 319 printf("%s: Permission denied\n", file); 320 return(0); 321 } 322 323 /* 324 * Check to see if we are sending files to a remote machine. If we are, 325 * then try removing files on the remote machine. 326 */ 327 void 328 rmremote(void) 329 { 330 char *cp; 331 int i, rem; 332 size_t n; 333 char buf[BUFSIZ]; 334 335 if (!remote) 336 return; /* not sending to a remote machine */ 337 338 /* 339 * Flush stdout so the user can see what has been deleted 340 * while we wait (possibly) for the connection. 341 */ 342 fflush(stdout); 343 344 /* the trailing space will be replaced with a newline later */ 345 n = snprintf(buf, sizeof(buf), "\5%s %s ", RP, all ? "-all" : person); 346 if (n == -1 || n >= sizeof(buf)) 347 goto bad; 348 cp = buf + n; 349 for (i = 0; i < users; i++) { 350 n = strlcpy(cp, user[i], sizeof(buf) - (cp - buf + 1)); 351 if (n >= sizeof(buf) - (cp - buf + 1)) 352 goto bad; 353 cp += n; 354 *cp++ = ' '; 355 } 356 *cp = '\0'; 357 for (i = 0; i < requests; i++) { 358 n = snprintf(cp, sizeof(buf) - (cp - buf), "%d ", requ[i]); 359 if (n == -1 || n >= sizeof(buf) - (cp - buf)) 360 goto bad; 361 cp += n; 362 } 363 cp[-1] = '\n'; /* replace space with newline, leave the NUL */ 364 rem = getport(RM, 0); 365 if (rem < 0) { 366 if (from != host) 367 printf("%s: ", host); 368 printf("connection to %s is down\n", RM); 369 } else { 370 struct sigaction osa, nsa; 371 372 memset(&nsa, 0, sizeof(nsa)); 373 nsa.sa_handler = alarmer; 374 sigemptyset(&nsa.sa_mask); 375 nsa.sa_flags = 0; 376 (void)sigaction(SIGALRM, &nsa, &osa); 377 alarm(wait_time); 378 379 i = strlen(buf); 380 if (write(rem, buf, i) != i) 381 fatal("Lost connection"); 382 while ((i = read(rem, buf, sizeof(buf))) > 0) 383 (void)fwrite(buf, 1, i, stdout); 384 alarm(0); 385 (void)sigaction(SIGALRM, &osa, NULL); 386 (void)close(rem); 387 } 388 return; 389 bad: 390 printf("remote buffer too large\n"); 391 return; 392 } 393 394 static void 395 alarmer(int s) 396 { 397 /* nothing */ 398 } 399 400 /* 401 * Return 1 if the filename begins with 'cf' 402 */ 403 int 404 iscf(struct dirent *d) 405 { 406 return(d->d_name[0] == 'c' && d->d_name[1] == 'f'); 407 } 408