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