1 1.16 joerg /* $NetBSD: pcnfsd_print.c,v 1.16 2020/04/22 23:46:02 joerg Exp $ */ 2 1.2 gwr 3 1.1 jtc /* RE_SID: @(%)/usr/dosnfs/shades_SCCS/unix/pcnfsd/v2/src/SCCS/s.pcnfsd_print.c 1.7 92/01/24 19:58:58 SMI */ 4 1.1 jtc /* 5 1.1 jtc **===================================================================== 6 1.1 jtc ** Copyright (c) 1986,1987,1988,1989,1990,1991 by Sun Microsystems, Inc. 7 1.1 jtc ** @(#)pcnfsd_print.c 1.7 1/24/92 8 1.1 jtc **===================================================================== 9 1.1 jtc */ 10 1.1 jtc /* 11 1.1 jtc **===================================================================== 12 1.1 jtc ** I N C L U D E F I L E S E C T I O N * 13 1.1 jtc ** * 14 1.1 jtc ** If your port requires different include files, add a suitable * 15 1.1 jtc ** #define in the customization section, and make the inclusion or * 16 1.1 jtc ** exclusion of the files conditional on this. * 17 1.1 jtc **===================================================================== 18 1.1 jtc */ 19 1.5 lukem 20 1.1 jtc #include <sys/file.h> 21 1.5 lukem #include <sys/ioctl.h> 22 1.1 jtc #include <sys/stat.h> 23 1.5 lukem 24 1.5 lukem #include <ctype.h> 25 1.5 lukem #include <errno.h> 26 1.1 jtc #include <netdb.h> 27 1.5 lukem #include <pwd.h> 28 1.5 lukem #include <signal.h> 29 1.5 lukem #include <stdio.h> 30 1.5 lukem #include <stdlib.h> 31 1.1 jtc #include <string.h> 32 1.5 lukem #include <unistd.h> 33 1.1 jtc 34 1.1 jtc #ifndef SYSV 35 1.1 jtc #include <sys/wait.h> 36 1.1 jtc #endif 37 1.1 jtc 38 1.1 jtc #ifdef ISC_2_0 39 1.1 jtc #include <sys/fcntl.h> 40 1.1 jtc #endif 41 1.1 jtc 42 1.1 jtc #ifdef SHADOW_SUPPORT 43 1.1 jtc #include <shadow.h> 44 1.1 jtc #endif 45 1.1 jtc 46 1.3 gwr #include "paths.h" 47 1.3 gwr 48 1.5 lukem #include "common.h" 49 1.5 lukem #include "pcnfsd.h" 50 1.5 lukem #include "extern.h" 51 1.5 lukem 52 1.1 jtc /* 53 1.1 jtc **--------------------------------------------------------------------- 54 1.5 lukem ** Other #define's 55 1.1 jtc **--------------------------------------------------------------------- 56 1.1 jtc */ 57 1.1 jtc #ifndef MAXPATHLEN 58 1.1 jtc #define MAXPATHLEN 1024 59 1.1 jtc #endif 60 1.1 jtc 61 1.1 jtc /* 62 1.6 wiz ** The following definitions give the maximum time allowed for 63 1.1 jtc ** an external command to run (in seconds) 64 1.1 jtc */ 65 1.1 jtc #define MAXTIME_FOR_PRINT 10 66 1.1 jtc #define MAXTIME_FOR_QUEUE 10 67 1.1 jtc #define MAXTIME_FOR_CANCEL 10 68 1.1 jtc #define MAXTIME_FOR_STATUS 10 69 1.1 jtc 70 1.1 jtc #define QMAX 50 71 1.1 jtc 72 1.1 jtc /* 73 1.1 jtc ** The following is derived from ucb/lpd/displayq.c 74 1.1 jtc */ 75 1.1 jtc #define SIZECOL 62 76 1.1 jtc #define FILECOL 24 77 1.1 jtc 78 1.14 sevan char *expand_alias(char *, char *, char *, char *); 79 1.14 sevan pr_list list_virtual_printers(void); 80 1.14 sevan char *map_printer_name(char *); 81 1.14 sevan void substitute(char *, const char *, const char *); 82 1.14 sevan int suspicious(char *); 83 1.14 sevan int valid_pr(char *); 84 1.1 jtc 85 1.1 jtc /* 86 1.1 jtc **--------------------------------------------------------------------- 87 1.1 jtc ** Misc. variable definitions 88 1.1 jtc **--------------------------------------------------------------------- 89 1.1 jtc */ 90 1.1 jtc 91 1.5 lukem struct stat statbuf; 92 1.5 lukem char pathname[MAXPATHLEN]; 93 1.5 lukem char new_pathname[MAXPATHLEN]; 94 1.5 lukem char sp_name[MAXPATHLEN] = SPOOLDIR; 95 1.16 joerg static char tempstr[256]; 96 1.5 lukem char delims[] = " \t\r\n:()"; 97 1.1 jtc 98 1.5 lukem pr_list printers = NULL; 99 1.5 lukem pr_queue queue = NULL; 100 1.1 jtc 101 1.1 jtc /* 102 1.1 jtc **===================================================================== 103 1.1 jtc ** C O D E S E C T I O N * 104 1.1 jtc **===================================================================== 105 1.1 jtc */ 106 1.1 jtc 107 1.1 jtc /* 108 1.1 jtc * This is the latest word on the security check. The following 109 1.1 jtc * routine "suspicious()" returns non-zero if the character string 110 1.1 jtc * passed to it contains any shell metacharacters. 111 1.1 jtc * Callers will typically code 112 1.1 jtc * 113 1.1 jtc * if(suspicious(some_parameter)) reject(); 114 1.1 jtc */ 115 1.1 jtc 116 1.5 lukem int 117 1.14 sevan suspicious(char *s) 118 1.1 jtc { 119 1.4 gwr if (strpbrk(s, ";|&<>`'#!?*()[]^/${}\n\r\"\\:") != NULL) 120 1.1 jtc return 1; 121 1.1 jtc return 0; 122 1.1 jtc } 123 1.1 jtc 124 1.1 jtc 125 1.1 jtc int 126 1.14 sevan valid_pr(char *pr) 127 1.1 jtc { 128 1.5 lukem char *p; 129 1.5 lukem pr_list curr; 130 1.5 lukem if (printers == NULL) 131 1.1 jtc build_pr_list(); 132 1.1 jtc 133 1.5 lukem if (printers == NULL) 134 1.5 lukem return (1); /* can't tell - assume it's good */ 135 1.1 jtc 136 1.1 jtc p = map_printer_name(pr); 137 1.1 jtc if (p == NULL) 138 1.5 lukem return (1); /* must be ok is maps to NULL! */ 139 1.1 jtc curr = printers; 140 1.5 lukem while (curr) { 141 1.5 lukem if (!strcmp(p, curr->pn)) 142 1.5 lukem return (1); 143 1.1 jtc curr = curr->pr_next; 144 1.1 jtc } 145 1.5 lukem 146 1.5 lukem return (0); 147 1.1 jtc } 148 1.4 gwr /* 149 1.4 gwr * get pathname of current directory and return to client 150 1.4 gwr * 151 1.4 gwr * Note: This runs as root on behalf of a client request. 152 1.4 gwr * As described in CERT advisory CA-96.08, be careful about 153 1.4 gwr * doing a chmod on something that could be a symlink... 154 1.4 gwr */ 155 1.4 gwr pirstat 156 1.14 sevan pr_init(char *sys, char *pr, char **sp) 157 1.1 jtc { 158 1.5 lukem int dir_mode = 0777; 159 1.5 lukem int rc; 160 1.5 lukem mode_t oldmask; 161 1.1 jtc 162 1.1 jtc *sp = &pathname[0]; 163 1.1 jtc pathname[0] = '\0'; 164 1.1 jtc 165 1.5 lukem if (suspicious(sys) || suspicious(pr)) 166 1.5 lukem return (PI_RES_FAIL); 167 1.1 jtc 168 1.4 gwr /* 169 1.4 gwr * Make sure the server spool directory exists. 170 1.4 gwr * Never create it here - the sysadmin does that. 171 1.4 gwr */ 172 1.4 gwr if (stat(sp_name, &statbuf) || !S_ISDIR(statbuf.st_mode)) 173 1.4 gwr goto badspool; 174 1.1 jtc 175 1.4 gwr /* 176 1.4 gwr * Create the client spool directory if needed. 177 1.4 gwr * Just do the mkdir call and ignore EEXIST. 178 1.4 gwr * Mode of client directory should be 777. 179 1.4 gwr */ 180 1.7 itojun (void) snprintf(pathname, sizeof(pathname), "%s/%s", sp_name, sys); 181 1.4 gwr oldmask = umask(0); 182 1.1 jtc rc = mkdir(pathname, dir_mode); /* DON'T ignore this return code */ 183 1.4 gwr umask(oldmask); 184 1.4 gwr if ((rc < 0) && (errno != EEXIST)) 185 1.4 gwr goto badspool; 186 1.4 gwr 187 1.4 gwr /* By this point the client spool dir should exist. */ 188 1.4 gwr if (stat(pathname, &statbuf) || !S_ISDIR(statbuf.st_mode)) { 189 1.4 gwr /* No spool directory... */ 190 1.5 lukem badspool: 191 1.7 itojun (void) snprintf(tempstr, sizeof(tempstr), 192 1.4 gwr "rpc.pcnfsd: unable to set up spool directory %s\n", 193 1.5 lukem pathname); 194 1.4 gwr msg_out(tempstr); 195 1.5 lukem pathname[0] = '\0'; /* null to tell client bad vibes */ 196 1.5 lukem return (PI_RES_FAIL); 197 1.4 gwr } 198 1.4 gwr /* OK, we have a spool directory. */ 199 1.5 lukem if (!valid_pr(pr)) { 200 1.5 lukem pathname[0] = '\0'; /* null to tell client bad vibes */ 201 1.5 lukem return (PI_RES_NO_SUCH_PRINTER); 202 1.4 gwr } 203 1.5 lukem return (PI_RES_OK); 204 1.1 jtc } 205 1.5 lukem psrstat 206 1.14 sevan pr_start2(char *sys, char *pr, char *user, char *fname, char *opts, char **id) 207 1.1 jtc { 208 1.5 lukem char snum[20]; 209 1.5 lukem static char req_id[256]; 210 1.5 lukem char cmdbuf[256]; 211 1.5 lukem char resbuf[256]; 212 1.5 lukem FILE *fd; 213 1.5 lukem int i; 214 1.5 lukem char *xcmd; 215 1.5 lukem int failed = 0; 216 1.1 jtc 217 1.1 jtc #ifdef HACK_FOR_ROTATED_TRANSCRIPT 218 1.5 lukem char scratch[512]; 219 1.1 jtc #endif 220 1.1 jtc 221 1.1 jtc 222 1.9 lukem if (suspicious(sys) || 223 1.5 lukem suspicious(pr) || 224 1.5 lukem suspicious(user) || 225 1.5 lukem suspicious(fname)) 226 1.5 lukem return (PS_RES_FAIL); 227 1.5 lukem 228 1.7 itojun (void) snprintf(pathname, sizeof(pathname), "%s/%s/%s", sp_name, 229 1.9 lukem sys, 230 1.5 lukem fname); 231 1.1 jtc 232 1.1 jtc *id = &req_id[0]; 233 1.1 jtc req_id[0] = '\0'; 234 1.1 jtc 235 1.5 lukem if (stat(pathname, &statbuf)) { 236 1.5 lukem /* 237 1.5 lukem **----------------------------------------------------------------- 238 1.5 lukem ** We can't stat the file. Let's try appending '.spl' and 239 1.5 lukem ** see if it's already in progress. 240 1.5 lukem **----------------------------------------------------------------- 241 1.5 lukem */ 242 1.5 lukem 243 1.7 itojun (void) strlcat(pathname, ".spl", sizeof(pathname)); 244 1.5 lukem if (stat(pathname, &statbuf)) { 245 1.5 lukem /* 246 1.5 lukem **---------------------------------------------------------------- 247 1.5 lukem ** It really doesn't exist. 248 1.5 lukem **---------------------------------------------------------------- 249 1.5 lukem */ 250 1.5 lukem 251 1.5 lukem 252 1.5 lukem return (PS_RES_NO_FILE); 253 1.5 lukem } 254 1.5 lukem /* 255 1.5 lukem **------------------------------------------------------------- 256 1.5 lukem ** It is already on the way. 257 1.5 lukem **------------------------------------------------------------- 258 1.5 lukem */ 259 1.5 lukem 260 1.5 lukem 261 1.5 lukem return (PS_RES_ALREADY); 262 1.5 lukem } 263 1.5 lukem if (statbuf.st_size == 0) { 264 1.5 lukem /* 265 1.5 lukem **------------------------------------------------------------- 266 1.5 lukem ** Null file - don't print it, just kill it. 267 1.5 lukem **------------------------------------------------------------- 268 1.5 lukem */ 269 1.5 lukem (void) unlink(pathname); 270 1.5 lukem 271 1.5 lukem return (PS_RES_NULL); 272 1.5 lukem } 273 1.5 lukem /* 274 1.5 lukem **------------------------------------------------------------- 275 1.5 lukem ** The file is real, has some data, and is not already going out. 276 1.5 lukem ** We rename it by appending '.spl' and exec "lpr" to do the 277 1.5 lukem ** actual work. 278 1.5 lukem **------------------------------------------------------------- 279 1.5 lukem */ 280 1.7 itojun (void) strlcpy(new_pathname, pathname, sizeof(new_pathname)); 281 1.7 itojun (void) strlcat(new_pathname, ".spl", sizeof(new_pathname)); 282 1.1 jtc 283 1.1 jtc /* 284 1.1 jtc **------------------------------------------------------------- 285 1.1 jtc ** See if the new filename exists so as not to overwrite it. 286 1.1 jtc **------------------------------------------------------------- 287 1.1 jtc */ 288 1.1 jtc 289 1.1 jtc 290 1.5 lukem if (!stat(new_pathname, &statbuf)) { 291 1.7 itojun (void) strlcpy(new_pathname, pathname, sizeof(new_pathname)); /* rebuild a new name */ 292 1.7 itojun (void) snprintf(snum, sizeof(snum), "%d", rand()); /* get some number */ 293 1.7 itojun (void) strlcat(new_pathname, snum, 4); 294 1.7 itojun (void) strlcat(new_pathname, ".spl", sizeof(new_pathname)); /* new spool file */ 295 1.5 lukem } 296 1.5 lukem if (rename(pathname, new_pathname)) { 297 1.5 lukem /* 298 1.5 lukem **--------------------------------------------------------------- 299 1.5 lukem ** Should never happen. 300 1.5 lukem **--------------------------------------------------------------- 301 1.5 lukem */ 302 1.7 itojun (void) snprintf(tempstr, sizeof(tempstr), 303 1.7 itojun "rpc.pcnfsd: spool file rename (%s->%s) failed.\n", 304 1.5 lukem pathname, new_pathname); 305 1.5 lukem msg_out(tempstr); 306 1.5 lukem return (PS_RES_FAIL); 307 1.5 lukem } 308 1.5 lukem if (*opts == 'd') { 309 1.5 lukem /* 310 1.5 lukem **------------------------------------------------------ 311 1.5 lukem ** This is a Diablo print stream. Apply the ps630 312 1.5 lukem ** filter with the appropriate arguments. 313 1.5 lukem **------------------------------------------------------ 314 1.5 lukem */ 315 1.5 lukem #if 0 /* XXX: Temporary fix for CERT advisory 316 1.5 lukem * CA-96.08 */ 317 1.5 lukem (void) run_ps630(new_pathname, opts); 318 1.4 gwr #else 319 1.7 itojun (void) snprintf(tempstr, sizeof(tempstr), 320 1.5 lukem "rpc.pcnfsd: ps630 filter disabled for %s\n", pathname); 321 1.5 lukem msg_out(tempstr); 322 1.5 lukem return (PS_RES_FAIL); 323 1.4 gwr #endif 324 1.5 lukem } 325 1.5 lukem /* 326 1.5 lukem ** Try to match to an aliased printer 327 1.5 lukem */ 328 1.9 lukem xcmd = expand_alias(pr, new_pathname, user, sys); 329 1.5 lukem if (!xcmd) { 330 1.5 lukem #ifdef SVR4 331 1.1 jtc /* 332 1.15 msaitoh * Use the copy option so we can remove the original 333 1.3 gwr * spooled nfs file from the spool directory. 334 1.3 gwr */ 335 1.7 itojun snprintf(cmdbuf, sizeof(cmdbuf), "/usr/bin/lp -c -d%s %s", 336 1.5 lukem pr, new_pathname); 337 1.5 lukem #else /* SVR4 */ 338 1.5 lukem /* BSD way: lpr */ 339 1.7 itojun snprintf(cmdbuf, sizeof(cmdbuf), "%s/lpr -P%s %s", 340 1.5 lukem LPRDIR, pr, new_pathname); 341 1.5 lukem #endif /* SVR4 */ 342 1.5 lukem xcmd = cmdbuf; 343 1.5 lukem } 344 1.5 lukem if ((fd = su_popen(user, xcmd, MAXTIME_FOR_PRINT)) == NULL) { 345 1.5 lukem msg_out("rpc.pcnfsd: su_popen failed"); 346 1.5 lukem return (PS_RES_FAIL); 347 1.5 lukem } 348 1.12 mbalmer req_id[0] = '\0'; /* assume failure */ 349 1.5 lukem while (fgets(resbuf, 255, fd) != NULL) { 350 1.5 lukem i = strlen(resbuf); 351 1.5 lukem if (i) 352 1.5 lukem resbuf[i - 1] = '\0'; /* trim NL */ 353 1.5 lukem if (!strncmp(resbuf, "request id is ", 14)) 354 1.5 lukem /* New - just the first word is needed */ 355 1.7 itojun strlcpy(req_id, strtok(&resbuf[14], delims), 356 1.7 itojun sizeof(req_id)); 357 1.5 lukem else 358 1.5 lukem if (strembedded("disabled", resbuf)) 359 1.1 jtc failed = 1; 360 1.5 lukem } 361 1.5 lukem if (su_pclose(fd) == 255) 362 1.5 lukem msg_out("rpc.pcnfsd: su_pclose alert"); 363 1.5 lukem (void) unlink(new_pathname); 364 1.5 lukem return ((failed | interrupted) ? PS_RES_FAIL : PS_RES_OK); 365 1.1 jtc } 366 1.1 jtc /* 367 1.3 gwr * build_pr_list: determine which printers are valid. 368 1.3 gwr * on SVR4 use "lpstat -v" 369 1.1 jtc * on BSD use "lpc status" 370 1.3 gwr */ 371 1.3 gwr 372 1.3 gwr #ifdef SVR4 373 1.3 gwr /* 374 1.3 gwr * In SVR4 the command to determine which printers are 375 1.3 gwr * valid is lpstat -v. The output is something like this: 376 1.3 gwr * 377 1.3 gwr * device for lp: /dev/lp0 378 1.3 gwr * system for pcdslw: hinode 379 1.3 gwr * system for bletch: hinode (as printer hisname) 380 1.3 gwr * 381 1.3 gwr * On SunOS using the SysV compatibility package, the output 382 1.3 gwr * is more like: 383 1.3 gwr * 384 1.3 gwr * device for lp is /dev/lp0 385 1.3 gwr * device for pcdslw is the remote printer pcdslw on hinode 386 1.3 gwr * device for bletch is the remote printer hisname on hinode 387 1.3 gwr * 388 1.3 gwr * It is fairly simple to create logic that will handle either 389 1.3 gwr * possibility: 390 1.3 gwr */ 391 1.3 gwr int 392 1.3 gwr build_pr_list() 393 1.3 gwr { 394 1.3 gwr pr_list last = NULL; 395 1.3 gwr pr_list curr = NULL; 396 1.5 lukem char buff[256]; 397 1.5 lukem FILE *p; 398 1.5 lukem char *cp; 399 1.5 lukem int saw_system; 400 1.3 gwr 401 1.3 gwr p = popen("lpstat -v", "r"); 402 1.5 lukem if (p == NULL) { 403 1.3 gwr msg_out("rpc.pcnfsd: unable to popen() lp status"); 404 1.5 lukem return (0); 405 1.3 gwr } 406 1.5 lukem while (fgets(buff, 255, p) != NULL) { 407 1.3 gwr cp = strtok(buff, delims); 408 1.5 lukem if (!cp) 409 1.3 gwr continue; 410 1.5 lukem if (!strcmp(cp, "device")) 411 1.3 gwr saw_system = 0; 412 1.3 gwr else 413 1.5 lukem if (!strcmp(cp, "system")) 414 1.5 lukem saw_system = 1; 415 1.5 lukem else 416 1.5 lukem continue; 417 1.3 gwr cp = strtok(NULL, delims); 418 1.5 lukem if (!cp || strcmp(cp, "for")) 419 1.3 gwr continue; 420 1.3 gwr cp = strtok(NULL, delims); 421 1.5 lukem if (!cp) 422 1.3 gwr continue; 423 1.3 gwr curr = (struct pr_list_item *) 424 1.5 lukem grab(sizeof(struct pr_list_item)); 425 1.3 gwr 426 1.3 gwr curr->pn = strdup(cp); 427 1.3 gwr curr->device = NULL; 428 1.3 gwr curr->remhost = NULL; 429 1.3 gwr curr->cm = strdup("-"); 430 1.3 gwr curr->pr_next = NULL; 431 1.3 gwr 432 1.3 gwr cp = strtok(NULL, delims); 433 1.3 gwr 434 1.5 lukem if (cp && !strcmp(cp, "is")) 435 1.3 gwr cp = strtok(NULL, delims); 436 1.3 gwr 437 1.5 lukem if (!cp) { 438 1.3 gwr free_pr_list_item(curr); 439 1.3 gwr continue; 440 1.3 gwr } 441 1.5 lukem if (saw_system) { 442 1.5 lukem /* "system" OR "system (as printer pname)" */ 443 1.3 gwr curr->remhost = strdup(cp); 444 1.3 gwr cp = strtok(NULL, delims); 445 1.5 lukem if (!cp) { 446 1.3 gwr /* simple format */ 447 1.3 gwr curr->device = strdup(curr->pn); 448 1.3 gwr } else { 449 1.3 gwr /* "sys (as printer pname)" */ 450 1.3 gwr if (strcmp(cp, "as")) { 451 1.3 gwr free_pr_list_item(curr); 452 1.3 gwr continue; 453 1.3 gwr } 454 1.3 gwr cp = strtok(NULL, delims); 455 1.3 gwr if (!cp || strcmp(cp, "printer")) { 456 1.3 gwr free_pr_list_item(curr); 457 1.3 gwr continue; 458 1.3 gwr } 459 1.3 gwr cp = strtok(NULL, delims); 460 1.5 lukem if (!cp) { 461 1.3 gwr free_pr_list_item(curr); 462 1.3 gwr continue; 463 1.3 gwr } 464 1.3 gwr curr->device = strdup(cp); 465 1.3 gwr } 466 1.5 lukem } else 467 1.5 lukem if (!strcmp(cp, "the")) { 468 1.5 lukem /* start of "the remote printer foo on bar" */ 469 1.5 lukem cp = strtok(NULL, delims); 470 1.5 lukem if (!cp || strcmp(cp, "remote")) { 471 1.5 lukem free_pr_list_item(curr); 472 1.5 lukem continue; 473 1.5 lukem } 474 1.5 lukem cp = strtok(NULL, delims); 475 1.5 lukem if (!cp || strcmp(cp, "printer")) { 476 1.5 lukem free_pr_list_item(curr); 477 1.5 lukem continue; 478 1.5 lukem } 479 1.5 lukem cp = strtok(NULL, delims); 480 1.5 lukem if (!cp) { 481 1.5 lukem free_pr_list_item(curr); 482 1.5 lukem continue; 483 1.5 lukem } 484 1.5 lukem curr->device = strdup(cp); 485 1.5 lukem cp = strtok(NULL, delims); 486 1.5 lukem if (!cp || strcmp(cp, "on")) { 487 1.5 lukem free_pr_list_item(curr); 488 1.5 lukem continue; 489 1.5 lukem } 490 1.5 lukem cp = strtok(NULL, delims); 491 1.5 lukem if (!cp) { 492 1.5 lukem free_pr_list_item(curr); 493 1.5 lukem continue; 494 1.5 lukem } 495 1.5 lukem curr->remhost = strdup(cp); 496 1.5 lukem } else { 497 1.5 lukem /* the local name */ 498 1.5 lukem curr->device = strdup(cp); 499 1.5 lukem curr->remhost = strdup(""); 500 1.3 gwr } 501 1.3 gwr 502 1.5 lukem if (last == NULL) 503 1.3 gwr printers = curr; 504 1.3 gwr else 505 1.3 gwr last->pr_next = curr; 506 1.3 gwr last = curr; 507 1.3 gwr 508 1.3 gwr } 509 1.3 gwr (void) pclose(p); 510 1.3 gwr 511 1.3 gwr /* 512 1.3 gwr ** Now add on the virtual printers, if any 513 1.3 gwr */ 514 1.5 lukem if (last == NULL) 515 1.3 gwr printers = list_virtual_printers(); 516 1.3 gwr else 517 1.3 gwr last->pr_next = list_virtual_printers(); 518 1.3 gwr 519 1.5 lukem return (1); 520 1.3 gwr } 521 1.5 lukem #else /* SVR4 */ 522 1.3 gwr 523 1.3 gwr /* 524 1.3 gwr * BSD way: lpc stat 525 1.1 jtc */ 526 1.1 jtc int 527 1.1 jtc build_pr_list() 528 1.1 jtc { 529 1.1 jtc pr_list last = NULL; 530 1.1 jtc pr_list curr = NULL; 531 1.5 lukem char buff[256]; 532 1.5 lukem FILE *p; 533 1.5 lukem char *cp; 534 1.1 jtc 535 1.7 itojun snprintf(buff, sizeof(buff), "%s/lpc status", LPCDIR); 536 1.1 jtc p = popen(buff, "r"); 537 1.5 lukem if (p == NULL) { 538 1.1 jtc msg_out("rpc.pcnfsd: unable to popen lpc stat"); 539 1.5 lukem return (0); 540 1.1 jtc } 541 1.5 lukem while (fgets(buff, 255, p) != NULL) { 542 1.8 dsl if (isspace((unsigned char)buff[0])) 543 1.1 jtc continue; 544 1.1 jtc 545 1.1 jtc if ((cp = strtok(buff, delims)) == NULL) 546 1.1 jtc continue; 547 1.1 jtc 548 1.1 jtc curr = (struct pr_list_item *) 549 1.5 lukem grab(sizeof(struct pr_list_item)); 550 1.1 jtc 551 1.1 jtc /* XXX - Should distinguish remote printers. */ 552 1.1 jtc curr->pn = strdup(cp); 553 1.1 jtc curr->device = strdup(cp); 554 1.1 jtc curr->remhost = strdup(""); 555 1.1 jtc curr->cm = strdup("-"); 556 1.1 jtc curr->pr_next = NULL; 557 1.1 jtc 558 1.5 lukem if (last == NULL) 559 1.1 jtc printers = curr; 560 1.1 jtc else 561 1.1 jtc last->pr_next = curr; 562 1.1 jtc last = curr; 563 1.1 jtc 564 1.1 jtc } 565 1.11 wiz (void) pclose(p); 566 1.1 jtc 567 1.1 jtc /* 568 1.1 jtc ** Now add on the virtual printers, if any 569 1.1 jtc */ 570 1.5 lukem if (last == NULL) 571 1.1 jtc printers = list_virtual_printers(); 572 1.1 jtc else 573 1.1 jtc last->pr_next = list_virtual_printers(); 574 1.1 jtc 575 1.5 lukem return (1); 576 1.1 jtc } 577 1.5 lukem #endif /* SVR4 */ 578 1.1 jtc 579 1.5 lukem void * 580 1.14 sevan grab(int n) 581 1.1 jtc { 582 1.5 lukem void *p; 583 1.1 jtc 584 1.5 lukem p = (void *) malloc(n); 585 1.5 lukem if (p == NULL) { 586 1.1 jtc msg_out("rpc.pcnfsd: malloc failure"); 587 1.1 jtc exit(1); 588 1.1 jtc } 589 1.5 lukem return (p); 590 1.1 jtc } 591 1.1 jtc 592 1.1 jtc void 593 1.14 sevan free_pr_list_item(pr_list curr) 594 1.1 jtc { 595 1.5 lukem if (curr->pn) 596 1.1 jtc free(curr->pn); 597 1.5 lukem if (curr->device) 598 1.1 jtc free(curr->device); 599 1.5 lukem if (curr->remhost) 600 1.1 jtc free(curr->remhost); 601 1.5 lukem if (curr->cm) 602 1.1 jtc free(curr->cm); 603 1.5 lukem if (curr->pr_next) 604 1.5 lukem free_pr_list_item(curr->pr_next); /* recurse */ 605 1.1 jtc free(curr); 606 1.1 jtc } 607 1.1 jtc /* 608 1.3 gwr * build_pr_queue: used to show the print queue. 609 1.3 gwr * 610 1.3 gwr * Note that the first thing we do is to discard any 611 1.3 gwr * existing queue. 612 1.3 gwr */ 613 1.3 gwr #ifdef SVR4 614 1.3 gwr 615 1.3 gwr /* 616 1.3 gwr ** In SVR4 the command to list the print jobs for printer 617 1.3 gwr ** lp is "lpstat lp" (or, equivalently, "lpstat -p lp"). 618 1.3 gwr ** The output looks like this: 619 1.5 lukem ** 620 1.3 gwr ** lp-2 root 939 Jul 10 21:56 621 1.3 gwr ** lp-5 geoff 15 Jul 12 23:23 622 1.3 gwr ** lp-6 geoff 15 Jul 12 23:23 623 1.5 lukem ** 624 1.3 gwr ** If the first job is actually printing the first line 625 1.3 gwr ** is modified, as follows: 626 1.1 jtc ** 627 1.3 gwr ** lp-2 root 939 Jul 10 21:56 on lp 628 1.5 lukem ** 629 1.3 gwr ** I don't yet have any info on what it looks like if the printer 630 1.3 gwr ** is remote and we're spooling over the net. However for 631 1.3 gwr ** the purposes of rpc.pcnfsd we can simply say that field 1 is the 632 1.3 gwr ** job ID, field 2 is the submitter, and field 3 is the size. 633 1.3 gwr ** We can check for the presence of the string " on " in the 634 1.3 gwr ** first record to determine if we should count it as rank 0 or rank 1, 635 1.3 gwr ** but it won't hurt if we get it wrong. 636 1.3 gwr **/ 637 1.3 gwr 638 1.3 gwr pirstat 639 1.14 sevan build_pr_queue(printername pn, username user, int just_mine, int p_qlen, int p_qshown) 640 1.3 gwr { 641 1.5 lukem pr_queue last = NULL; 642 1.5 lukem pr_queue curr = NULL; 643 1.5 lukem char buff[256]; 644 1.5 lukem FILE *p; 645 1.5 lukem char *owner; 646 1.5 lukem char *job; 647 1.5 lukem char *totsize; 648 1.3 gwr 649 1.5 lukem if (queue) { 650 1.3 gwr free_pr_queue_item(queue); 651 1.3 gwr queue = NULL; 652 1.3 gwr } 653 1.3 gwr *p_qlen = 0; 654 1.3 gwr *p_qshown = 0; 655 1.3 gwr 656 1.3 gwr pn = map_printer_name(pn); 657 1.5 lukem if (pn == NULL || !valid_pr(pn) || suspicious(pn)) 658 1.5 lukem return (PI_RES_NO_SUCH_PRINTER); 659 1.3 gwr 660 1.7 itojun snprintf(buff, sizeof(buff), "/usr/bin/lpstat %s", pn); 661 1.3 gwr p = su_popen(user, buff, MAXTIME_FOR_QUEUE); 662 1.5 lukem if (p == NULL) { 663 1.3 gwr msg_out("rpc.pcnfsd: unable to popen() lpstat queue query"); 664 1.5 lukem return (PI_RES_FAIL); 665 1.3 gwr } 666 1.5 lukem while (fgets(buff, 255, p) != NULL) { 667 1.3 gwr job = strtok(buff, delims); 668 1.5 lukem if (!job) 669 1.3 gwr continue; 670 1.3 gwr 671 1.3 gwr owner = strtok(NULL, delims); 672 1.5 lukem if (!owner) 673 1.3 gwr continue; 674 1.3 gwr 675 1.3 gwr totsize = strtok(NULL, delims); 676 1.5 lukem if (!totsize) 677 1.3 gwr continue; 678 1.3 gwr 679 1.3 gwr *p_qlen += 1; 680 1.3 gwr 681 1.5 lukem if (*p_qshown > QMAX) 682 1.3 gwr continue; 683 1.3 gwr 684 1.5 lukem if (just_mine && strcasecmp(owner, user)) 685 1.3 gwr continue; 686 1.3 gwr 687 1.3 gwr *p_qshown += 1; 688 1.3 gwr 689 1.3 gwr curr = (struct pr_queue_item *) 690 1.5 lukem grab(sizeof(struct pr_queue_item)); 691 1.3 gwr 692 1.3 gwr curr->position = *p_qlen; 693 1.3 gwr curr->id = strdup(job); 694 1.3 gwr curr->size = strdup(totsize); 695 1.3 gwr curr->status = strdup(""); 696 1.3 gwr curr->system = strdup(""); 697 1.3 gwr curr->user = strdup(owner); 698 1.3 gwr curr->file = strdup(""); 699 1.3 gwr curr->cm = strdup("-"); 700 1.3 gwr curr->pr_next = NULL; 701 1.3 gwr 702 1.5 lukem if (last == NULL) 703 1.3 gwr queue = curr; 704 1.3 gwr else 705 1.3 gwr last->pr_next = curr; 706 1.3 gwr last = curr; 707 1.3 gwr 708 1.3 gwr } 709 1.3 gwr (void) su_pclose(p); 710 1.5 lukem return (PI_RES_OK); 711 1.3 gwr } 712 1.5 lukem #else /* SVR4 */ 713 1.1 jtc 714 1.1 jtc pirstat 715 1.14 sevan build_pr_queue(printername pn, username user, int just_mine, int *p_qlen, int *p_qshown) 716 1.1 jtc { 717 1.5 lukem pr_queue last = NULL; 718 1.5 lukem pr_queue curr = NULL; 719 1.5 lukem char buff[256]; 720 1.5 lukem FILE *p; 721 1.5 lukem char *cp; 722 1.5 lukem int i; 723 1.5 lukem char *rank; 724 1.5 lukem char *owner; 725 1.5 lukem char *job; 726 1.5 lukem char *files; 727 1.5 lukem char *totsize; 728 1.1 jtc 729 1.5 lukem if (queue) { 730 1.1 jtc free_pr_queue_item(queue); 731 1.1 jtc queue = NULL; 732 1.1 jtc } 733 1.1 jtc *p_qlen = 0; 734 1.1 jtc *p_qshown = 0; 735 1.1 jtc pn = map_printer_name(pn); 736 1.5 lukem if (pn == NULL || suspicious(pn)) 737 1.5 lukem return (PI_RES_NO_SUCH_PRINTER); 738 1.1 jtc 739 1.7 itojun snprintf(buff, sizeof(buff), "%s/lpq -P%s", LPRDIR, pn); 740 1.1 jtc 741 1.1 jtc p = su_popen(user, buff, MAXTIME_FOR_QUEUE); 742 1.5 lukem if (p == NULL) { 743 1.1 jtc msg_out("rpc.pcnfsd: unable to popen() lpq"); 744 1.5 lukem return (PI_RES_FAIL); 745 1.1 jtc } 746 1.5 lukem while (fgets(buff, 255, p) != NULL) { 747 1.1 jtc i = strlen(buff) - 1; 748 1.5 lukem buff[i] = '\0'; /* zap trailing NL */ 749 1.5 lukem if (i < SIZECOL) 750 1.1 jtc continue; 751 1.5 lukem if (!strncasecmp(buff, "rank", 4)) 752 1.1 jtc continue; 753 1.1 jtc 754 1.5 lukem totsize = &buff[SIZECOL - 1]; 755 1.5 lukem files = &buff[FILECOL - 1]; 756 1.1 jtc cp = totsize; 757 1.1 jtc cp--; 758 1.8 dsl while (cp > files && isspace((unsigned char)*cp)) 759 1.1 jtc *cp-- = '\0'; 760 1.1 jtc 761 1.5 lukem buff[FILECOL - 2] = '\0'; 762 1.1 jtc 763 1.1 jtc cp = strtok(buff, delims); 764 1.5 lukem if (!cp) 765 1.1 jtc continue; 766 1.1 jtc rank = cp; 767 1.1 jtc 768 1.1 jtc cp = strtok(NULL, delims); 769 1.5 lukem if (!cp) 770 1.1 jtc continue; 771 1.1 jtc owner = cp; 772 1.1 jtc 773 1.1 jtc cp = strtok(NULL, delims); 774 1.5 lukem if (!cp) 775 1.1 jtc continue; 776 1.1 jtc job = cp; 777 1.1 jtc 778 1.1 jtc *p_qlen += 1; 779 1.1 jtc 780 1.5 lukem if (*p_qshown > QMAX) 781 1.1 jtc continue; 782 1.1 jtc 783 1.5 lukem if (just_mine && strcasecmp(owner, user)) 784 1.1 jtc continue; 785 1.1 jtc 786 1.1 jtc *p_qshown += 1; 787 1.1 jtc 788 1.1 jtc curr = (struct pr_queue_item *) 789 1.5 lukem grab(sizeof(struct pr_queue_item)); 790 1.1 jtc 791 1.5 lukem curr->position = atoi(rank); /* active -> 0 */ 792 1.1 jtc curr->id = strdup(job); 793 1.1 jtc curr->size = strdup(totsize); 794 1.1 jtc curr->status = strdup(rank); 795 1.1 jtc curr->system = strdup(""); 796 1.1 jtc curr->user = strdup(owner); 797 1.1 jtc curr->file = strdup(files); 798 1.1 jtc curr->cm = strdup("-"); 799 1.1 jtc curr->pr_next = NULL; 800 1.1 jtc 801 1.5 lukem if (last == NULL) 802 1.1 jtc queue = curr; 803 1.1 jtc else 804 1.1 jtc last->pr_next = curr; 805 1.1 jtc last = curr; 806 1.1 jtc 807 1.1 jtc } 808 1.1 jtc (void) su_pclose(p); 809 1.5 lukem return (PI_RES_OK); 810 1.1 jtc } 811 1.5 lukem #endif /* SVR4 */ 812 1.3 gwr 813 1.1 jtc void 814 1.14 sevan free_pr_queue_item(pr_queue curr) 815 1.1 jtc { 816 1.5 lukem if (curr->id) 817 1.1 jtc free(curr->id); 818 1.5 lukem if (curr->size) 819 1.1 jtc free(curr->size); 820 1.5 lukem if (curr->status) 821 1.1 jtc free(curr->status); 822 1.5 lukem if (curr->system) 823 1.1 jtc free(curr->system); 824 1.5 lukem if (curr->user) 825 1.1 jtc free(curr->user); 826 1.5 lukem if (curr->file) 827 1.1 jtc free(curr->file); 828 1.5 lukem if (curr->cm) 829 1.1 jtc free(curr->cm); 830 1.5 lukem if (curr->pr_next) 831 1.5 lukem free_pr_queue_item(curr->pr_next); /* recurse */ 832 1.1 jtc free(curr); 833 1.1 jtc } 834 1.3 gwr #ifdef SVR4 835 1.1 jtc 836 1.3 gwr /* 837 1.3 gwr ** New - SVR4 printer status handling. 838 1.3 gwr ** 839 1.3 gwr ** The command we'll use for checking the status of printer "lp" 840 1.3 gwr ** is "lpstat -a lp -p lp". Here are some sample outputs: 841 1.3 gwr ** 842 1.5 lukem ** 843 1.3 gwr ** lp accepting requests since Wed Jul 10 21:49:25 EDT 1991 844 1.3 gwr ** printer lp disabled since Thu Feb 21 22:52:36 EST 1991. available. 845 1.3 gwr ** new printer 846 1.3 gwr ** --- 847 1.3 gwr ** pcdslw not accepting requests since Fri Jul 12 22:30:00 EDT 1991 - 848 1.3 gwr ** unknown reason 849 1.3 gwr ** printer pcdslw disabled since Fri Jul 12 22:15:37 EDT 1991. available. 850 1.3 gwr ** new printer 851 1.3 gwr ** --- 852 1.3 gwr ** lp accepting requests since Wed Jul 10 21:49:25 EDT 1991 853 1.3 gwr ** printer lp now printing lp-2. enabled since Sat Jul 13 12:02:17 EDT 1991. available. 854 1.3 gwr ** --- 855 1.3 gwr ** lp accepting requests since Wed Jul 10 21:49:25 EDT 1991 856 1.3 gwr ** printer lp now printing lp-2. enabled since Sat Jul 13 12:02:17 EDT 1991. available. 857 1.3 gwr ** --- 858 1.3 gwr ** lp accepting requests since Wed Jul 10 21:49:25 EDT 1991 859 1.3 gwr ** printer lp disabled since Sat Jul 13 12:05:20 EDT 1991. available. 860 1.3 gwr ** unknown reason 861 1.3 gwr ** --- 862 1.3 gwr ** pcdslw not accepting requests since Fri Jul 12 22:30:00 EDT 1991 - 863 1.3 gwr ** unknown reason 864 1.3 gwr ** printer pcdslw is idle. enabled since Sat Jul 13 12:05:28 EDT 1991. available. 865 1.3 gwr ** 866 1.3 gwr ** Note that these are actual outputs. The format (which is totally 867 1.3 gwr ** different from the lpstat in SunOS) seems to break down as 868 1.3 gwr ** follows: 869 1.3 gwr ** (1) The first line has the form "printername [not] accepting requests,,," 870 1.3 gwr ** This is trivial to decode. 871 1.3 gwr ** (2) The second line has several forms, all beginning "printer printername": 872 1.3 gwr ** (2.1) "... disabled" 873 1.3 gwr ** (2.2) "... is idle" 874 1.3 gwr ** (2.3) "... now printing jobid" 875 1.3 gwr ** The "available" comment seems to be meaningless. The next line 876 1.3 gwr ** is the "reason" code which the operator can supply when issuing 877 1.3 gwr ** a "disable" or "reject" command. 878 1.3 gwr ** Note that there is no way to check the number of entries in the 879 1.3 gwr ** queue except to ask for the queue and count them. 880 1.3 gwr */ 881 1.1 jtc 882 1.1 jtc pirstat 883 1.14 sevan get_pr_status(printername pn, bool_t *avail, bool_t *printing, int *qlen, bool_t *needs_operator, char *status, size_t statuslen) 884 1.1 jtc { 885 1.5 lukem char buff[256]; 886 1.5 lukem char cmd[64]; 887 1.5 lukem FILE *p; 888 1.5 lukem int n; 889 1.5 lukem pirstat stat = PI_RES_NO_SUCH_PRINTER; 890 1.1 jtc 891 1.1 jtc /* assume the worst */ 892 1.1 jtc *avail = FALSE; 893 1.1 jtc *printing = FALSE; 894 1.1 jtc *needs_operator = FALSE; 895 1.1 jtc *qlen = 0; 896 1.1 jtc *status = '\0'; 897 1.1 jtc 898 1.1 jtc pn = map_printer_name(pn); 899 1.5 lukem if (pn == NULL || !valid_pr(pn) || suspicious(pn)) 900 1.5 lukem return (PI_RES_NO_SUCH_PRINTER); 901 1.3 gwr n = strlen(pn); 902 1.3 gwr 903 1.7 itojun snprintf(cmd, sizeof(cmd), "/usr/bin/lpstat -a %s -p %s", pn, pn); 904 1.3 gwr 905 1.3 gwr p = popen(cmd, "r"); 906 1.5 lukem if (p == NULL) { 907 1.3 gwr msg_out("rpc.pcnfsd: unable to popen() lp status"); 908 1.5 lukem return (PI_RES_FAIL); 909 1.3 gwr } 910 1.3 gwr stat = PI_RES_OK; 911 1.3 gwr 912 1.5 lukem while (fgets(buff, 255, p) != NULL) { 913 1.5 lukem if (!strncmp(buff, pn, n)) { 914 1.5 lukem if (!strstr(buff, "not accepting")) 915 1.5 lukem *avail = TRUE; 916 1.3 gwr continue; 917 1.3 gwr } 918 1.5 lukem if (!strncmp(buff, "printer ", 8)) { 919 1.5 lukem if (!strstr(buff, "disabled")) 920 1.3 gwr *printing = TRUE; 921 1.5 lukem if (strstr(buff, "printing")) 922 1.13 apb strlcpy(status, "printing", statuslen); 923 1.5 lukem else 924 1.5 lukem if (strstr(buff, "idle")) 925 1.13 apb strlcpy(status, "idle", statuslen); 926 1.3 gwr continue; 927 1.3 gwr } 928 1.5 lukem if (!strncmp(buff, "UX:", 3)) { 929 1.3 gwr stat = PI_RES_NO_SUCH_PRINTER; 930 1.3 gwr } 931 1.3 gwr } 932 1.3 gwr (void) pclose(p); 933 1.5 lukem return (stat); 934 1.3 gwr } 935 1.5 lukem #else /* SVR4 */ 936 1.3 gwr 937 1.3 gwr /* 938 1.3 gwr * BSD way: lpc status 939 1.3 gwr */ 940 1.3 gwr pirstat 941 1.14 sevan get_pr_status(printername pn, bool_t *avail, bool_t *printing, int *qlen, bool_t *needs_operator, char *status, size_t statuslen) 942 1.3 gwr { 943 1.5 lukem char cmd[128]; 944 1.5 lukem char buff[256]; 945 1.5 lukem char buff2[256]; 946 1.5 lukem char pname[64]; 947 1.5 lukem FILE *p; 948 1.5 lukem char *cp; 949 1.5 lukem char *cp1; 950 1.5 lukem char *cp2; 951 1.5 lukem int n; 952 1.9 lukem pirstat pstat = PI_RES_NO_SUCH_PRINTER; 953 1.3 gwr 954 1.3 gwr /* assume the worst */ 955 1.3 gwr *avail = FALSE; 956 1.3 gwr *printing = FALSE; 957 1.3 gwr *needs_operator = FALSE; 958 1.3 gwr *qlen = 0; 959 1.3 gwr *status = '\0'; 960 1.3 gwr 961 1.3 gwr pn = map_printer_name(pn); 962 1.5 lukem if (pn == NULL || suspicious(pn)) 963 1.5 lukem return (PI_RES_NO_SUCH_PRINTER); 964 1.1 jtc 965 1.7 itojun snprintf(pname, sizeof(pname), "%s:", pn); 966 1.1 jtc n = strlen(pname); 967 1.1 jtc 968 1.7 itojun snprintf(cmd, sizeof(cmd), "%s/lpc status %s", LPCDIR, pn); 969 1.1 jtc p = popen(cmd, "r"); 970 1.5 lukem if (p == NULL) { 971 1.1 jtc msg_out("rpc.pcnfsd: unable to popen() lp status"); 972 1.5 lukem return (PI_RES_FAIL); 973 1.1 jtc } 974 1.5 lukem while (fgets(buff, 255, p) != NULL) { 975 1.5 lukem if (strncmp(buff, pname, n)) 976 1.1 jtc continue; 977 1.1 jtc /* 978 1.1 jtc ** We have a match. The only failure now is PI_RES_FAIL if 979 1.1 jtc ** lpstat output cannot be decoded 980 1.1 jtc */ 981 1.9 lukem pstat = PI_RES_FAIL; 982 1.1 jtc /* 983 1.1 jtc ** The next four lines are usually if the form 984 1.1 jtc ** 985 1.1 jtc ** queuing is [enabled|disabled] 986 1.1 jtc ** printing is [enabled|disabled] 987 1.1 jtc ** [no entries | N entr[y|ies] in spool area] 988 1.1 jtc ** <status message, may include the word "attention"> 989 1.1 jtc */ 990 1.8 dsl while (fgets(buff, 255, p) != NULL && isspace((unsigned char)buff[0])) { 991 1.1 jtc cp = buff; 992 1.8 dsl while (isspace((unsigned char)*cp)) 993 1.1 jtc cp++; 994 1.5 lukem if (*cp == '\0') 995 1.1 jtc break; 996 1.1 jtc cp1 = cp; 997 1.1 jtc cp2 = buff2; 998 1.5 lukem while (*cp1 && *cp1 != '\n') { 999 1.8 dsl *cp2++ = tolower((unsigned char)*cp1); 1000 1.1 jtc cp1++; 1001 1.1 jtc } 1002 1.1 jtc *cp1 = '\0'; 1003 1.1 jtc *cp2 = '\0'; 1004 1.1 jtc /* 1005 1.1 jtc ** Now buff2 has a lower-cased copy and cp points at the original; 1006 1.1 jtc ** both are null terminated without any newline 1007 1.5 lukem */ 1008 1.5 lukem if (!strncmp(buff2, "queuing", 7)) { 1009 1.1 jtc *avail = (strstr(buff2, "enabled") != NULL); 1010 1.1 jtc continue; 1011 1.1 jtc } 1012 1.5 lukem if (!strncmp(buff2, "printing", 8)) { 1013 1.1 jtc *printing = (strstr(buff2, "enabled") != NULL); 1014 1.1 jtc continue; 1015 1.1 jtc } 1016 1.8 dsl if (isdigit((unsigned char)buff2[0]) && (strstr(buff2, "entr") != NULL)) { 1017 1.1 jtc 1018 1.1 jtc *qlen = atoi(buff2); 1019 1.1 jtc continue; 1020 1.1 jtc } 1021 1.5 lukem if (strstr(buff2, "attention") != NULL || 1022 1.5 lukem strstr(buff2, "error") != NULL) 1023 1.1 jtc *needs_operator = TRUE; 1024 1.5 lukem if (*needs_operator || strstr(buff2, "waiting") != NULL) 1025 1.13 apb strlcpy(status, cp, statuslen); 1026 1.1 jtc } 1027 1.9 lukem pstat = PI_RES_OK; 1028 1.1 jtc break; 1029 1.1 jtc } 1030 1.1 jtc (void) pclose(p); 1031 1.9 lukem return (pstat); 1032 1.1 jtc } 1033 1.5 lukem #endif /* SVR4 */ 1034 1.3 gwr 1035 1.3 gwr /* 1036 1.3 gwr * pr_cancel: cancel a print job 1037 1.3 gwr */ 1038 1.3 gwr #ifdef SVR4 1039 1.1 jtc 1040 1.3 gwr /* 1041 1.3 gwr ** For SVR4 we have to be prepared for the following kinds of output: 1042 1.5 lukem ** 1043 1.3 gwr ** # cancel lp-6 1044 1.3 gwr ** request "lp-6" cancelled 1045 1.3 gwr ** # cancel lp-33 1046 1.3 gwr ** UX:cancel: WARNING: Request "lp-33" doesn't exist. 1047 1.3 gwr ** # cancel foo-88 1048 1.3 gwr ** UX:cancel: WARNING: Request "foo-88" doesn't exist. 1049 1.3 gwr ** # cancel foo 1050 1.3 gwr ** UX:cancel: WARNING: "foo" is not a request id or a printer. 1051 1.3 gwr ** TO FIX: Cancel requests by id or by 1052 1.3 gwr ** name of printer where printing. 1053 1.3 gwr ** # su geoff 1054 1.3 gwr ** $ cancel lp-2 1055 1.3 gwr ** UX:cancel: WARNING: Can't cancel request "lp-2". 1056 1.3 gwr ** TO FIX: You are not allowed to cancel 1057 1.3 gwr ** another's request. 1058 1.3 gwr ** 1059 1.3 gwr ** There are probably other variations for remote printers. 1060 1.3 gwr ** Basically, if the reply begins with the string 1061 1.3 gwr ** "UX:cancel: WARNING: " 1062 1.3 gwr ** we can strip this off and look for one of the following 1063 1.3 gwr ** (1) 'R' - should be part of "Request "xxxx" doesn't exist." 1064 1.3 gwr ** (2) '"' - should be start of ""foo" is not a request id or..." 1065 1.3 gwr ** (3) 'C' - should be start of "Can't cancel request..." 1066 1.3 gwr ** 1067 1.3 gwr ** The fly in the ointment: all of this can change if these 1068 1.3 gwr ** messages are localized..... :-( 1069 1.3 gwr */ 1070 1.5 lukem pcrstat 1071 1.14 sevan pr_cancel(char *pr, char *user, char *id) 1072 1.1 jtc { 1073 1.5 lukem char cmdbuf[256]; 1074 1.5 lukem char resbuf[256]; 1075 1.5 lukem FILE *fd; 1076 1.5 lukem pcrstat stat = PC_RES_NO_SUCH_JOB; 1077 1.1 jtc 1078 1.1 jtc pr = map_printer_name(pr); 1079 1.5 lukem if (pr == NULL || suspicious(pr)) 1080 1.5 lukem return (PC_RES_NO_SUCH_PRINTER); 1081 1.5 lukem if (suspicious(id)) 1082 1.5 lukem return (PC_RES_NO_SUCH_JOB); 1083 1.1 jtc 1084 1.7 itojun snprintf(cmdbuf, sizeof(cmdbuf), "/usr/bin/cancel %s", id); 1085 1.3 gwr if ((fd = su_popen(user, cmdbuf, MAXTIME_FOR_CANCEL)) == NULL) { 1086 1.3 gwr msg_out("rpc.pcnfsd: su_popen failed"); 1087 1.5 lukem return (PC_RES_FAIL); 1088 1.3 gwr } 1089 1.5 lukem if (fgets(resbuf, 255, fd) == NULL) 1090 1.3 gwr stat = PC_RES_FAIL; 1091 1.5 lukem else 1092 1.5 lukem if (!strstr(resbuf, "UX:")) 1093 1.5 lukem stat = PC_RES_OK; 1094 1.5 lukem else 1095 1.5 lukem if (strstr(resbuf, "doesn't exist")) 1096 1.5 lukem stat = PC_RES_NO_SUCH_JOB; 1097 1.5 lukem else 1098 1.5 lukem if (strstr(resbuf, "not a request id")) 1099 1.5 lukem stat = PC_RES_NO_SUCH_JOB; 1100 1.5 lukem else 1101 1.5 lukem if (strstr(resbuf, "Can't cancel request")) 1102 1.5 lukem stat = PC_RES_NOT_OWNER; 1103 1.5 lukem else 1104 1.5 lukem stat = PC_RES_FAIL; 1105 1.3 gwr 1106 1.5 lukem if (su_pclose(fd) == 255) 1107 1.3 gwr msg_out("rpc.pcnfsd: su_pclose alert"); 1108 1.5 lukem return (stat); 1109 1.3 gwr } 1110 1.5 lukem #else /* SVR4 */ 1111 1.3 gwr 1112 1.3 gwr /* 1113 1.3 gwr * BSD way: lprm 1114 1.3 gwr */ 1115 1.5 lukem pcrstat 1116 1.14 sevan pr_cancel(char *pr, char *user, char *id) 1117 1.3 gwr { 1118 1.5 lukem char cmdbuf[256]; 1119 1.5 lukem char resbuf[256]; 1120 1.5 lukem FILE *fd; 1121 1.5 lukem int i; 1122 1.9 lukem pcrstat pstat = PC_RES_NO_SUCH_JOB; 1123 1.3 gwr 1124 1.3 gwr pr = map_printer_name(pr); 1125 1.5 lukem if (pr == NULL || suspicious(pr)) 1126 1.5 lukem return (PC_RES_NO_SUCH_PRINTER); 1127 1.5 lukem if (suspicious(id)) 1128 1.5 lukem return (PC_RES_NO_SUCH_JOB); 1129 1.5 lukem 1130 1.7 itojun snprintf(cmdbuf, sizeof(cmdbuf), "%s/lprm -P%s %s", LPRDIR, pr, id); 1131 1.5 lukem if ((fd = su_popen(user, cmdbuf, MAXTIME_FOR_CANCEL)) == NULL) { 1132 1.5 lukem msg_out("rpc.pcnfsd: su_popen failed"); 1133 1.5 lukem return (PC_RES_FAIL); 1134 1.5 lukem } 1135 1.5 lukem while (fgets(resbuf, 255, fd) != NULL) { 1136 1.5 lukem i = strlen(resbuf); 1137 1.5 lukem if (i) 1138 1.5 lukem resbuf[i - 1] = '\0'; /* trim NL */ 1139 1.5 lukem if (strstr(resbuf, "dequeued") != NULL) 1140 1.9 lukem pstat = PC_RES_OK; 1141 1.5 lukem if (strstr(resbuf, "unknown printer") != NULL) 1142 1.9 lukem pstat = PC_RES_NO_SUCH_PRINTER; 1143 1.5 lukem if (strstr(resbuf, "Permission denied") != NULL) 1144 1.9 lukem pstat = PC_RES_NOT_OWNER; 1145 1.5 lukem } 1146 1.5 lukem if (su_pclose(fd) == 255) 1147 1.5 lukem msg_out("rpc.pcnfsd: su_pclose alert"); 1148 1.9 lukem return (pstat); 1149 1.1 jtc } 1150 1.5 lukem #endif /* SVR4 */ 1151 1.5 lukem 1152 1.1 jtc /* 1153 1.1 jtc ** New subsystem here. We allow the administrator to define 1154 1.1 jtc ** up to NPRINTERDEFS aliases for printer names. This is done 1155 1.1 jtc ** using the "/etc/pcnfsd.conf" file, which is read at startup. 1156 1.1 jtc ** There are three entry points to this subsystem 1157 1.1 jtc ** 1158 1.1 jtc ** void add_printer_alias(char *printer, char *alias_for, char *command) 1159 1.1 jtc ** 1160 1.1 jtc ** This is invoked from "config_from_file()" for each 1161 1.1 jtc ** "printer" line. "printer" is the name of a printer; note that 1162 1.1 jtc ** it is possible to redefine an existing printer. "alias_for" 1163 1.1 jtc ** is the name of the underlying printer, used for queue listing 1164 1.1 jtc ** and other control functions. If it is "-", there is no 1165 1.1 jtc ** underlying printer, or the administrative functions are 1166 1.1 jtc ** not applicable to this printer. "command" 1167 1.1 jtc ** is the command which should be run (via "su_popen()") if a 1168 1.1 jtc ** job is printed on this printer. The following tokens may be 1169 1.1 jtc ** embedded in the command, and are substituted as follows: 1170 1.1 jtc ** 1171 1.1 jtc ** $FILE - path to the file containing the print data 1172 1.1 jtc ** $USER - login of user 1173 1.1 jtc ** $HOST - hostname from which job originated 1174 1.1 jtc ** 1175 1.1 jtc ** Tokens may occur multiple times. If The command includes no 1176 1.1 jtc ** $FILE token, the string " $FILE" is silently appended. 1177 1.1 jtc ** 1178 1.1 jtc ** pr_list list_virtual_printers() 1179 1.1 jtc ** 1180 1.1 jtc ** This is invoked from build_pr_list to generate a list of aliased 1181 1.1 jtc ** printers, so that the client that asks for a list of valid printers 1182 1.1 jtc ** will see these ones. 1183 1.1 jtc ** 1184 1.1 jtc ** char *map_printer_name(char *printer) 1185 1.1 jtc ** 1186 1.1 jtc ** If "printer" identifies an aliased printer, this function returns 1187 1.1 jtc ** the "alias_for" name, or NULL if the "alias_for" was given as "-". 1188 1.1 jtc ** Otherwise it returns its argument. 1189 1.1 jtc ** 1190 1.1 jtc ** char *expand_alias(char *printer, char *file, char *user, char *host) 1191 1.1 jtc ** 1192 1.1 jtc ** If "printer" is an aliased printer, this function returns a 1193 1.1 jtc ** pointer to a static string in which the corresponding command 1194 1.1 jtc ** has been expanded. Otherwise ot returns NULL. 1195 1.1 jtc */ 1196 1.1 jtc #define NPRINTERDEFS 16 1197 1.5 lukem int num_aliases = 0; 1198 1.1 jtc struct { 1199 1.5 lukem char *a_printer; 1200 1.5 lukem char *a_alias_for; 1201 1.5 lukem char *a_command; 1202 1.5 lukem } alias[NPRINTERDEFS]; 1203 1.1 jtc 1204 1.1 jtc void 1205 1.14 sevan add_printer_alias(char *printer, char *alias_for, char *command) 1206 1.1 jtc { 1207 1.7 itojun size_t l; 1208 1.7 itojun 1209 1.5 lukem if (num_aliases < NPRINTERDEFS) { 1210 1.1 jtc alias[num_aliases].a_printer = strdup(printer); 1211 1.1 jtc alias[num_aliases].a_alias_for = 1212 1.5 lukem (strcmp(alias_for, "-") ? strdup(alias_for) : NULL); 1213 1.5 lukem if (strstr(command, "$FILE")) 1214 1.1 jtc alias[num_aliases].a_command = strdup(command); 1215 1.1 jtc else { 1216 1.7 itojun l = strlen(command) + 8; 1217 1.7 itojun alias[num_aliases].a_command = (char *) grab(l); 1218 1.7 itojun strlcpy(alias[num_aliases].a_command, command, l); 1219 1.7 itojun strlcat(alias[num_aliases].a_command, " $FILE", l); 1220 1.1 jtc } 1221 1.1 jtc num_aliases++; 1222 1.1 jtc } 1223 1.1 jtc } 1224 1.1 jtc 1225 1.5 lukem pr_list 1226 1.5 lukem list_virtual_printers() 1227 1.1 jtc { 1228 1.5 lukem pr_list first = NULL; 1229 1.5 lukem pr_list last = NULL; 1230 1.5 lukem pr_list curr = NULL; 1231 1.5 lukem int i; 1232 1.1 jtc 1233 1.1 jtc 1234 1.5 lukem if (num_aliases == 0) 1235 1.5 lukem return (NULL); 1236 1.1 jtc 1237 1.1 jtc for (i = 0; i < num_aliases; i++) { 1238 1.1 jtc curr = (struct pr_list_item *) 1239 1.5 lukem grab(sizeof(struct pr_list_item)); 1240 1.1 jtc 1241 1.1 jtc curr->pn = strdup(alias[i].a_printer); 1242 1.5 lukem if (alias[i].a_alias_for == NULL) 1243 1.1 jtc curr->device = strdup(""); 1244 1.1 jtc else 1245 1.1 jtc curr->device = strdup(alias[i].a_alias_for); 1246 1.1 jtc curr->remhost = strdup(""); 1247 1.1 jtc curr->cm = strdup("(alias)"); 1248 1.1 jtc curr->pr_next = NULL; 1249 1.5 lukem if (last == NULL) 1250 1.1 jtc first = curr; 1251 1.1 jtc else 1252 1.1 jtc last->pr_next = curr; 1253 1.1 jtc last = curr; 1254 1.1 jtc 1255 1.1 jtc } 1256 1.5 lukem return (first); 1257 1.1 jtc } 1258 1.1 jtc 1259 1.1 jtc 1260 1.5 lukem char * 1261 1.14 sevan map_printer_name(char *printer) 1262 1.1 jtc { 1263 1.5 lukem int i; 1264 1.5 lukem for (i = 0; i < num_aliases; i++) { 1265 1.5 lukem if (!strcmp(printer, alias[i].a_printer)) 1266 1.5 lukem return (alias[i].a_alias_for); 1267 1.1 jtc } 1268 1.5 lukem return (printer); 1269 1.1 jtc } 1270 1.1 jtc 1271 1.5 lukem void 1272 1.14 sevan substitute(char *string, const char *token, const char *data) 1273 1.1 jtc { 1274 1.5 lukem char temp[512]; 1275 1.5 lukem char *c; 1276 1.1 jtc 1277 1.5 lukem while ((c = strstr(string, token)) != NULL) { 1278 1.1 jtc *c = '\0'; 1279 1.7 itojun strlcpy(temp, string, sizeof(temp)); 1280 1.7 itojun strlcat(temp, data, sizeof(temp)); 1281 1.1 jtc c += strlen(token); 1282 1.7 itojun strlcat(temp, c, sizeof(temp)); 1283 1.1 jtc strcpy(string, temp); 1284 1.1 jtc } 1285 1.1 jtc } 1286 1.1 jtc 1287 1.5 lukem char * 1288 1.14 sevan expand_alias(char *printer, char *file, char *user, char *host) 1289 1.1 jtc { 1290 1.5 lukem static char expansion[512]; 1291 1.5 lukem int i; 1292 1.5 lukem for (i = 0; i < num_aliases; i++) { 1293 1.5 lukem if (!strcmp(printer, alias[i].a_printer)) { 1294 1.7 itojun strlcpy(expansion, alias[i].a_command, 1295 1.7 itojun sizeof(expansion)); 1296 1.1 jtc substitute(expansion, "$FILE", file); 1297 1.1 jtc substitute(expansion, "$USER", user); 1298 1.1 jtc substitute(expansion, "$HOST", host); 1299 1.5 lukem return (expansion); 1300 1.1 jtc } 1301 1.1 jtc } 1302 1.5 lukem return (NULL); 1303 1.1 jtc } 1304