xref: /illumos-gate/usr/src/cmd/lp/cmd/lpadmin/do_align.c (revision 48bbca81)
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 #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 them.
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 (!fgets(answer, sizeof (answer), stdin))
638 			return (0);
639 
640 		answer[strlen(answer) -1] = '\0';
641 
642 		if (
643 		        STREQU(answer, "q")
644 		     || STREQU(answer, "n")
645 		     || STREQU(answer, "no")
646 		)
647 			return (0);
648 
649 		else if (
650 			!*answer
651 		     || STREQU(answer, "y")
652 		     || STREQU(answer, "yes")
653 		)
654 			return (1);
655 
656 		printf (gettext("Sorry?\n"));
657 	}
658 }
659 
660 /**
661  ** sigpipe()
662  ** sigother()
663  **/
664 
665 static void		sigpipe ()
666 {
667 	signal (SIGPIPE, SIG_IGN);
668 	longjmp (pipe_env, 1);
669 	/*NOTREACHED*/
670 }
671 
672 static void		sigother (sig)
673 	int			sig;
674 {
675 	signal (sig, SIG_IGN);
676 	longjmp (cleanup_env, 1);
677 	/*NOTREACHED*/
678 }
679