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
do_align(printer,form,pwheel)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
accept(printer)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
reject(printer,reason)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
enable(printer)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
disable(printer,reason,when)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
cancel(req_id)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
again()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
sigpipe()664 static void sigpipe ()
665 {
666 signal (SIGPIPE, SIG_IGN);
667 longjmp (pipe_env, 1);
668 /*NOTREACHED*/
669 }
670
sigother(sig)671 static void sigother (sig)
672 int sig;
673 {
674 signal (sig, SIG_IGN);
675 longjmp (cleanup_env, 1);
676 /*NOTREACHED*/
677 }
678