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