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