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
54extern void		mount_unmount();
55
56extern short		printer_status;
57
58extern char		*cur_pwheel,
59			*disable_reason,
60			*reject_reason;
61
62extern FORM		formbuf;
63
64static int		again();
65
66static void		disable(),
67			enable(),
68			accept(),
69			reject(),
70			cancel(),
71			sigpipe(),
72			sigother();
73
74static jmp_buf		cleanup_env,
75			pipe_env;
76
77/**
78 ** do_align() - SET UP PRINTER TO PRINT ALIGNMENT PATTERNS
79 **/
80
81int			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;
192Again:	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
367Done:		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
455P("We were interrupted while printing the alignment pattern;\n");
456P("check the printer. The form is mounted, so you will have to\n");
457P("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
482static 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
510static 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
540static 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
568static 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
598static 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
626static 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
665static void		sigpipe ()
666{
667	signal (SIGPIPE, SIG_IGN);
668	longjmp (pipe_env, 1);
669	/*NOTREACHED*/
670}
671
672static void		sigother (sig)
673	int			sig;
674{
675	signal (sig, SIG_IGN);
676	longjmp (cleanup_env, 1);
677	/*NOTREACHED*/
678}
679