1 /* 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 /* 36 static char sccsid[] = "@(#)displayq.c 8.4 (Berkeley) 4/28/95"; 37 */ 38 static const char rcsid[] = 39 "$FreeBSD$"; 40 #endif /* not lint */ 41 42 #include <sys/param.h> 43 #include <sys/stat.h> 44 45 #include <ctype.h> 46 #include <dirent.h> 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <signal.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #define psignal foil_gcc_psignal 54 #define sys_siglist foil_gcc_siglist 55 #include <unistd.h> 56 #undef psignal 57 #undef sys_siglist 58 59 #include "lp.h" 60 #include "lp.local.h" 61 #include "pathnames.h" 62 63 /* 64 * Routines to display the state of the queue. 65 */ 66 #define JOBCOL 40 /* column for job # in -l format */ 67 #define OWNCOL 7 /* start of Owner column in normal */ 68 #define SIZCOL 62 /* start of Size column in normal */ 69 70 /* 71 * Stuff for handling job specifications 72 */ 73 extern uid_t uid, euid; 74 75 static int col; /* column on screen */ 76 static char current[MAXNAMLEN+1]; /* current file being printed */ 77 static char file[MAXNAMLEN+1]; /* print file name */ 78 static int first; /* first file in ``files'' column? */ 79 static int garbage; /* # of garbage cf files */ 80 static int lflag; /* long output option */ 81 static int rank; /* order to be printed (-1=none, 0=active) */ 82 static long totsize; /* total print job size in bytes */ 83 84 static const char *head0 = "Rank Owner Job Files"; 85 static const char *head1 = "Total Size\n"; 86 87 static void alarmhandler(int _signo); 88 static void warn(const struct printer *_pp); 89 90 /* 91 * Display the current state of the queue. Format = 1 if long format. 92 */ 93 void 94 displayq(struct printer *pp, int format) 95 { 96 register struct jobqueue *q; 97 register int i, nitems, fd, ret; 98 char *cp, *endp; 99 struct jobqueue **queue; 100 struct stat statb; 101 FILE *fp; 102 void (*savealrm)(int); 103 104 lflag = format; 105 totsize = 0; 106 rank = -1; 107 108 if ((cp = checkremote(pp))) { 109 printf("Warning: %s\n", cp); 110 free(cp); 111 } 112 113 /* 114 * Print out local queue 115 * Find all the control files in the spooling directory 116 */ 117 seteuid(euid); 118 if (chdir(pp->spool_dir) < 0) 119 fatal(pp, "cannot chdir to spooling directory: %s", 120 strerror(errno)); 121 seteuid(uid); 122 if ((nitems = getq(pp, &queue)) < 0) 123 fatal(pp, "cannot examine spooling area\n"); 124 seteuid(euid); 125 ret = stat(pp->lock_file, &statb); 126 seteuid(uid); 127 if (ret >= 0) { 128 if (statb.st_mode & LFM_PRINT_DIS) { 129 if (pp->remote) 130 printf("%s: ", local_host); 131 printf("Warning: %s is down: ", pp->printer); 132 seteuid(euid); 133 fd = open(pp->status_file, O_RDONLY|O_SHLOCK); 134 seteuid(uid); 135 if (fd >= 0) { 136 while ((i = read(fd, line, sizeof(line))) > 0) 137 (void) fwrite(line, 1, i, stdout); 138 (void) close(fd); /* unlocks as well */ 139 } else 140 putchar('\n'); 141 } 142 if (statb.st_mode & LFM_QUEUE_DIS) { 143 if (pp->remote) 144 printf("%s: ", local_host); 145 printf("Warning: %s queue is turned off\n", 146 pp->printer); 147 } 148 } 149 150 if (nitems) { 151 seteuid(euid); 152 fp = fopen(pp->lock_file, "r"); 153 seteuid(uid); 154 if (fp == NULL) 155 warn(pp); 156 else { 157 /* get daemon pid */ 158 cp = current; 159 endp = cp + sizeof(current) - 1; 160 while ((i = getc(fp)) != EOF && i != '\n') { 161 if (cp < endp) 162 *cp++ = i; 163 } 164 *cp = '\0'; 165 i = atoi(current); 166 if (i <= 0) { 167 ret = -1; 168 } else { 169 seteuid(euid); 170 ret = kill(i, 0); 171 seteuid(uid); 172 } 173 if (ret < 0) { 174 warn(pp); 175 } else { 176 /* read current file name */ 177 cp = current; 178 endp = cp + sizeof(current) - 1; 179 while ((i = getc(fp)) != EOF && i != '\n') { 180 if (cp < endp) 181 *cp++ = i; 182 } 183 *cp = '\0'; 184 /* 185 * Print the status file. 186 */ 187 if (pp->remote) 188 printf("%s: ", local_host); 189 seteuid(euid); 190 fd = open(pp->status_file, O_RDONLY|O_SHLOCK); 191 seteuid(uid); 192 if (fd >= 0) { 193 while ((i = read(fd, line, 194 sizeof(line))) > 0) 195 fwrite(line, 1, i, stdout); 196 close(fd); /* unlocks as well */ 197 } else 198 putchar('\n'); 199 } 200 (void) fclose(fp); 201 } 202 /* 203 * Now, examine the control files and print out the jobs to 204 * be done for each user. 205 */ 206 if (!lflag) 207 header(); 208 for (i = 0; i < nitems; i++) { 209 q = queue[i]; 210 inform(pp, q->job_cfname); 211 free(q); 212 } 213 free(queue); 214 } 215 if (!pp->remote) { 216 if (nitems == 0) 217 puts("no entries"); 218 return; 219 } 220 221 /* 222 * Print foreign queue 223 * Note that a file in transit may show up in either queue. 224 */ 225 if (nitems) 226 putchar('\n'); 227 (void) snprintf(line, sizeof(line), "%c%s", format ? '\4' : '\3', 228 pp->remote_queue); 229 cp = line; 230 for (i = 0; i < requests && cp-line+10 < sizeof(line) - 1; i++) { 231 cp += strlen(cp); 232 (void) sprintf(cp, " %d", requ[i]); 233 } 234 for (i = 0; i < users && cp - line + 1 + strlen(user[i]) < 235 sizeof(line) - 1; i++) { 236 cp += strlen(cp); 237 *cp++ = ' '; 238 (void) strcpy(cp, user[i]); 239 } 240 strcat(line, "\n"); 241 savealrm = signal(SIGALRM, alarmhandler); 242 alarm(pp->conn_timeout); 243 fd = getport(pp, pp->remote_host, 0); 244 alarm(0); 245 (void)signal(SIGALRM, savealrm); 246 if (fd < 0) { 247 if (from_host != local_host) 248 printf("%s: ", local_host); 249 printf("connection to %s is down\n", pp->remote_host); 250 } 251 else { 252 i = strlen(line); 253 if (write(fd, line, i) != i) 254 fatal(pp, "Lost connection"); 255 while ((i = read(fd, line, sizeof(line))) > 0) 256 (void) fwrite(line, 1, i, stdout); 257 (void) close(fd); 258 } 259 } 260 261 /* 262 * Print a warning message if there is no daemon present. 263 */ 264 static void 265 warn(const struct printer *pp) 266 { 267 if (pp->remote) 268 printf("%s: ", local_host); 269 puts("Warning: no daemon present"); 270 current[0] = '\0'; 271 } 272 273 /* 274 * Print the header for the short listing format 275 */ 276 void 277 header(void) 278 { 279 printf("%s", head0); 280 col = strlen(head0)+1; 281 blankfill(SIZCOL); 282 printf("%s", head1); 283 } 284 285 void 286 inform(const struct printer *pp, char *cf) 287 { 288 register int copycnt; 289 char savedname[MAXPATHLEN+1]; 290 FILE *cfp; 291 292 /* 293 * There's a chance the control file has gone away 294 * in the meantime; if this is the case just keep going 295 */ 296 seteuid(euid); 297 if ((cfp = fopen(cf, "r")) == NULL) 298 return; 299 seteuid(uid); 300 301 if (rank < 0) 302 rank = 0; 303 if (pp->remote || garbage || strcmp(cf, current)) 304 rank++; 305 306 /* 307 * The cf-file may include commands to print more than one datafile 308 * from the user. For each datafile, the cf-file contains at least 309 * one line which starts with some format-specifier ('a'-'z'), and 310 * a second line ('N'ame) which indicates the original name the user 311 * specified for that file. There can be multiple format-spec lines 312 * for a single Name-line, if the user requested multiple copies of 313 * that file. Standard lpr puts the format-spec line(s) before the 314 * Name-line, while lprNG puts the Name-line before the format-spec 315 * line(s). This section needs to handle the lines in either order. 316 */ 317 copycnt = 0; 318 file[0] = '\0'; 319 savedname[0] = '\0'; 320 while (getline(cfp)) { 321 switch (line[0]) { 322 case 'P': /* Was this file specified in the user's list? */ 323 if (!inlist(line+1, cf)) { 324 fclose(cfp); 325 return; 326 } 327 if (lflag) { 328 printf("\n%s: ", line+1); 329 col = strlen(line+1) + 2; 330 prank(rank); 331 blankfill(JOBCOL); 332 printf(" [job %s]\n", cf+3); 333 } else { 334 col = 0; 335 prank(rank); 336 blankfill(OWNCOL); 337 printf("%-10s %-3d ", line+1, atoi(cf+3)); 338 col += 16; 339 first = 1; 340 } 341 continue; 342 default: /* some format specifer and file name? */ 343 if (line[0] < 'a' || line[0] > 'z') 344 break; 345 if (copycnt == 0 || strcmp(file, line+1) != 0) { 346 strlcpy(file, line + 1, sizeof(file)); 347 } 348 copycnt++; 349 /* 350 * deliberately 'continue' to another getline(), so 351 * all format-spec lines for this datafile are read 352 * in and counted before calling show() 353 */ 354 continue; 355 case 'N': 356 strlcpy(savedname, line + 1, sizeof(savedname)); 357 break; 358 } 359 if ((file[0] != '\0') && (savedname[0] != '\0')) { 360 show(savedname, file, copycnt); 361 copycnt = 0; 362 file[0] = '\0'; 363 savedname[0] = '\0'; 364 } 365 } 366 fclose(cfp); 367 /* check for a file which hasn't been shown yet */ 368 if (file[0] != '\0') { 369 if (savedname[0] == '\0') { 370 /* a safeguard in case the N-ame line is missing */ 371 strlcpy(savedname, file, sizeof(savedname)); 372 } 373 show(savedname, file, copycnt); 374 } 375 if (!lflag) { 376 blankfill(SIZCOL); 377 printf("%ld bytes\n", totsize); 378 totsize = 0; 379 } 380 } 381 382 int 383 inlist(char *uname, char *cfile) 384 { 385 register int *r, n; 386 register char **u, *cp; 387 388 if (users == 0 && requests == 0) 389 return(1); 390 /* 391 * Check to see if it's in the user list 392 */ 393 for (u = user; u < &user[users]; u++) 394 if (!strcmp(*u, uname)) 395 return(1); 396 /* 397 * Check the request list 398 */ 399 for (n = 0, cp = cfile+3; isdigit(*cp); ) 400 n = n * 10 + (*cp++ - '0'); 401 for (r = requ; r < &requ[requests]; r++) 402 if (*r == n && !strcmp(cp, from_host)) 403 return(1); 404 return(0); 405 } 406 407 void 408 show(const char *nfile, const char *datafile, int copies) 409 { 410 if (strcmp(nfile, " ") == 0) 411 nfile = "(standard input)"; 412 if (lflag) 413 ldump(nfile, datafile, copies); 414 else 415 dump(nfile, datafile, copies); 416 } 417 418 /* 419 * Fill the line with blanks to the specified column 420 */ 421 void 422 blankfill(int tocol) 423 { 424 while (col++ < tocol) 425 putchar(' '); 426 } 427 428 /* 429 * Give the abbreviated dump of the file names 430 */ 431 void 432 dump(const char *nfile, const char *datafile, int copies) 433 { 434 struct stat lbuf; 435 const char etctmpl[] = ", ..."; 436 char etc[sizeof(etctmpl)]; 437 char *lastsep; 438 short fill, nlen; 439 short rem, remetc; 440 441 /* 442 * Print as many filenames as will fit 443 * (leaving room for the 'total size' field) 444 */ 445 fill = first ? 0 : 2; /* fill space for ``, '' */ 446 nlen = strlen(nfile); 447 rem = SIZCOL - 1 - col; 448 if (nlen + fill > rem) { 449 if (first) { 450 /* print the right-most part of the name */ 451 printf("...%s ", &nfile[3+nlen-rem]); 452 col = SIZCOL; 453 } else if (rem > 0) { 454 /* fit as much of the etc-string as we can */ 455 remetc = rem; 456 if (rem > strlen(etctmpl)) 457 remetc = strlen(etctmpl); 458 etc[0] = '\0'; 459 strncat(etc, etctmpl, remetc); 460 printf("%s", etc); 461 col += remetc; 462 rem -= remetc; 463 /* room for the last segment of this filename? */ 464 lastsep = strrchr(nfile, '/'); 465 if ((lastsep != NULL) && (rem > strlen(lastsep))) { 466 /* print the right-most part of this name */ 467 printf("%s", lastsep); 468 col += strlen(lastsep); 469 } else { 470 /* do not pack any more names in here */ 471 blankfill(SIZCOL); 472 } 473 } 474 } else { 475 if (!first) 476 printf(", "); 477 printf("%s", nfile); 478 col += nlen + fill; 479 } 480 first = 0; 481 482 seteuid(euid); 483 if (*datafile && !stat(datafile, &lbuf)) 484 totsize += copies * lbuf.st_size; 485 seteuid(uid); 486 } 487 488 /* 489 * Print the long info about the file 490 */ 491 void 492 ldump(const char *nfile, const char *datafile, int copies) 493 { 494 struct stat lbuf; 495 496 putchar('\t'); 497 if (copies > 1) 498 printf("%-2d copies of %-19s", copies, nfile); 499 else 500 printf("%-32s", nfile); 501 if (*datafile && !stat(datafile, &lbuf)) 502 printf(" %qd bytes", (long long) lbuf.st_size); 503 else 504 printf(" ??? bytes"); 505 putchar('\n'); 506 } 507 508 /* 509 * Print the job's rank in the queue, 510 * update col for screen management 511 */ 512 void 513 prank(int n) 514 { 515 char rline[100]; 516 static const char *r[] = { 517 "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" 518 }; 519 520 if (n == 0) { 521 printf("active"); 522 col += 6; 523 return; 524 } 525 if ((n/10)%10 == 1) 526 (void)snprintf(rline, sizeof(rline), "%dth", n); 527 else 528 (void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]); 529 col += strlen(rline); 530 printf("%s", rline); 531 } 532 533 void 534 alarmhandler(int signo __unused) 535 { 536 /* the signal is ignored */ 537 /* (the '__unused' is just to avoid a compile-time warning) */ 538 } 539