Home | History | Annotate | Download | only in lpsched
      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 
     22 /*
     23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
     28 /*	  All Rights Reserved  	*/
     29 
     30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     31 
     32 /* EMACS_MODES: !fill, lnumb, !overwrite, !nodelete, !picture */
     33 
     34 #include "lpsched.h"
     35 #include "ctype.h"
     36 #include "sys/stat.h"
     37 #include <syslog.h>
     38 
     39 /*
     40  * Macro to test if we should notify the user.
     41  */
     42 #define SHOULD_NOTIFY(PRS) \
     43 	( \
     44 		(PRS)->request->actions & (ACT_MAIL|ACT_WRITE|ACT_NOTIFY)\
     45 	     || (PRS)->request->alert \
     46 	)
     47 
     48 static char *		geterrbuf ( RSTATUS * );
     49 
     50 /**
     51  ** dowait() - CLEAN UP CHILD THAT HAS FINISHED, RESCHEDULE ANOTHER TASK
     52  **/
     53 
     54 void
     55 dowait (void)
     56 {
     57 	int			exited,
     58 				killed,
     59 				canned,
     60 				i;
     61 	EXEC			*ep;
     62 	char			*errbuf = NULL;
     63 	register RSTATUS	*prs;
     64 	register PSTATUS	*pps;
     65 	register ALERT		*pas;
     66 
     67 	syslog(LOG_DEBUG, "dowait(%d)", DoneChildren);
     68 	while (DoneChildren > 0) {
     69 		DoneChildren--;
     70 
     71 		for (i = 0; (ep = Exec_Table[i]) != NULL; i++)
     72 			if (ep->pid == -99)
     73 				break;
     74 
     75 		syslog(LOG_DEBUG, "dowait(): 0x%8.8x", ep);
     76 
     77 		if (Exec_Table[i] == NULL)	/* nothing to cleanup */
     78 			continue;
     79 
     80 		syslog(LOG_DEBUG, "dowait(): cleaning up 0x%8.8x", ep);
     81 
     82 		ep->pid = 0;
     83 		ep->key = 0;	/* avoid subsequent sneaks */
     84 		if (ep->md)
     85 			DROP_MD(ep->md);
     86 
     87 		killed = KILLED(ep->status);
     88 		exited = EXITED(ep->status);
     89 
     90 		syslog(LOG_DEBUG, "dowait(): type %d, killed %d, exited %d",
     91 			ep->type, killed, exited);
     92 
     93 		switch (ep->type) {
     94 
     95 		case EX_INTERF:
     96 			/*
     97 			 * WARNING: It could be that when we get here
     98 			 *
     99 			 *	pps->request->printer != pps
    100 			 *
    101 			 * because the request has been assigned to
    102 			 * another printer.
    103 			 */
    104 			pps = ep->ex.printer;
    105 			prs = pps->request;
    106 			pps->request = 0;
    107 			pps->status &= ~PS_BUSY;
    108 
    109 			/*
    110 			 * If the interface program exited cleanly
    111 			 * or with just a user error, the printer
    112 			 * is assumed to be working.
    113 			 */
    114 			if (0 <= exited && exited < EXEC_EXIT_USER) {
    115 				pps->status &= ~PS_FAULTED;
    116 				if (pps->alert->active)
    117 					cancel_alert (A_PRINTER, pps);
    118 			}
    119 
    120 			/*
    121 			 * If the interface program was killed with
    122 			 * SIGTERM, it may have been because we canceled
    123 			 * the request, disabled the printer, or for some
    124 			 * other reason stopped the request.
    125 			 * If so, clear the "killed" flag because that's
    126 			 * not the condition of importance here.
    127 			 */
    128 			canned = 0;
    129 			if (killed == SIGTERM) {
    130 				if (prs->request->outcome & RS_CANCELLED)
    131 					canned = 1;
    132 
    133 				if (
    134 					canned
    135 				     || pps->status & (PS_DISABLED|PS_FAULTED)
    136 				     || prs->request->outcome & RS_STOPPED
    137 				     || Shutdown
    138 				)
    139 					killed = 0;
    140 			}
    141 
    142 			/*
    143 			 * If there was standard error output from the
    144 			 * interface program, or if the interface program
    145 			 * exited with a (user) exit code, or if it got
    146 			 * a strange signal, the user should be notified.
    147 			 */
    148 			errbuf = geterrbuf(prs);
    149 			if (
    150 				errbuf
    151 			     || (0 < exited && exited <= EXEC_EXIT_USER)
    152 			     || killed
    153 			) {
    154 				if (exited != EXIT_RETRY) {
    155 					prs->request->outcome |= RS_FAILED;
    156 				}
    157 				prs->request->outcome |= RS_NOTIFY;
    158 				notify (prs, errbuf, killed, exited, 0);
    159 				if (errbuf)
    160 					Free (errbuf);
    161 
    162 			/*
    163 			 * If the request was canceled, call "notify()"
    164 			 * in case we're to notify the user.
    165 			 */
    166 			} else if (canned) {
    167 				if (SHOULD_NOTIFY(prs))
    168 					prs->request->outcome |= RS_NOTIFY;
    169 				notify (prs, (char *)0, 0, 0, 0);
    170 
    171 			/*
    172 			 * If the request finished successfully, call
    173 			 * "notify()" in case we're to notify the user.
    174 			 */
    175 			} else if (exited == 0) {
    176 				prs->request->outcome |= RS_PRINTED;
    177 
    178 				if (SHOULD_NOTIFY(prs))
    179 					prs->request->outcome |= RS_NOTIFY;
    180 				notify (prs, (char *)0, 0, 0, 0);
    181 			}
    182 
    183 			/*
    184 			 * If the interface program exits with an
    185 			 * exit code higher than EXEC_EXIT_USER, it's
    186 			 * a special case.
    187 			 */
    188 
    189 			switch (exited) {
    190 
    191 			case EXEC_EXIT_FAULT:
    192 				printer_fault (pps, prs, 0, 0);
    193 				break;
    194 
    195 			case EXEC_EXIT_HUP:
    196 				printer_fault (pps, prs, HANGUP_FAULT, 0);
    197 				break;
    198 
    199 			case EXEC_EXIT_INTR:
    200 				printer_fault (pps, prs, INTERRUPT_FAULT, 0);
    201 				break;
    202 
    203 			case EXEC_EXIT_PIPE:
    204 				printer_fault (pps, prs, PIPE_FAULT, 0);
    205 				break;
    206 
    207 			case EXEC_EXIT_EXIT:
    208 				note (
    209 					"Bad exit from interface program for printer %s: %d\n",
    210 					pps->printer->name,
    211 					ep->Errno
    212 				);
    213 				printer_fault (pps, prs, EXIT_FAULT, 0);
    214 				break;
    215 
    216 			case EXEC_EXIT_NPORT:
    217 				printer_fault (pps, prs, OPEN_FAULT, ep->Errno);
    218 				break;
    219 
    220 			case EXEC_EXIT_TMOUT:
    221 				printer_fault (pps, prs, TIMEOUT_FAULT, 0);
    222 				break;
    223 
    224 			case EXEC_EXIT_NOPEN:
    225 				errno = ep->Errno;
    226 				note (
    227 					"Failed to open a print service file (%s).\n",
    228 					PERROR
    229 				);
    230 				break;
    231 
    232 			case EXEC_EXIT_NEXEC:
    233 				errno = ep->Errno;
    234 				note (
    235 					"Failed to exec child process (%s).\n",
    236 					PERROR
    237 				);
    238 				break;
    239 
    240 			case EXEC_EXIT_NOMEM:
    241 				mallocfail ();
    242 				break;
    243 
    244 			case EXEC_EXIT_NFORK:
    245 				errno = ep->Errno;
    246 				note (
    247 					"Failed to fork child process (%s).\n",
    248 					PERROR
    249 				);
    250 				break;
    251 
    252 			case EXEC_EXIT_NPUSH:
    253 				printer_fault (pps, prs, PUSH_FAULT, ep->Errno);
    254 				break;
    255 
    256 			default:
    257 				if ((exited & EXEC_EXIT_NMASK) == EXEC_EXIT_NDIAL)
    258 					dial_problem (
    259 						pps,
    260 						prs,
    261 						exited & ~EXEC_EXIT_NMASK
    262 					);
    263 
    264 				else if (
    265 					exited < -1
    266 				     || exited > EXEC_EXIT_USER
    267 				)
    268 					note (
    269 						"Bad exit from exec() for printer %s: %d\n",
    270 						pps->printer->name,
    271 						exited
    272 					);
    273 
    274 				break;
    275 			}
    276 
    277 			/*
    278 			 * Being in the "dowait()" routine means the
    279 			 * interface (and fast filter!) have stopped.
    280 			 * If we have a fault and we're expected to try
    281 			 * again later, make sure we try again later.
    282 			 */
    283 			if (
    284 				(pps->status & PS_FAULTED)
    285 			     && !STREQU(pps->printer->fault_rec, NAME_WAIT)
    286 			     && !(pps->status & (PS_LATER|PS_DISABLED))
    287 			) {
    288 				load_str (&pps->dis_reason, CUZ_STOPPED);
    289 				schedule (EV_LATER, WHEN_PRINTER, EV_ENABLE, pps);
    290 			}
    291 
    292 			prs->request->outcome &= ~(RS_PRINTING|RS_STOPPED);
    293 
    294 			/*
    295 			 * If the printer to which this request was
    296 			 * assigned is not able to handle requests now,
    297 			 * push waiting requests off on to another
    298 			 * printer.
    299 			 */
    300 			if (prs->printer->status & (PS_FAULTED|PS_DISABLED|PS_LATER))
    301 				(void)queue_repel (prs->printer, 0, (qchk_fnc_type)0);
    302 
    303 			/*
    304 			 * If the request is now assigned to a different
    305 			 * printer, call "schedule()" to fire up an
    306 			 * interface. If this request also happens to
    307 			 * be dead, or in need of refiltering, it won't
    308 			 * get scheduled.
    309 			 */
    310 			if (
    311 				prs->printer != pps
    312 			)
    313 				schedule (EV_INTERF, prs->printer);
    314 
    315 			check_request (prs);
    316 
    317 			/*
    318 			 * Attract the FIRST request that is waiting to
    319 			 * print to this printer, unless the printer isn't
    320 			 * ready to print another request. We do this
    321 			 * even though requests may already be assigned
    322 			 * to this printer, because a request NOT assigned
    323 			 * might be ahead of them in the queue.
    324 			 */
    325 			if (!(pps->status & (PS_FAULTED|PS_DISABLED|PS_LATER)))
    326 				queue_attract (pps, qchk_waiting, 1);
    327 
    328 			break;
    329 
    330 		case EX_SLOWF:
    331 			prs = ep->ex.request;
    332 			ep->ex.request = 0;
    333 			prs->exec = 0;
    334 			prs->request->outcome &= ~RS_FILTERING;
    335 
    336 			/*
    337 			 * If the slow filter was killed with SIGTERM,
    338 			 * it may have been because we canceled the
    339 			 * request, stopped the filtering, or put a
    340 			 * change hold on the request. If so, clear
    341 			 * the "killed" flag because that's not the
    342 			 * condition of importance.
    343 			 */
    344 			canned = 0;
    345 			if (killed == SIGTERM){
    346 				if (prs->request->outcome & RS_CANCELLED)
    347 					canned = 1;
    348 
    349 				if (
    350 					canned
    351 				     || prs->request->outcome & RS_STOPPED
    352 				     || Shutdown
    353 				)
    354 					killed = 0;
    355 			}
    356 
    357 			/*
    358 			 * If there was standard error output from the
    359 			 * slow filter, or if the interface program exited
    360 			 * with a non-zero exit code, the user should
    361 			 * be notified.
    362 			 */
    363 			errbuf = geterrbuf(prs);
    364 			if (prs->request->outcome
    365 			    & (RS_REFILTER | RS_STOPPED)) {
    366 				if (errbuf) {
    367 					Free(errbuf);
    368 					errbuf = NULL;
    369 				}
    370 			}
    371 			if (
    372 				errbuf
    373 			     || 0 < exited && exited <= EXEC_EXIT_USER
    374 			     || killed
    375 			) {
    376 				prs->request->outcome |= RS_FAILED;
    377 				prs->request->outcome |= RS_NOTIFY;
    378 				notify (prs, errbuf, killed, exited, 1);
    379 				if (errbuf)
    380 					Free (errbuf);
    381 
    382 
    383 			/*
    384 			 * If the request was canceled, call "notify()"
    385 			 * in case we're to notify the user.
    386 			 */
    387 			} else if (canned) {
    388 				if (SHOULD_NOTIFY(prs))
    389 					prs->request->outcome |= RS_NOTIFY;
    390 				notify (prs, (char *)0, 0, 0, 1);
    391 
    392 			/*
    393 			 * If the slow filter exited normally, mark
    394 			 * the request as finished slow filtering.
    395 			 */
    396 			} else if (exited == 0) {
    397 				prs->request->outcome |= RS_FILTERED;
    398 
    399 			} else if (exited == -1) {
    400 				/*EMPTY*/;
    401 
    402 			} else if (exited == EXEC_EXIT_NOPEN) {
    403 				errno = ep->Errno;
    404 				note (
    405 					"Failed to open a print service file (%s).\n",
    406 					PERROR
    407 				);
    408 
    409 			} else if (exited == EXEC_EXIT_NEXEC) {
    410 				errno = ep->Errno;
    411 				note (
    412 					"Failed to exec child process (%s).\n",
    413 					PERROR
    414 				);
    415 
    416 			} else if (exited == EXEC_EXIT_NOMEM) {
    417 				mallocfail ();
    418 
    419 			}
    420 
    421 			prs->request->outcome &= ~RS_STOPPED;
    422 
    423 			schedule (EV_INTERF, prs->printer);
    424 			if (
    425 				prs->request->outcome & RS_REFILTER
    426 			)
    427 				schedule (EV_SLOWF, prs);
    428 			else
    429 				schedule (EV_SLOWF, (RSTATUS *)0);
    430 
    431 			check_request (prs);
    432 			break;
    433 
    434 		case EX_NOTIFY:
    435 			prs = ep->ex.request;
    436 			ep->ex.request = 0;
    437 			prs->exec = 0;
    438 
    439 			prs->request->outcome &= ~RS_NOTIFYING;
    440 			    if (!Shutdown || !killed)
    441 				prs->request->outcome &= ~RS_NOTIFY;
    442 
    443 			/*
    444 			 * Now that this notification process slot
    445 			 * has opened up, schedule the next notification
    446 			 * (if any).
    447 			 */
    448 			schedule (EV_NOTIFY, (RSTATUS *)0);
    449 
    450 			check_request (prs);
    451 			break;
    452 
    453 		case EX_ALERT:
    454 			pas = ep->ex.printer->alert;
    455 			goto CleanUpAlert;
    456 
    457 		case EX_FALERT:
    458 			pas = ep->ex.form->alert;
    459 			goto CleanUpAlert;
    460 
    461 		case EX_PALERT:
    462 			pas = ep->ex.pwheel->alert;
    463 			/*
    464 			 * CAUTION: It may well be that we've removed
    465 			 * the print wheel by the time we get here.
    466 			 * Only the alert structure (and exec structure)
    467 			 * can be considered okay.
    468 			 */
    469 
    470 CleanUpAlert:
    471 			if (Shutdown)
    472 				break;
    473 
    474 			if (ep->flags & EXF_RESTART) {
    475 				ep->flags &= ~(EXF_RESTART);
    476 				if (exec(ep->type, ep->ex.form) == 0) {
    477 					pas->active = 1;
    478 					break;
    479 				}
    480 			}
    481 			(void)Unlink (pas->msgfile);
    482 			break;
    483 
    484 		}
    485 	}
    486 
    487 	return;
    488 }
    489 
    490 
    491 /**
    492  ** geterrbuf() - READ NON-BLANK STANDARD ERROR OUTPUT
    493  **/
    494 
    495 static char *
    496 geterrbuf(RSTATUS *prs)
    497 {
    498 	register char		*cp;
    499 	int                     fd,
    500 				n;
    501 	char                    *buf    = 0,
    502 				*file;
    503 	struct stat             statbuf;
    504 
    505 	if (!prs) return(NULL);
    506 
    507 	file = makereqerr(prs);
    508 	if (
    509 		Stat(file, &statbuf) == 0
    510 	     && statbuf.st_size
    511 	     && (fd = Open(file, O_RDONLY)) != -1
    512 	) {
    513 		/*
    514 		 * Don't die if we can't allocate space for this
    515 		 * file--the file may be huge!
    516 		 */
    517 		lp_alloc_fail_handler = 0;
    518 		if ((buf = Malloc(statbuf.st_size + 1)))
    519 			if ((n = Read(fd, buf, statbuf.st_size)) > 0) {
    520 				buf[n] = 0;
    521 
    522 				/*
    523 				 * NOTE: Ignore error output with no
    524 				 * printable text. This hides problems we
    525 				 * have with some shell scripts that
    526 				 * occasionally cause spurious newlines
    527 				 * when stopped via SIGTERM. Without this
    528 				 * check for non-blank output, stopping
    529 				 * a request sometimes causes a request
    530 				 * failure.
    531 				 */
    532 				for (cp = buf; *cp && isspace(*cp); cp++)
    533 					;
    534 				if (!*cp) {
    535 					Free (buf);
    536 					buf = 0;
    537 				}
    538 			} else {
    539 				Free (buf);
    540 				buf = 0;
    541 			}
    542 		lp_alloc_fail_handler = mallocfail;
    543 		Close(fd);
    544 	}
    545 	if (file)
    546 		Free (file);
    547 
    548 	return (buf);
    549 }
    550 
    551 /**
    552  ** check_request() - CLEAN UP AFTER REQUEST
    553  **/
    554 
    555 void
    556 check_request(RSTATUS *prs)
    557 {
    558 	/*
    559 	 * If the request is done, decrement the count of requests
    560 	 * needing the form or print wheel. Update the disk copy of
    561 	 * the request. If we're finished with the request, get rid of it.
    562 	 */
    563 	if (prs->request->outcome & RS_DONE) {
    564 		unqueue_form (prs);
    565 		unqueue_pwheel (prs);
    566 		putrequest (prs->req_file, prs->request);
    567 		if (!(prs->request->outcome & (RS_ACTIVE | RS_NOTIFY))) {
    568 			rmfiles (prs, 1);
    569 			free_rstatus (prs);
    570 		}
    571 	}
    572 	return;
    573 }
    574 
    575 /**
    576  ** check_children()
    577  **/
    578 
    579 void
    580 check_children(void)
    581 {
    582 	register int		i;
    583 
    584 	for (i = 0; Exec_Table[i] != NULL; i++)
    585 		if (Exec_Table[i]->pid > 0)
    586 			break;
    587 
    588 	if (Exec_Table[i] == NULL)
    589 		Shutdown = 2;
    590 }
    591