1 /* 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #if 0 35 #ifndef lint 36 static char sccsid[] = "@(#)displayq.c 8.4 (Berkeley) 4/28/95"; 37 #endif /* not lint */ 38 #endif 39 40 #include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 41 __FBSDID("$FreeBSD$"); 42 43 #include <sys/param.h> 44 #include <sys/stat.h> 45 46 #include <ctype.h> 47 #include <dirent.h> 48 #include <errno.h> 49 #include <fcntl.h> 50 #include <signal.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #define psignal foil_gcc_psignal 55 #define sys_siglist foil_gcc_siglist 56 #include <unistd.h> 57 #undef psignal 58 #undef sys_siglist 59 60 #include "lp.h" 61 #include "lp.local.h" 62 #include "pathnames.h" 63 64 /* 65 * Routines to display the state of the queue. 66 */ 67 #define JOBCOL 40 /* column for job # in -l format */ 68 #define OWNCOL 7 /* start of Owner column in normal */ 69 #define SIZCOL 62 /* start of Size column in normal */ 70 71 /* 72 * Stuff for handling job specifications 73 */ 74 extern uid_t uid, euid; 75 76 static int col; /* column on screen */ 77 static char current[MAXNAMLEN+1]; /* current file being printed */ 78 static char file[MAXNAMLEN+1]; /* print file name */ 79 static int first; /* first file in ``files'' column? */ 80 static int garbage; /* # of garbage cf files */ 81 static int lflag; /* long output option */ 82 static int rank; /* order to be printed (-1=none, 0=active) */ 83 static long totsize; /* total print job size in bytes */ 84 85 static const char *head0 = "Rank Owner Job Files"; 86 static const char *head1 = "Total Size\n"; 87 88 static void alarmhandler(int _signo); 89 static void warn(const struct printer *_pp); 90 91 /* 92 * Display the current state of the queue. Format = 1 if long format. 93 */ 94 void 95 displayq(struct printer *pp, int format) 96 { 97 register struct jobqueue *q; 98 register int i, nitems, fd, ret; 99 char *cp, *endp; 100 struct jobqueue **queue; 101 struct stat statb; 102 FILE *fp; 103 void (*savealrm)(int); 104 105 lflag = format; 106 totsize = 0; 107 rank = -1; 108 109 if ((cp = checkremote(pp))) { 110 printf("Warning: %s\n", cp); 111 free(cp); 112 } 113 114 /* 115 * Print out local queue 116 * Find all the control files in the spooling directory 117 */ 118 seteuid(euid); 119 if (chdir(pp->spool_dir) < 0) 120 fatal(pp, "cannot chdir to spooling directory: %s", 121 strerror(errno)); 122 seteuid(uid); 123 if ((nitems = getq(pp, &queue)) < 0) 124 fatal(pp, "cannot examine spooling area\n"); 125 seteuid(euid); 126 ret = stat(pp->lock_file, &statb); 127 seteuid(uid); 128 if (ret >= 0) { 129 if (statb.st_mode & LFM_PRINT_DIS) { 130 if (pp->remote) 131 printf("%s: ", local_host); 132 printf("Warning: %s is down: ", pp->printer); 133 seteuid(euid); 134 fd = open(pp->status_file, O_RDONLY|O_SHLOCK); 135 seteuid(uid); 136 if (fd >= 0) { 137 while ((i = read(fd, line, sizeof(line))) > 0) 138 (void) fwrite(line, 1, i, stdout); 139 (void) close(fd); /* unlocks as well */ 140 } else 141 putchar('\n'); 142 } 143 if (statb.st_mode & LFM_QUEUE_DIS) { 144 if (pp->remote) 145 printf("%s: ", local_host); 146 printf("Warning: %s queue is turned off\n", 147 pp->printer); 148 } 149 } 150 151 if (nitems) { 152 seteuid(euid); 153 fp = fopen(pp->lock_file, "r"); 154 seteuid(uid); 155 if (fp == NULL) 156 warn(pp); 157 else { 158 /* get daemon pid */ 159 cp = current; 160 endp = cp + sizeof(current) - 1; 161 while ((i = getc(fp)) != EOF && i != '\n') { 162 if (cp < endp) 163 *cp++ = i; 164 } 165 *cp = '\0'; 166 i = atoi(current); 167 if (i <= 0) { 168 ret = -1; 169 } else { 170 seteuid(euid); 171 ret = kill(i, 0); 172 seteuid(uid); 173 } 174 if (ret < 0) { 175 warn(pp); 176 } else { 177 /* read current file name */ 178 cp = current; 179 endp = cp + sizeof(current) - 1; 180 while ((i = getc(fp)) != EOF && i != '\n') { 181 if (cp < endp) 182 *cp++ = i; 183 } 184 *cp = '\0'; 185 /* 186 * Print the status file. 187 */ 188 if (pp->remote) 189 printf("%s: ", local_host); 190 seteuid(euid); 191 fd = open(pp->status_file, O_RDONLY|O_SHLOCK); 192 seteuid(uid); 193 if (fd >= 0) { 194 while ((i = read(fd, line, 195 sizeof(line))) > 0) 196 fwrite(line, 1, i, stdout); 197 close(fd); /* unlocks as well */ 198 } else 199 putchar('\n'); 200 } 201 (void) fclose(fp); 202 } 203 /* 204 * Now, examine the control files and print out the jobs to 205 * be done for each user. 206 */ 207 if (!lflag) 208 header(); 209 for (i = 0; i < nitems; i++) { 210 q = queue[i]; 211 inform(pp, q->job_cfname); 212 free(q); 213 } 214 free(queue); 215 } 216 if (!pp->remote) { 217 if (nitems == 0) 218 puts("no entries"); 219 return; 220 } 221 222 /* 223 * Print foreign queue 224 * Note that a file in transit may show up in either queue. 225 */ 226 if (nitems) 227 putchar('\n'); 228 (void) snprintf(line, sizeof(line), "%c%s", format ? '\4' : '\3', 229 pp->remote_queue); 230 cp = line; 231 for (i = 0; i < requests && cp-line+10 < sizeof(line) - 1; i++) { 232 cp += strlen(cp); 233 (void) sprintf(cp, " %d", requ[i]); 234 } 235 for (i = 0; i < users && cp - line + 1 + strlen(user[i]) < 236 sizeof(line) - 1; i++) { 237 cp += strlen(cp); 238 *cp++ = ' '; 239 (void) strcpy(cp, user[i]); 240 } 241 strcat(line, "\n"); 242 savealrm = signal(SIGALRM, alarmhandler); 243 alarm(pp->conn_timeout); 244 fd = getport(pp, pp->remote_host, 0); 245 alarm(0); 246 (void)signal(SIGALRM, savealrm); 247 if (fd < 0) { 248 if (from_host != local_host) 249 printf("%s: ", local_host); 250 printf("connection to %s is down\n", pp->remote_host); 251 } 252 else { 253 i = strlen(line); 254 if (write(fd, line, i) != i) 255 fatal(pp, "Lost connection"); 256 while ((i = read(fd, line, sizeof(line))) > 0) 257 (void) fwrite(line, 1, i, stdout); 258 (void) close(fd); 259 } 260 } 261 262 /* 263 * Print a warning message if there is no daemon present. 264 */ 265 static void 266 warn(const struct printer *pp) 267 { 268 if (pp->remote) 269 printf("%s: ", local_host); 270 puts("Warning: no daemon present"); 271 current[0] = '\0'; 272 } 273 274 /* 275 * Print the header for the short listing format 276 */ 277 void 278 header(void) 279 { 280 printf("%s", head0); 281 col = strlen(head0)+1; 282 blankfill(SIZCOL); 283 printf("%s", head1); 284 } 285 286 void 287 inform(const struct printer *pp, char *cf) 288 { 289 register int copycnt; 290 char savedname[MAXPATHLEN+1]; 291 FILE *cfp; 292 293 /* 294 * There's a chance the control file has gone away 295 * in the meantime; if this is the case just keep going 296 */ 297 seteuid(euid); 298 if ((cfp = fopen(cf, "r")) == NULL) 299 return; 300 seteuid(uid); 301 302 if (rank < 0) 303 rank = 0; 304 if (pp->remote || garbage || strcmp(cf, current)) 305 rank++; 306 307 /* 308 * The cf-file may include commands to print more than one datafile 309 * from the user. For each datafile, the cf-file contains at least 310 * one line which starts with some format-specifier ('a'-'z'), and 311 * a second line ('N'ame) which indicates the original name the user 312 * specified for that file. There can be multiple format-spec lines 313 * for a single Name-line, if the user requested multiple copies of 314 * that file. Standard lpr puts the format-spec line(s) before the 315 * Name-line, while lprNG puts the Name-line before the format-spec 316 * line(s). This section needs to handle the lines in either order. 317 */ 318 copycnt = 0; 319 file[0] = '\0'; 320 savedname[0] = '\0'; 321 while (getline(cfp)) { 322 switch (line[0]) { 323 case 'P': /* Was this file specified in the user's list? */ 324 if (!inlist(line+1, cf)) { 325 fclose(cfp); 326 return; 327 } 328 if (lflag) { 329 printf("\n%s: ", line+1); 330 col = strlen(line+1) + 2; 331 prank(rank); 332 blankfill(JOBCOL); 333 printf(" [job %s]\n", cf+3); 334 } else { 335 col = 0; 336 prank(rank); 337 blankfill(OWNCOL); 338 printf("%-10s %-3d ", line+1, atoi(cf+3)); 339 col += 16; 340 first = 1; 341 } 342 continue; 343 default: /* some format specifer and file name? */ 344 if (line[0] < 'a' || line[0] > 'z') 345 break; 346 if (copycnt == 0 || strcmp(file, line+1) != 0) { 347 strlcpy(file, line + 1, sizeof(file)); 348 } 349 copycnt++; 350 /* 351 * deliberately 'continue' to another getline(), so 352 * all format-spec lines for this datafile are read 353 * in and counted before calling show() 354 */ 355 continue; 356 case 'N': 357 strlcpy(savedname, line + 1, sizeof(savedname)); 358 break; 359 } 360 if ((file[0] != '\0') && (savedname[0] != '\0')) { 361 show(savedname, file, copycnt); 362 copycnt = 0; 363 file[0] = '\0'; 364 savedname[0] = '\0'; 365 } 366 } 367 fclose(cfp); 368 /* check for a file which hasn't been shown yet */ 369 if (file[0] != '\0') { 370 if (savedname[0] == '\0') { 371 /* a safeguard in case the N-ame line is missing */ 372 strlcpy(savedname, file, sizeof(savedname)); 373 } 374 show(savedname, file, copycnt); 375 } 376 if (!lflag) { 377 blankfill(SIZCOL); 378 printf("%ld bytes\n", totsize); 379 totsize = 0; 380 } 381 } 382 383 int 384 inlist(char *uname, char *cfile) 385 { 386 register int *r, n; 387 register char **u, *cp; 388 389 if (users == 0 && requests == 0) 390 return(1); 391 /* 392 * Check to see if it's in the user list 393 */ 394 for (u = user; u < &user[users]; u++) 395 if (!strcmp(*u, uname)) 396 return(1); 397 /* 398 * Check the request list 399 */ 400 for (n = 0, cp = cfile+3; isdigit(*cp); ) 401 n = n * 10 + (*cp++ - '0'); 402 for (r = requ; r < &requ[requests]; r++) 403 if (*r == n && !strcmp(cp, from_host)) 404 return(1); 405 return(0); 406 } 407 408 void 409 show(const char *nfile, const char *datafile, int copies) 410 { 411 if (strcmp(nfile, " ") == 0) 412 nfile = "(standard input)"; 413 if (lflag) 414 ldump(nfile, datafile, copies); 415 else 416 dump(nfile, datafile, copies); 417 } 418 419 /* 420 * Fill the line with blanks to the specified column 421 */ 422 void 423 blankfill(int tocol) 424 { 425 while (col++ < tocol) 426 putchar(' '); 427 } 428 429 /* 430 * Give the abbreviated dump of the file names 431 */ 432 void 433 dump(const char *nfile, const char *datafile, int copies) 434 { 435 struct stat lbuf; 436 const char etctmpl[] = ", ..."; 437 char etc[sizeof(etctmpl)]; 438 char *lastsep; 439 short fill, nlen; 440 short rem, remetc; 441 442 /* 443 * Print as many filenames as will fit 444 * (leaving room for the 'total size' field) 445 */ 446 fill = first ? 0 : 2; /* fill space for ``, '' */ 447 nlen = strlen(nfile); 448 rem = SIZCOL - 1 - col; 449 if (nlen + fill > rem) { 450 if (first) { 451 /* print the right-most part of the name */ 452 printf("...%s ", &nfile[3+nlen-rem]); 453 col = SIZCOL; 454 } else if (rem > 0) { 455 /* fit as much of the etc-string as we can */ 456 remetc = rem; 457 if (rem > strlen(etctmpl)) 458 remetc = strlen(etctmpl); 459 etc[0] = '\0'; 460 strncat(etc, etctmpl, remetc); 461 printf("%s", etc); 462 col += remetc; 463 rem -= remetc; 464 /* room for the last segment of this filename? */ 465 lastsep = strrchr(nfile, '/'); 466 if ((lastsep != NULL) && (rem > strlen(lastsep))) { 467 /* print the right-most part of this name */ 468 printf("%s", lastsep); 469 col += strlen(lastsep); 470 } else { 471 /* do not pack any more names in here */ 472 blankfill(SIZCOL); 473 } 474 } 475 } else { 476 if (!first) 477 printf(", "); 478 printf("%s", nfile); 479 col += nlen + fill; 480 } 481 first = 0; 482 483 seteuid(euid); 484 if (*datafile && !stat(datafile, &lbuf)) 485 totsize += copies * lbuf.st_size; 486 seteuid(uid); 487 } 488 489 /* 490 * Print the long info about the file 491 */ 492 void 493 ldump(const char *nfile, const char *datafile, int copies) 494 { 495 struct stat lbuf; 496 497 putchar('\t'); 498 if (copies > 1) 499 printf("%-2d copies of %-19s", copies, nfile); 500 else 501 printf("%-32s", nfile); 502 if (*datafile && !stat(datafile, &lbuf)) 503 printf(" %qd bytes", (long long) lbuf.st_size); 504 else 505 printf(" ??? bytes"); 506 putchar('\n'); 507 } 508 509 /* 510 * Print the job's rank in the queue, 511 * update col for screen management 512 */ 513 void 514 prank(int n) 515 { 516 char rline[100]; 517 static const char *r[] = { 518 "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" 519 }; 520 521 if (n == 0) { 522 printf("active"); 523 col += 6; 524 return; 525 } 526 if ((n/10)%10 == 1) 527 (void)snprintf(rline, sizeof(rline), "%dth", n); 528 else 529 (void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]); 530 col += strlen(rline); 531 printf("%s", rline); 532 } 533 534 void 535 alarmhandler(int signo __unused) 536 { 537 /* the signal is ignored */ 538 /* (the '__unused' is just to avoid a compile-time warning) */ 539 } 540