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