1 /* $OpenBSD: displayq.c,v 1.21 2002/06/09 21:42:02 millert 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. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 #if 0 39 static const char sccsid[] = "@(#)displayq.c 8.4 (Berkeley) 4/28/95"; 40 #else 41 static const char rcsid[] = "$OpenBSD: displayq.c,v 1.21 2002/06/09 21:42:02 millert Exp $"; 42 #endif 43 #endif /* not lint */ 44 45 #include <sys/param.h> 46 #include <sys/file.h> 47 #include <sys/ioctl.h> 48 #include <sys/stat.h> 49 50 #include <ctype.h> 51 #include <errno.h> 52 #include <dirent.h> 53 #include <fcntl.h> 54 #include <signal.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <unistd.h> 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 * Stuff for handling job specifications 73 */ 74 extern int requ[]; /* job number of spool entries */ 75 extern int requests; /* # of spool requests */ 76 extern char *user[]; /* users to process */ 77 extern int users; /* # of users in user array */ 78 79 static int termwidth; 80 static int col; /* column on screen */ 81 static char current[NAME_MAX]; /* current file being printed */ 82 static char file[NAME_MAX]; /* print file name */ 83 static int first; /* first file in ``files'' column? */ 84 static int lflag; /* long output option */ 85 static off_t totsize; /* total print job size in bytes */ 86 87 static const char *head0 = "Rank Owner Job Files"; 88 static const char *head1 = "Total Size\n"; 89 90 static void alarmer(int); 91 static void inform(char *, int); 92 93 /* 94 * Display the current state of the queue. Format = 1 if long format. 95 */ 96 void 97 displayq(int format) 98 { 99 struct queue *q; 100 int i, nitems, fd, ret, len; 101 char *cp, *ecp, *p; 102 struct queue **queue; 103 struct winsize win; 104 struct stat statb; 105 FILE *fp; 106 107 termwidth = 80; 108 if (isatty(STDOUT_FILENO)) { 109 if ((p = getenv("COLUMNS")) != NULL) 110 termwidth = atoi(p); 111 else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && 112 win.ws_col > 0) 113 termwidth = win.ws_col; 114 } 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\n"); 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 for (i = 0; i < nitems; i++) { 233 q = queue[i]; 234 /* 235 * If this is a local job that is currently 236 * printing, use job #0 which inform() will 237 * convert to "active". Otherwise, we start 238 * counting from 1... 239 */ 240 if (!remote && strcmp(current, q->q_name) == 0) 241 inform(q->q_name, 0); 242 else 243 inform(q->q_name, i + 1); 244 free(q); 245 } 246 free(queue); 247 } 248 if (!remote) { 249 if (nitems == 0) 250 puts("no entries"); 251 return; 252 } 253 254 /* 255 * Print foreign queue 256 * Note that a file in transit may show up in either queue. 257 */ 258 if (nitems) 259 putchar('\n'); 260 (void)snprintf(line, sizeof(line), "%c%s", format + '\3', RP); 261 cp = line; 262 cp += strlen(cp); 263 for (i = 0; i < requests && cp - line < sizeof(line) - 1; i++) { 264 len = line + sizeof(line) - cp; 265 if (snprintf(cp, len, " %d", requ[i]) >= len) { 266 cp += strlen(cp); 267 break; 268 } 269 cp += strlen(cp); 270 } 271 for (i = 0; i < users && cp - line < sizeof(line) - 1; i++) { 272 len = line + sizeof(line) - cp; 273 if (snprintf(cp, len, " %s", user[i]) >= len) { 274 cp += strlen(cp); 275 break; 276 } 277 } 278 if (cp-line < sizeof(line) - 1) 279 strcat(line, "\n"); 280 else 281 line[sizeof(line) - 2] = '\n'; 282 fd = getport(RM, 0); 283 if (fd < 0) { 284 if (from != host) 285 printf("%s: ", host); 286 (void)printf("connection to %s is down\n", RM); 287 } 288 else { 289 struct sigaction osa, nsa; 290 291 i = strlen(line); 292 if (write(fd, line, i) != i) 293 fatal("Lost connection"); 294 memset(&nsa, 0, sizeof(nsa)); 295 nsa.sa_handler = alarmer; 296 sigemptyset(&nsa.sa_mask); 297 nsa.sa_flags = 0; 298 (void)sigaction(SIGALRM, &nsa, &osa); 299 alarm(wait_time); 300 while ((i = read(fd, line, sizeof(line))) > 0) { 301 (void)fwrite(line, 1, i, stdout); 302 alarm(wait_time); 303 } 304 alarm(0); 305 (void)sigaction(SIGALRM, &osa, NULL); 306 (void)close(fd); 307 } 308 } 309 310 static void 311 alarmer(int s) 312 { 313 /* nothing */ 314 } 315 316 /* 317 * Print a warning message if there is no daemon present. 318 */ 319 void 320 nodaemon(void) 321 { 322 if (remote) 323 printf("\n%s: ", host); 324 puts("Warning: no daemon present"); 325 current[0] = '\0'; 326 } 327 328 /* 329 * Print the header for the short listing format 330 */ 331 void 332 header(void) 333 { 334 printf(head0); 335 col = strlen(head0)+1; 336 blankfill(termwidth - (80 - SIZCOL)); 337 printf(head1); 338 } 339 340 static void 341 inform(char *cf, int rank) 342 { 343 int fd, j; 344 FILE *cfp = NULL; 345 346 /* 347 * There's a chance the control file has gone away 348 * in the meantime; if this is the case just keep going 349 */ 350 PRIV_START; 351 fd = safe_open(cf, O_RDONLY|O_NOFOLLOW, 0); 352 PRIV_END; 353 if (fd < 0 || (cfp = fdopen(fd, "r")) == NULL) { 354 if (fd >= 0) 355 close(fd); 356 return; 357 } 358 359 j = 0; 360 while (getline(cfp)) { 361 switch (line[0]) { 362 case 'P': /* Was this file specified in the user's list? */ 363 if (!inlist(line+1, cf)) { 364 fclose(cfp); 365 return; 366 } 367 if (lflag) { 368 printf("\n%s: ", line+1); 369 col = strlen(line+1) + 2; 370 prank(rank); 371 blankfill(JOBCOL); 372 printf(" [job %s]\n", cf+3); 373 } else { 374 col = 0; 375 prank(rank); 376 blankfill(OWNCOL); 377 printf("%-10s %-3d ", line+1, atoi(cf+3)); 378 col += 16; 379 first = 1; 380 } 381 continue; 382 default: /* some format specifer and file name? */ 383 if (line[0] < 'a' || line[0] > 'z') 384 continue; 385 if (j == 0 || strcmp(file, line+1) != 0) 386 (void)strlcpy(file, line+1, sizeof(file)); 387 j++; 388 continue; 389 case 'N': 390 show(line+1, file, j); 391 file[0] = '\0'; 392 j = 0; 393 } 394 } 395 fclose(cfp); 396 if (!lflag) { 397 blankfill(termwidth - (80 - SIZCOL)); 398 printf("%lld bytes\n", (long long)totsize); 399 totsize = 0; 400 } 401 } 402 403 int 404 inlist(char *name, char *file) 405 { 406 int *r, n; 407 char **u, *cp; 408 409 if (users == 0 && requests == 0) 410 return(1); 411 /* 412 * Check to see if it's in the user list 413 */ 414 for (u = user; u < &user[users]; u++) 415 if (!strcmp(*u, name)) 416 return(1); 417 /* 418 * Check the request list 419 */ 420 for (n = 0, cp = file+3; isdigit(*cp); ) 421 n = n * 10 + (*cp++ - '0'); 422 for (r = requ; r < &requ[requests]; r++) 423 if (*r == n && !strcmp(cp, from)) 424 return(1); 425 return(0); 426 } 427 428 void 429 show(char *nfile, char *file, int copies) 430 { 431 if (strcmp(nfile, " ") == 0) 432 nfile = "(standard input)"; 433 if (lflag) 434 ldump(nfile, file, copies); 435 else 436 dump(nfile, file, copies); 437 } 438 439 /* 440 * Fill the line with blanks to the specified column 441 */ 442 void 443 blankfill(int n) 444 { 445 while (col++ < n) 446 putchar(' '); 447 } 448 449 /* 450 * Give the abbreviated dump of the file names 451 */ 452 void 453 dump(char *nfile, char *file, int copies) 454 { 455 int n, fill; 456 struct stat lbuf; 457 458 /* 459 * Print as many files as will fit 460 * (leaving room for the total size) 461 */ 462 fill = first ? 0 : 2; /* fill space for ``, '' */ 463 if (((n = strlen(nfile)) + col + fill) >= 464 (termwidth - (80 - SIZCOL)) - 4) { 465 if (col < (termwidth - (80 - SIZCOL))) { 466 printf(" ..."), col += 4; 467 blankfill(termwidth - (80 - SIZCOL)); 468 } 469 } else { 470 if (first) 471 first = 0; 472 else 473 printf(", "); 474 printf("%s", nfile); 475 col += n+fill; 476 } 477 PRIV_START; 478 if (*file && !stat(file, &lbuf)) 479 totsize += copies * lbuf.st_size; 480 PRIV_END; 481 } 482 483 /* 484 * Print the long info about the file 485 */ 486 void 487 ldump(char *nfile, char *file, int copies) 488 { 489 struct stat lbuf; 490 491 putchar('\t'); 492 if (copies > 1) 493 printf("%-2d copies of %-19s", copies, nfile); 494 else 495 printf("%-32s", nfile); 496 if (*file && !stat(file, &lbuf)) 497 printf(" %lld bytes", (long long)lbuf.st_size); 498 else 499 printf(" ??? bytes"); 500 putchar('\n'); 501 } 502 503 /* 504 * Print the job's rank in the queue, 505 * update col for screen management 506 */ 507 void 508 prank(int n) 509 { 510 char rline[100]; 511 static char *r[] = { 512 "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" 513 }; 514 515 if (n == 0) { 516 printf("active"); 517 col += 6; 518 return; 519 } 520 if ((n/10)%10 == 1) 521 (void)snprintf(rline, sizeof(rline), "%dth", n); 522 else 523 (void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]); 524 col += strlen(rline); 525 printf("%s", rline); 526 } 527