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