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