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