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