xref: /illumos-gate/usr/src/cmd/lp/cmd/lpadmin/do_align.c (revision 2a8bcb4e)
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