Home | History | Annotate | Download | only in lpadmin
      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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    664 static void		sigpipe ()
    665 {
    666 	signal (SIGPIPE, SIG_IGN);
    667 	longjmp (pipe_env, 1);
    668 	/*NOTREACHED*/
    669 }
    670 
    671 static void		sigother (sig)
    672 	int			sig;
    673 {
    674 	signal (sig, SIG_IGN);
    675 	longjmp (cleanup_env, 1);
    676 	/*NOTREACHED*/
    677 }
    678