1 /* $OpenBSD: displayq.c,v 1.40 2018/04/26 12:42:51 guenther Exp $ */ 2 /* $NetBSD: displayq.c,v 1.21 2001/08/30 00:51:50 itojun Exp $ */ 3 4 /* 5 * Copyright (c) 1983, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/ioctl.h> 34 #include <sys/stat.h> 35 36 #include <ctype.h> 37 #include <errno.h> 38 #include <dirent.h> 39 #include <fcntl.h> 40 #include <limits.h> 41 #include <signal.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 #include <vis.h> 47 48 #include "lp.h" 49 #include "lp.local.h" 50 #include "pathnames.h" 51 52 /* 53 * Routines to display the state of the queue. 54 */ 55 #define JOBCOL 40 /* column for job # in -l format */ 56 #define OWNCOL 7 /* start of Owner column in normal */ 57 #define SIZCOL 62 /* start of Size column in normal */ 58 59 /* 60 * Stuff for handling job specifications 61 */ 62 extern int requ[]; /* job number of spool entries */ 63 extern int requests; /* # of spool requests */ 64 extern char *user[]; /* users to process */ 65 extern int users; /* # of users in user array */ 66 67 static int termwidth; 68 static int col; /* column on screen */ 69 static char current[NAME_MAX]; /* current file being printed */ 70 static char file[NAME_MAX]; /* print file name */ 71 static int first; /* first file in ``files'' column? */ 72 static int lflag; /* long output option */ 73 static off_t totsize; /* total print job size in bytes */ 74 75 static const char head0[] = "Rank Owner Job Files"; 76 static const char head1[] = "Total Size\n"; 77 78 static void alarmer(int); 79 static void blankfill(int); 80 static void dump(char *, char *, int); 81 static void header(void); 82 static void inform(char *, int); 83 static int inlist(char *, char *); 84 static void ldump(char *, char *, int); 85 static void nodaemon(void); 86 static void prank(int); 87 static void show(char *, char *, int); 88 89 /* 90 * Display the current state of the queue. Format = 1 if long format. 91 */ 92 void 93 displayq(int format) 94 { 95 struct queue *q; 96 int i, rank, nitems, fd, ret, len; 97 char *cp, *ecp, *p; 98 struct queue **queue; 99 struct winsize win; 100 struct stat statb; 101 FILE *fp; 102 103 termwidth = 0; 104 if ((p = getenv("COLUMNS")) != NULL) 105 termwidth = strtonum(p, 1, INT_MAX, NULL); 106 if (termwidth == 0 && ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && 107 win.ws_col > 0) 108 termwidth = win.ws_col; 109 if (termwidth == 0) 110 termwidth = 80; 111 112 if (termwidth < 60) 113 termwidth = 60; 114 115 lflag = format; 116 totsize = 0; 117 if ((i = cgetent(&bp, printcapdb, printer)) == -2) 118 fatal("can't open printer description file"); 119 else if (i == -1) 120 fatal("unknown printer"); 121 else if (i == -3) 122 fatal("potential reference loop detected in printcap file"); 123 if (cgetstr(bp, DEFLP, &LP) < 0) 124 LP = _PATH_DEFDEVLP; 125 if (cgetstr(bp, "rp", &RP) < 0) 126 RP = DEFLP; 127 if (cgetstr(bp, "sd", &SD) < 0) 128 SD = _PATH_DEFSPOOL; 129 if (cgetstr(bp, "lo", &LO) < 0) 130 LO = DEFLOCK; 131 if (cgetstr(bp, "st", &ST) < 0) 132 ST = DEFSTAT; 133 cgetstr(bp, "rm", &RM); 134 if ((cp = checkremote()) != NULL) 135 printf("Warning: %s\n", cp); 136 137 /* 138 * Print out local queue 139 * Find all the control files in the spooling directory 140 */ 141 PRIV_START; 142 if (chdir(SD) < 0) 143 fatal("cannot chdir to spooling directory"); 144 PRIV_END; 145 if ((nitems = getq(&queue)) < 0) 146 fatal("cannot examine spooling area"); 147 PRIV_START; 148 ret = stat(LO, &statb); 149 PRIV_END; 150 if (ret >= 0) { 151 if (statb.st_mode & S_IXUSR) { 152 if (remote) 153 printf("%s: ", host); 154 printf("Warning: %s is down: ", printer); 155 PRIV_START; 156 fd = safe_open(ST, O_RDONLY|O_NOFOLLOW, 0); 157 PRIV_END; 158 if (fd >= 0 && flock(fd, LOCK_SH) == 0) { 159 while ((i = read(fd, line, sizeof(line))) > 0) 160 (void)fwrite(line, 1, i, stdout); 161 (void)close(fd); /* unlocks as well */ 162 } else 163 putchar('\n'); 164 } 165 if (statb.st_mode & S_IXGRP) { 166 if (remote) 167 printf("%s: ", host); 168 printf("Warning: %s queue is turned off\n", printer); 169 } 170 } 171 172 if (nitems) { 173 PRIV_START; 174 fd = safe_open(LO, O_RDONLY|O_NOFOLLOW, 0); 175 PRIV_END; 176 if (fd < 0 || (fp = fdopen(fd, "r")) == NULL) { 177 if (fd >= 0) 178 close(fd); 179 nodaemon(); 180 } else { 181 /* get daemon pid */ 182 cp = current; 183 ecp = cp + sizeof(current) - 1; 184 while ((i = getc(fp)) != EOF && i != '\n') { 185 if (cp < ecp) 186 *cp++ = i; 187 } 188 *cp = '\0'; 189 i = atoi(current); 190 if (i <= 0) { 191 ret = -1; 192 } else { 193 PRIV_START; 194 ret = kill(i, 0); 195 PRIV_END; 196 } 197 if (ret < 0 && errno != EPERM) { 198 nodaemon(); 199 } else { 200 /* read current file name */ 201 cp = current; 202 ecp = cp + sizeof(current) - 1; 203 while ((i = getc(fp)) != EOF && i != '\n') { 204 if (cp < ecp) 205 *cp++ = i; 206 } 207 *cp = '\0'; 208 /* 209 * Print the status file. 210 */ 211 if (remote) 212 printf("%s: ", host); 213 PRIV_START; 214 fd = safe_open(ST, O_RDONLY|O_NOFOLLOW, 0); 215 PRIV_END; 216 if (fd >= 0 && flock(fd, LOCK_SH) == 0) { 217 while ((i = read(fd, line, sizeof(line))) > 0) 218 (void)fwrite(line, 1, i, stdout); 219 (void)close(fd); /* unlocks as well */ 220 } else 221 putchar('\n'); 222 } 223 (void)fclose(fp); 224 } 225 /* 226 * Now, examine the control files and print out the jobs to 227 * be done for each user. 228 */ 229 if (!lflag) 230 header(); 231 /* The currently printed job is treated specially. */ 232 if (!remote && current[0] != '\0') 233 inform(current, 0); 234 for (i = 0, rank = 1; i < nitems; i++) { 235 q = queue[i]; 236 if (remote || strcmp(current, q->q_name) != 0) 237 inform(q->q_name, rank++); 238 free(q); 239 } 240 } 241 free(queue); 242 if (!remote) { 243 if (nitems == 0) 244 puts("no entries"); 245 return; 246 } 247 248 /* 249 * Print foreign queue 250 * Note that a file in transit may show up in either queue. 251 */ 252 if (nitems) 253 putchar('\n'); 254 (void)snprintf(line, sizeof(line), "%c%s", format + '\3', RP); 255 cp = line; 256 cp += strlen(cp); 257 for (i = 0; i < requests && cp - line < sizeof(line) - 1; i++) { 258 len = line + sizeof(line) - cp; 259 if (snprintf(cp, len, " %d", requ[i]) >= len) { 260 cp += strlen(cp); 261 break; 262 } 263 cp += strlen(cp); 264 } 265 for (i = 0; i < users && cp - line < sizeof(line) - 1; i++) { 266 len = line + sizeof(line) - cp; 267 if (snprintf(cp, len, " %s", user[i]) >= len) { 268 cp += strlen(cp); 269 break; 270 } 271 } 272 if (cp-line < sizeof(line) - 1) 273 strlcat(line, "\n", sizeof(line)); 274 else 275 line[sizeof(line) - 2] = '\n'; 276 fd = getport(RM, 0); 277 if (fd < 0) { 278 if (from != host) 279 printf("%s: ", host); 280 (void)printf("connection to %s is down\n", RM); 281 } 282 else { 283 struct sigaction osa, nsa; 284 char *visline; 285 int n = 0; 286 287 i = strlen(line); 288 if (write(fd, line, i) != i) 289 fatal("Lost connection"); 290 memset(&nsa, 0, sizeof(nsa)); 291 nsa.sa_handler = alarmer; 292 sigemptyset(&nsa.sa_mask); 293 nsa.sa_flags = 0; 294 (void)sigaction(SIGALRM, &nsa, &osa); 295 alarm(wait_time); 296 if ((visline = malloc(4 * sizeof(line) + 1)) == NULL) 297 fatal("Out of memory"); 298 while ((i = read(fd, line, sizeof(line))) > 0) { 299 n = strvisx(visline, line, i, VIS_SAFE|VIS_NOSLASH); 300 (void)fwrite(visline, 1, n, stdout); 301 alarm(wait_time); 302 } 303 /* XXX some LPR implementations may not end stream with '\n' */ 304 if (n > 0 && visline[n-1] != '\n') 305 putchar('\n'); 306 alarm(0); 307 (void)sigaction(SIGALRM, &osa, NULL); 308 free(visline); 309 (void)close(fd); 310 } 311 } 312 313 static void 314 alarmer(int s) 315 { 316 /* nothing */ 317 } 318 319 /* 320 * Print a warning message if there is no daemon present. 321 */ 322 static void 323 nodaemon(void) 324 { 325 if (remote) 326 printf("\n%s: ", host); 327 puts("Warning: no daemon present"); 328 current[0] = '\0'; 329 } 330 331 /* 332 * Print the header for the short listing format 333 */ 334 static void 335 header(void) 336 { 337 printf(head0); 338 col = strlen(head0)+1; 339 blankfill(termwidth - (80 - SIZCOL)); 340 printf(head1); 341 } 342 343 static void 344 inform(char *cf, int rank) 345 { 346 int fd, j; 347 FILE *cfp = NULL; 348 349 /* 350 * There's a chance the control file has gone away 351 * in the meantime; if this is the case just keep going 352 */ 353 PRIV_START; 354 fd = safe_open(cf, O_RDONLY|O_NOFOLLOW, 0); 355 PRIV_END; 356 if (fd < 0 || (cfp = fdopen(fd, "r")) == NULL) { 357 if (fd >= 0) 358 close(fd); 359 return; 360 } 361 362 j = 0; 363 while (get_line(cfp)) { 364 switch (line[0]) { 365 case 'P': /* Was this file specified in the user's list? */ 366 if (!inlist(line+1, cf)) { 367 fclose(cfp); 368 return; 369 } 370 if (lflag) { 371 printf("\n%s: ", line+1); 372 col = strlen(line+1) + 2; 373 prank(rank); 374 blankfill(JOBCOL); 375 printf(" [job %s]\n", cf+3); 376 } else { 377 col = 0; 378 prank(rank); 379 blankfill(OWNCOL); 380 printf("%-10s %-3d ", line+1, atoi(cf+3)); 381 col += 16; 382 first = 1; 383 } 384 continue; 385 default: /* some format specifer and file name? */ 386 if (line[0] < 'a' || line[0] > 'z') 387 continue; 388 if (j == 0 || strcmp(file, line+1) != 0) 389 (void)strlcpy(file, line+1, sizeof(file)); 390 j++; 391 continue; 392 case 'N': 393 show(line+1, file, j); 394 file[0] = '\0'; 395 j = 0; 396 } 397 } 398 fclose(cfp); 399 if (!lflag) { 400 blankfill(termwidth - (80 - SIZCOL)); 401 printf("%lld bytes\n", (long long)totsize); 402 totsize = 0; 403 } 404 } 405 406 static int 407 inlist(char *name, char *file) 408 { 409 int *r, n; 410 char **u, *cp; 411 412 if (users == 0 && requests == 0) 413 return(1); 414 /* 415 * Check to see if it's in the user list 416 */ 417 for (u = user; u < &user[users]; u++) 418 if (!strcmp(*u, name)) 419 return(1); 420 /* 421 * Check the request list 422 */ 423 for (n = 0, cp = file+3; isdigit((unsigned char)*cp); ) 424 n = n * 10 + (*cp++ - '0'); 425 for (r = requ; r < &requ[requests]; r++) 426 if (*r == n && !strcmp(cp, from)) 427 return(1); 428 return(0); 429 } 430 431 static void 432 show(char *nfile, char *file, int copies) 433 { 434 if (strcmp(nfile, " ") == 0) 435 nfile = "(standard input)"; 436 if (lflag) 437 ldump(nfile, file, copies); 438 else 439 dump(nfile, file, copies); 440 } 441 442 /* 443 * Fill the line with blanks to the specified column 444 */ 445 static void 446 blankfill(int n) 447 { 448 while (col++ < n) 449 putchar(' '); 450 } 451 452 /* 453 * Give the abbreviated dump of the file names 454 */ 455 static void 456 dump(char *nfile, char *file, int copies) 457 { 458 int n, fill; 459 struct stat lbuf; 460 461 /* 462 * Print as many files as will fit 463 * (leaving room for the total size) 464 */ 465 fill = first ? 0 : 2; /* fill space for ``, '' */ 466 if (((n = strlen(nfile)) + col + fill) >= 467 (termwidth - (80 - SIZCOL)) - 4) { 468 if (col < (termwidth - (80 - SIZCOL))) { 469 printf(" ..."), col += 4; 470 blankfill(termwidth - (80 - SIZCOL)); 471 } 472 } else { 473 if (first) 474 first = 0; 475 else 476 printf(", "); 477 printf("%s", nfile); 478 col += n+fill; 479 } 480 PRIV_START; 481 if (*file && !stat(file, &lbuf)) 482 totsize += copies * lbuf.st_size; 483 PRIV_END; 484 } 485 486 /* 487 * Print the long info about the file 488 */ 489 static void 490 ldump(char *nfile, char *file, int copies) 491 { 492 struct stat lbuf; 493 int ret; 494 495 putchar('\t'); 496 if (copies > 1) 497 printf("%-2d copies of %-19s", copies, nfile); 498 else 499 printf("%-32s", nfile); 500 PRIV_START; 501 ret = stat(file, &lbuf); 502 PRIV_END; 503 if (*file && !ret) 504 printf(" %lld bytes", (long long)lbuf.st_size); 505 else 506 printf(" ??? bytes"); 507 putchar('\n'); 508 } 509 510 /* 511 * Print the job's rank in the queue, 512 * update col for screen management 513 */ 514 static void 515 prank(int n) 516 { 517 char rline[100]; 518 static char *r[] = { 519 "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" 520 }; 521 522 if (n == 0) { 523 printf("active"); 524 col += 6; 525 return; 526 } 527 if ((n/10)%10 == 1) 528 (void)snprintf(rline, sizeof(rline), "%dth", n); 529 else 530 (void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]); 531 col += strlen(rline); 532 printf("%s", rline); 533 } 534