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