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