1 /* $OpenBSD: displayq.c,v 1.16 2002/05/20 23:13:50 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.16 2002/05/20 23:13:50 millert Exp $"; 42 #endif 43 #endif /* not lint */ 44 45 #include <sys/param.h> 46 #include <sys/stat.h> 47 #include <sys/file.h> 48 49 #include <signal.h> 50 #include <fcntl.h> 51 #include <sys/ioctl.h> 52 #include <dirent.h> 53 #include <unistd.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <ctype.h> 58 #include "lp.h" 59 #include "lp.local.h" 60 #include "pathnames.h" 61 62 /* 63 * Routines to display the state of the queue. 64 */ 65 #define JOBCOL 40 /* column for job # in -l format */ 66 #define OWNCOL 7 /* start of Owner column in normal */ 67 #define SIZCOL 62 /* start of Size column in normal */ 68 69 /* 70 * Stuff for handling job specifications 71 */ 72 extern int requ[]; /* job number of spool entries */ 73 extern int requests; /* # of spool requests */ 74 extern char *user[]; /* users to process */ 75 extern int users; /* # of users in user array */ 76 77 static int termwidth; 78 static int col; /* column on screen */ 79 static char current[NAME_MAX]; /* current file being printed */ 80 static char file[NAME_MAX]; /* print file name */ 81 static int first; /* first file in ``files'' column? */ 82 static int garbage; /* # of garbage cf files */ 83 static int lflag; /* long output option */ 84 static int rank; /* order to be printed (-1=none, 0=active) */ 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 92 /* 93 * Display the current state of the queue. Format = 1 if long format. 94 */ 95 void 96 displayq(int format) 97 { 98 struct queue *q; 99 int i, nitems, fd, ret, len; 100 char *cp, *ecp, *p; 101 struct queue **queue; 102 struct winsize win; 103 struct stat statb; 104 FILE *fp; 105 106 termwidth = 80; 107 if (isatty(STDOUT_FILENO)) { 108 if ((p = getenv("COLUMNS")) != NULL) 109 termwidth = atoi(p); 110 else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && 111 win.ws_col > 0) 112 termwidth = win.ws_col; 113 } 114 115 lflag = format; 116 totsize = 0; 117 rank = -1; 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 seteuid(euid); 143 if (chdir(SD) < 0) 144 fatal("cannot chdir to spooling directory"); 145 seteuid(uid); 146 if ((nitems = getq(&queue)) < 0) 147 fatal("cannot examine spooling area\n"); 148 seteuid(euid); 149 ret = stat(LO, &statb); 150 seteuid(uid); 151 if (ret >= 0) { 152 if (statb.st_mode & 0100) { 153 if (remote) 154 printf("%s: ", host); 155 printf("Warning: %s is down: ", printer); 156 seteuid(euid); 157 fd = open(ST, O_RDONLY); 158 seteuid(uid); 159 if (fd >= 0) { 160 (void)flock(fd, LOCK_SH); 161 while ((i = read(fd, line, sizeof(line))) > 0) 162 (void)fwrite(line, 1, i, stdout); 163 (void)close(fd); /* unlocks as well */ 164 } else 165 putchar('\n'); 166 } 167 if (statb.st_mode & 010) { 168 if (remote) 169 printf("%s: ", host); 170 printf("Warning: %s queue is turned off\n", printer); 171 } 172 } 173 174 if (nitems) { 175 seteuid(euid); 176 fp = fopen(LO, "r"); 177 seteuid(uid); 178 if (fp == NULL) 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 seteuid(euid); 194 ret = kill(i, 0); 195 seteuid(uid); 196 } 197 if (ret < 0) { 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 seteuid(euid); 214 fd = open(ST, O_RDONLY); 215 seteuid(uid); 216 if (fd >= 0) { 217 (void)flock(fd, LOCK_SH); 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 inform(q->q_name); 235 free(q); 236 } 237 free(queue); 238 } 239 if (!remote) { 240 if (nitems == 0) 241 puts("no entries"); 242 return; 243 } 244 245 /* 246 * Print foreign queue 247 * Note that a file in transit may show up in either queue. 248 */ 249 if (nitems) 250 putchar('\n'); 251 (void)snprintf(line, sizeof(line), "%c%s", format + '\3', RP); 252 cp = line; 253 cp += strlen(cp); 254 for (i = 0; i < requests && cp - line < sizeof(line) - 1; i++) { 255 len = line + sizeof(line) - cp; 256 if (snprintf(cp, len, " %d", requ[i]) >= len) { 257 cp += strlen(cp); 258 break; 259 } 260 cp += strlen(cp); 261 } 262 for (i = 0; i < users && cp - line < sizeof(line) - 1; i++) { 263 len = line + sizeof(line) - cp; 264 if (snprintf(cp, len, " %s", user[i]) >= len) { 265 cp += strlen(cp); 266 break; 267 } 268 } 269 if (cp-line < sizeof(line) - 1) 270 strcat(line, "\n"); 271 else 272 line[sizeof(line) - 2] = '\n'; 273 fd = getport(RM, 0); 274 if (fd < 0) { 275 if (from != host) 276 printf("%s: ", host); 277 (void)printf("connection to %s is down\n", RM); 278 } 279 else { 280 struct sigaction osa, nsa; 281 282 i = strlen(line); 283 if (write(fd, line, i) != i) 284 fatal("Lost connection"); 285 memset(&nsa, 0, sizeof(nsa)); 286 nsa.sa_handler = alarmer; 287 sigemptyset(&nsa.sa_mask); 288 sigaddset(&nsa.sa_mask, SIGALRM); 289 nsa.sa_flags = 0; 290 (void)sigaction(SIGALRM, &nsa, &osa); 291 alarm(wait_time); 292 while ((i = read(fd, line, sizeof(line))) > 0) { 293 (void)fwrite(line, 1, i, stdout); 294 alarm(wait_time); 295 } 296 alarm(0); 297 (void)sigaction(SIGALRM, &osa, NULL); 298 (void)close(fd); 299 } 300 } 301 302 static void 303 alarmer(int s) 304 { 305 /* nothing */ 306 } 307 308 /* 309 * Print a warning message if there is no daemon present. 310 */ 311 void 312 nodaemon(void) 313 { 314 if (remote) 315 printf("\n%s: ", host); 316 puts("Warning: no daemon present"); 317 current[0] = '\0'; 318 } 319 320 /* 321 * Print the header for the short listing format 322 */ 323 void 324 header(void) 325 { 326 printf(head0); 327 col = strlen(head0)+1; 328 blankfill(termwidth - (80 - SIZCOL)); 329 printf(head1); 330 } 331 332 void 333 inform(char *cf) 334 { 335 int j; 336 FILE *cfp; 337 338 /* 339 * There's a chance the control file has gone away 340 * in the meantime; if this is the case just keep going 341 */ 342 seteuid(euid); 343 if ((cfp = fopen(cf, "r")) == NULL) 344 return; 345 seteuid(uid); 346 347 if (rank < 0) 348 rank = 0; 349 if (remote || garbage || strcmp(cf, current)) 350 rank++; 351 j = 0; 352 while (getline(cfp)) { 353 switch (line[0]) { 354 case 'P': /* Was this file specified in the user's list? */ 355 if (!inlist(line+1, cf)) { 356 fclose(cfp); 357 return; 358 } 359 if (lflag) { 360 printf("\n%s: ", line+1); 361 col = strlen(line+1) + 2; 362 prank(rank); 363 blankfill(JOBCOL); 364 printf(" [job %s]\n", cf+3); 365 } else { 366 col = 0; 367 prank(rank); 368 blankfill(OWNCOL); 369 printf("%-10s %-3d ", line+1, atoi(cf+3)); 370 col += 16; 371 first = 1; 372 } 373 continue; 374 default: /* some format specifer and file name? */ 375 if (line[0] < 'a' || line[0] > 'z') 376 continue; 377 if (j == 0 || strcmp(file, line+1) != 0) 378 (void)strlcpy(file, line+1, sizeof(file)); 379 j++; 380 continue; 381 case 'N': 382 show(line+1, file, j); 383 file[0] = '\0'; 384 j = 0; 385 } 386 } 387 fclose(cfp); 388 if (!lflag) { 389 blankfill(termwidth - (80 - SIZCOL)); 390 printf("%lld bytes\n", (long long)totsize); 391 totsize = 0; 392 } 393 } 394 395 int 396 inlist(char *name, char *file) 397 { 398 int *r, n; 399 char **u, *cp; 400 401 if (users == 0 && requests == 0) 402 return(1); 403 /* 404 * Check to see if it's in the user list 405 */ 406 for (u = user; u < &user[users]; u++) 407 if (!strcmp(*u, name)) 408 return(1); 409 /* 410 * Check the request list 411 */ 412 for (n = 0, cp = file+3; isdigit(*cp); ) 413 n = n * 10 + (*cp++ - '0'); 414 for (r = requ; r < &requ[requests]; r++) 415 if (*r == n && !strcmp(cp, from)) 416 return(1); 417 return(0); 418 } 419 420 void 421 show(char *nfile, char *file, int copies) 422 { 423 if (strcmp(nfile, " ") == 0) 424 nfile = "(standard input)"; 425 if (lflag) 426 ldump(nfile, file, copies); 427 else 428 dump(nfile, file, copies); 429 } 430 431 /* 432 * Fill the line with blanks to the specified column 433 */ 434 void 435 blankfill(int n) 436 { 437 while (col++ < n) 438 putchar(' '); 439 } 440 441 /* 442 * Give the abbreviated dump of the file names 443 */ 444 void 445 dump(char *nfile, char *file, int copies) 446 { 447 int n, fill; 448 struct stat lbuf; 449 450 /* 451 * Print as many files as will fit 452 * (leaving room for the total size) 453 */ 454 fill = first ? 0 : 2; /* fill space for ``, '' */ 455 if (((n = strlen(nfile)) + col + fill) >= 456 (termwidth - (80 - SIZCOL)) - 4) { 457 if (col < (termwidth - (80 - SIZCOL))) { 458 printf(" ..."), col += 4; 459 blankfill(termwidth - (80 - SIZCOL)); 460 } 461 } else { 462 if (first) 463 first = 0; 464 else 465 printf(", "); 466 printf("%s", nfile); 467 col += n+fill; 468 } 469 seteuid(euid); 470 if (*file && !stat(file, &lbuf)) 471 totsize += copies * lbuf.st_size; 472 seteuid(uid); 473 } 474 475 /* 476 * Print the long info about the file 477 */ 478 void 479 ldump(char *nfile, char *file, int copies) 480 { 481 struct stat lbuf; 482 483 putchar('\t'); 484 if (copies > 1) 485 printf("%-2d copies of %-19s", copies, nfile); 486 else 487 printf("%-32s", nfile); 488 if (*file && !stat(file, &lbuf)) 489 printf(" %lld bytes", (long long)lbuf.st_size); 490 else 491 printf(" ??? bytes"); 492 putchar('\n'); 493 } 494 495 /* 496 * Print the job's rank in the queue, 497 * update col for screen management 498 */ 499 void 500 prank(int n) 501 { 502 char rline[100]; 503 static char *r[] = { 504 "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" 505 }; 506 507 if (n == 0) { 508 printf("active"); 509 col += 6; 510 return; 511 } 512 if ((n/10)%10 == 1) 513 (void)snprintf(rline, sizeof(rline), "%dth", n); 514 else 515 (void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]); 516 col += strlen(rline); 517 printf("%s", rline); 518 } 519