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