1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 22 /* All Rights Reserved */ 23 24 25 /* 26 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 27 * Use is subject to license terms. 28 * Copyright (c) 2016 by Delphix. All rights reserved. 29 */ 30 31 #include <stdio.h> 32 #include <errno.h> 33 #include <sys/types.h> 34 #include <signal.h> 35 #include <setjmp.h> 36 #include <string.h> 37 #include <stdlib.h> 38 #include <libintl.h> 39 40 #include "lp.h" 41 #include "msgs.h" 42 #include "printers.h" 43 #include "requests.h" 44 #include "form.h" 45 46 #define WHO_AM_I I_AM_LPADMIN 47 #include "oam.h" 48 49 #include "lpadmin.h" 50 51 52 extern void mount_unmount(); 53 54 extern short printer_status; 55 56 extern char *cur_pwheel, 57 *disable_reason, 58 *reject_reason; 59 60 extern FORM formbuf; 61 62 static int again(); 63 64 static void disable(), 65 enable(), 66 accept(), 67 reject(), 68 cancel(), 69 sigpipe(), 70 sigother(); 71 72 static jmp_buf cleanup_env, 73 pipe_env; 74 75 /** 76 ** do_align() - SET UP PRINTER TO PRINT ALIGNMENT PATTERNS 77 **/ 78 79 int do_align (printer, form, pwheel) 80 char *printer, 81 *form, 82 *pwheel; 83 { 84 short status; 85 86 char *req_id = 0, 87 *file_prefix, 88 *rfile, 89 *fifo, 90 buffer[MSGMAX]; 91 92 long printer_chk; 93 94 int try; 95 96 FILE *align_fp, 97 *fifo_fp; 98 99 REQUEST req; 100 101 void (*old_sighup)(), 102 (*old_sigint)(), 103 (*old_sigquit)(), 104 (*old_sigterm)(); 105 106 107 /* 108 * Having reached this point means we've already fetched 109 * the form definition. Now get the alignment pattern. 110 */ 111 if (getform(form, (FORM *)0, (FALERT *)0, &align_fp) == -1) { 112 LP_ERRMSG2 (ERROR, E_LP_GETFORM, form, PERROR); 113 done (1); 114 } 115 if (!align_fp) { 116 LP_ERRMSG1 (WARNING, E_ADM_NOALIGN, form); 117 return (0); 118 } 119 120 /* 121 * Having reached this far also means we've already obtained 122 * the printer status from the Spooler. We'll be changing the 123 * status of the printer and queue and will have to restore 124 * the disable/reject reasons. 125 * NOTE: We can't restore the dates! 126 */ 127 128 129 /* 130 * Construct a request to print a ``file'' for copy. The 131 * equivalent "lp" command (with a filename) would be: 132 * 133 * lp -p printer -H immediate -f form -T type -S charset -c -P 1-N 134 * 135 * "type", "charset", and "N" are derived from the form def'n. 136 * This command would make us next to print ONCE THE FORM IS 137 * MOUNTED. 138 * 139 * NOTE: Don't bother with the -S charset if it isn't mandatory, 140 * so we won't get a rejection. Also, we use either the print 141 * wheel given in the -S option or, lacking that, the currently 142 * mounted print wheel. (The former WILL be mounted.) This also 143 * avoids a rejection by the Spooler. 144 */ 145 req.copies = 1; 146 req.destination = printer; 147 /* req.file_list = 0; This is done later. */ 148 req.form = form; 149 req.actions = ACT_IMMEDIATE | ACT_FAST; 150 req.alert = 0; 151 req.options = "nobanner"; 152 req.priority = 20; /* it doesn't matter */ 153 sprintf ((req.pages = "1-999999")+2, "%d", formbuf.np); 154 req.charset = NAME_ANY; /* Don't restrict the request */ 155 req.modes = 0; 156 req.title = "Aligning Form"; 157 req.input_type = formbuf.conttype; 158 req.user = getname(); 159 160 161 /* 162 * The following code is sensitive to interrupts: We must 163 * catch interrupts so to restore the printer to its original 164 * state, but if we get interrupted while receiving a message 165 * from the Spooler, we can't issue additional messages because 166 * the old responses still in the response queue will confuse us. 167 * Thus while sending/receiving a message we ignore signals. 168 */ 169 if (setjmp(cleanup_env) != 0) 170 done (1); 171 trap_signals (); /* make sure we've done this once */ 172 old_sighup = signal(SIGHUP, sigother); 173 old_sigint = signal(SIGINT, sigother); 174 old_sigquit = signal(SIGQUIT, sigother); 175 old_sigterm = signal(SIGTERM, sigother); 176 177 /* 178 * We'll try the following twice, first with the page list 179 * set as above. If the request gets refused because there's 180 * no filter to convert the content, we'll try again without 181 * the page list. I don't think the number-of-pages-in-a-form 182 * feature is likely to be used much, so why hassle the 183 * administrator? 184 #if defined(WARN_OF_TOO_MANY_LINES) 185 * However, do warn them. 186 #endif 187 */ 188 189 try = 0; 190 Again: try++; 191 192 /* 193 * Have the Spooler allocate a request file and another file 194 * for our use. We'll delete the other file and recreate it 195 * as a FIFO. We can do this because "lpadmin" can only be run 196 * (successfully) by an administrator. This is the key to what 197 * we're doing! We are submitting a named pipe (FIFO) for 198 * printing, which gives us a connection to the printer 199 * through any filters needed! 200 */ 201 202 BEGIN_CRITICAL 203 send_message (S_ALLOC_FILES, 2); 204 if (mrecv(buffer, MSGMAX) != R_ALLOC_FILES) { 205 LP_ERRMSG (ERROR, E_LP_MRECV); 206 done (1); 207 } 208 END_CRITICAL 209 (void)getmessage (buffer, R_ALLOC_FILES, &status, &file_prefix); 210 211 switch (status) { 212 case MOK: 213 break; 214 215 case MNOMEM: 216 LP_ERRMSG (ERROR, E_LP_MNOMEM); 217 done (1); 218 } 219 220 if (!(rfile = malloc((unsigned int)strlen(file_prefix) + 2 + 1))) { 221 LP_ERRMSG (ERROR, E_LP_MALLOC); 222 done (1); 223 } 224 225 sprintf (rfile, "%s-1", file_prefix); 226 227 if (!(fifo = makepath(Lp_Temp, rfile, (char *)0))) { 228 LP_ERRMSG (ERROR, E_LP_MALLOC); 229 done (1); 230 } 231 req.file_list = 0; 232 addlist (&req.file_list, fifo); 233 234 if ( 235 Unlink(fifo) == -1 236 || Mknod(fifo, S_IFIFO | 0600, 0) == -1 237 ) { 238 LP_ERRMSG1 (ERROR, E_ADM_NFIFO, PERROR); 239 done (1); 240 } 241 242 /* 243 * In quick succession, 244 * 245 * - mount the form, 246 * - disable the printer, 247 * - make the Spooler accept requests (if need be), 248 * - submit the request, 249 * - make the Spooler reject requests (if need be). 250 * 251 * We want to minimize the window when another request can 252 * be submitted ahead of ours. Though this window is small, 253 * it is a flaw in our design. Disabling the printer will 254 * help, because it will stop any request that is printing 255 * (if the form is already mounted) and will prevent any other 256 * request from printing. (We disable the printer AFTER trying 257 * to mount the form, because we don't disable a printer for a 258 * regular mount, and we'd like to make this mount APPEAR to 259 * be as similar as possible.) 260 */ 261 262 if (try == 1) { 263 264 mount_unmount (S_MOUNT, printer, NB(form), NB(pwheel)); 265 /* This will die if the mount fails, leaving */ 266 /* the Spooler to clean up our files. */ 267 268 if (!(printer_status & PS_DISABLED)) 269 disable (printer, CUZ_MOUNTING, 0); 270 271 if (printer_status & PS_REJECTED) 272 accept (printer); 273 274 if (setjmp(cleanup_env) != 0) { 275 if (printer_status & PS_DISABLED) 276 disable (printer, disable_reason, 1); 277 if (printer_status & PS_REJECTED) 278 reject (printer, reject_reason); 279 if (req_id && *req_id) 280 cancel (req_id); 281 done (1); 282 } 283 } 284 285 sprintf (rfile, "%s-0", file_prefix); 286 if (putrequest(rfile, &req) == -1) { 287 LP_ERRMSG1 (ERROR, E_LP_PUTREQUEST, PERROR); 288 goto Done; 289 } 290 BEGIN_CRITICAL 291 send_message (S_PRINT_REQUEST, rfile); 292 if (mrecv(buffer, MSGMAX) != R_PRINT_REQUEST) { 293 LP_ERRMSG (ERROR, E_LP_MRECV); 294 done (1); 295 } 296 END_CRITICAL 297 (void)getmessage (buffer, R_PRINT_REQUEST, &status, &req_id, &printer_chk); 298 299 switch (status) { 300 301 case MNOFILTER: 302 if (try == 1) { 303 req.pages = 0; 304 goto Again; 305 } 306 LP_ERRMSG (ERROR, E_ADM_NFILTER); 307 goto Done; 308 309 case MOK: 310 #if defined(WARN_OF_TOO_MANY_LINES) 311 if (!req.pages) 312 LP_ERRMSG1 (WARNING, E_ADM_NPAGES, formbuf.np); 313 #endif 314 break; 315 316 case MERRDEST: 317 accept (printer); /* someone snuck a reject in! */ 318 goto Again; 319 320 case MNOMEM: 321 LP_ERRMSG (ERROR, E_LP_MNOMEM); 322 goto Done; 323 324 case MNODEST: 325 LP_ERRMSG1 (ERROR, E_LP_PGONE, printer); 326 goto Done; 327 328 case MNOOPEN: /* not quite, but close */ 329 LP_ERRMSG (ERROR, E_ADM_ERRDEST); 330 goto Done; 331 332 case MDENYDEST: 333 if (printer_chk) { 334 char reason[1024], 335 *cp = reason; 336 337 if (printer_chk & PCK_TYPE) 338 cp += sprintf(cp, "printer type, "); 339 if (printer_chk & PCK_CHARSET) 340 cp += sprintf(cp, "character set, "); 341 if (printer_chk & PCK_CPI) 342 cp += sprintf(cp, "character pitch, "); 343 if (printer_chk & PCK_LPI) 344 cp += sprintf(cp, "line pitch, "); 345 if (printer_chk & PCK_WIDTH) 346 cp += sprintf(cp, "page width, "); 347 if (printer_chk & PCK_LENGTH) 348 cp += sprintf(cp, "page length, "); 349 if (printer_chk & PCK_BANNER) 350 cp += sprintf(cp, "nobanner, "); 351 cp[-2] = 0; 352 LP_ERRMSG1 (ERROR, E_LP_PTRCHK, reason); 353 goto Done; 354 } 355 /*fall through*/ 356 357 case MUNKNOWN: 358 case MNOMEDIA: 359 case MDENYMEDIA: 360 case MNOMOUNT: 361 case MNOSPACE: 362 case MNOPERM: 363 LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, status); 364 365 Done: if (!(printer_status & PS_DISABLED)) 366 enable (printer); 367 if (printer_status & PS_REJECTED) 368 reject (printer, reject_reason); 369 done (1); 370 /*NOTREACHED*/ 371 } 372 373 if (printer_status & PS_REJECTED) 374 reject (printer, reject_reason); 375 376 /* 377 * Enable printing, to start the interface program going. 378 * Because of our precautions above, our request SHOULD be 379 * the one that prints! 380 */ 381 enable (printer); 382 383 /* 384 * Open the FIFO. One problem: This will hang until the 385 * interface program opens the other end!! 386 */ 387 if (!(fifo_fp = fopen(fifo, "w"))) { 388 LP_ERRMSG1 (ERROR, E_ADM_NFIFO, PERROR); 389 done (1); 390 } 391 392 /* 393 * Loop, dumping the ENTIRE alignment pattern to the FIFO 394 * each time. SIGPIPE probably means the printer faulted. 395 */ 396 if (setjmp(pipe_env) == 0) { 397 /* 398 * Don't send a form feed after the last copy, since 399 * the interface program does that. To implement this, 400 * we send the form feed BEFORE the alignment pattern; 401 * this way we can simply not send it the first time. 402 */ 403 char * ff = 0; 404 char * ff_before = 0; 405 406 /* 407 * If we'll be inserting page breaks between alignment 408 * patterns, look up the control sequence for this. 409 * 410 * MORE: We currently don't have the smarts to figure out 411 * WHICH printer type the Spooler will pick; we would need 412 * to steal some of its code for that (see pickfilter.c) 413 * The best we do so far is use the alignment pattern's 414 * content type, if known. 415 */ 416 if (filebreak) { 417 if ( 418 formbuf.conttype 419 && searchlist_with_terminfo( 420 formbuf.conttype, 421 T /* having "filebreak" => OK */ 422 ) 423 ) 424 tidbit (formbuf.conttype, "ff", &ff); 425 else 426 tidbit (*T, "ff", &ff); 427 } 428 429 signal (SIGPIPE, sigpipe); 430 do { 431 register int n; 432 char buf[BUFSIZ]; 433 434 if (ff_before && *ff_before) 435 fputs (ff_before, fifo_fp); 436 ff_before = ff; 437 438 rewind (align_fp); 439 while ((n = fread(buf, 1, BUFSIZ, align_fp)) > 0) 440 fwrite (buf, 1, n, fifo_fp); 441 442 fflush (fifo_fp); 443 444 } while (again()); 445 fclose (align_fp); 446 signal (SIGPIPE, SIG_DFL); 447 448 } else { 449 cancel (req_id); 450 451 #define P(X) printf (X) 452 453 P("We were interrupted while printing the alignment pattern;\n"); 454 P("check the printer. The form is mounted, so you will have to\n"); 455 P("unmount it if you need to print more alignment patterns later.\n"); 456 } 457 458 /* 459 * Disable the printer, if needed, and close the FIFO. 460 * Use the wait version of the disable, so our request isn't 461 * stopped, and do it before closing the FIFO, so another request 462 * can't start printing if it isn't supposed to. 463 */ 464 if (printer_status & PS_DISABLED) 465 disable (printer, disable_reason, 1); 466 fclose (fifo_fp); 467 468 signal (SIGHUP, old_sighup); 469 signal (SIGINT, old_sigint); 470 signal (SIGQUIT, old_sigquit); 471 signal (SIGTERM, old_sigterm); 472 473 return (1); 474 } 475 476 /** 477 ** accept() - MAKE PRINTER ACCEPT REQUESTS 478 **/ 479 480 static void accept (printer) 481 char *printer; 482 { 483 int rc; 484 485 BEGIN_CRITICAL 486 send_message (S_ACCEPT_DEST, printer); 487 rc = output(R_ACCEPT_DEST); 488 END_CRITICAL 489 490 switch (rc) { 491 case MOK: 492 case MERRDEST: /* someone may have snuck in an accept */ 493 break; 494 495 case MNODEST: /* make up your mind, Spooler! */ 496 case MNOPERM: /* taken care of up front */ 497 default: 498 LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc); 499 done (1); 500 } 501 return; 502 } 503 504 /** 505 ** reject() - MAKE PRINTER REJECT REQUESTS 506 **/ 507 508 static void reject (printer, reason) 509 char *printer, 510 *reason; 511 { 512 int rc; 513 514 BEGIN_CRITICAL 515 send_message (S_REJECT_DEST, printer, reason); 516 rc = output(R_REJECT_DEST); 517 END_CRITICAL 518 519 switch (rc) { 520 521 case MOK: 522 case MERRDEST: /* someone may have snuck in a reject */ 523 break; 524 525 case MNODEST: /* make up your mind, Spooler! */ 526 case MNOPERM: /* taken care of up front */ 527 default: 528 LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc); 529 done (1); 530 } 531 return; 532 } 533 534 /** 535 ** enable() - ENABLE THE PRINTER 536 **/ 537 538 static void enable (printer) 539 char *printer; 540 { 541 int rc; 542 543 BEGIN_CRITICAL 544 send_message (S_ENABLE_DEST, printer); 545 rc = output(R_ENABLE_DEST); 546 END_CRITICAL 547 548 switch (rc) { 549 case MOK: 550 case MERRDEST: /* someone may have snuck in an enable */ 551 break; 552 553 case MNODEST: /* make up your mind, Spooler! */ 554 case MNOPERM: /* taken care of up front */ 555 default: 556 LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc); 557 done (1); 558 } 559 return; 560 } 561 562 /** 563 ** disable() - DISABLE THE PRINTER 564 **/ 565 566 static void disable (printer, reason, when) 567 char *printer, 568 *reason; 569 int when; 570 { 571 int rc; 572 573 BEGIN_CRITICAL 574 send_message (S_DISABLE_DEST, printer, reason, when); 575 rc = output(R_DISABLE_DEST); 576 END_CRITICAL 577 578 switch (rc) { 579 case MOK: 580 case MERRDEST: /* someone may have snuck in a disable */ 581 break; 582 583 case MNODEST: /* make up your mind, Spooler! */ 584 case MNOPERM: /* taken care of up front */ 585 default: 586 LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc); 587 done (1); 588 } 589 return; 590 } 591 592 /** 593 ** cancel() - MAKE PRINTER ACCEPT REQUESTS 594 **/ 595 596 static void cancel (req_id) 597 char *req_id; 598 { 599 int rc; 600 601 BEGIN_CRITICAL 602 send_message (S_CANCEL_REQUEST, req_id); 603 rc = output(R_CANCEL_REQUEST); 604 END_CRITICAL 605 606 switch (rc) { 607 case MOK: 608 case MUNKNOWN: 609 case M2LATE: 610 break; 611 612 case MNOPERM: 613 default: 614 LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc); 615 done (1); 616 } 617 return; 618 } 619 620 /** 621 ** again() 622 **/ 623 624 static int again () 625 { 626 char answer[BUFSIZ]; 627 628 629 for (;;) { 630 631 printf ( 632 gettext("Press return to print an alignment pattern [q to quit]: ") 633 ); 634 635 if (!fgets(answer, sizeof (answer), stdin)) 636 return (0); 637 638 answer[strlen(answer) -1] = '\0'; 639 640 if ( 641 STREQU(answer, "q") 642 || STREQU(answer, "n") 643 || STREQU(answer, "no") 644 ) 645 return (0); 646 647 else if ( 648 !*answer 649 || STREQU(answer, "y") 650 || STREQU(answer, "yes") 651 ) 652 return (1); 653 654 printf (gettext("Sorry?\n")); 655 } 656 } 657 658 /** 659 ** sigpipe() 660 ** sigother() 661 **/ 662 663 static void sigpipe () 664 { 665 signal (SIGPIPE, SIG_IGN); 666 longjmp (pipe_env, 1); 667 /*NOTREACHED*/ 668 } 669 670 static void sigother (sig) 671 int sig; 672 { 673 signal (sig, SIG_IGN); 674 longjmp (cleanup_env, 1); 675 /*NOTREACHED*/ 676 } 677