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