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