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 
     31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     32 
     33 #include "stdarg.h"
     34 #include "lpsched.h"
     35 #include <syslog.h>
     36 
     37 extern int isStartingForms;
     38 
     39 typedef struct later {
     40 	struct later *		next;
     41 	int			event,
     42 				ticks;
     43 	union arg {
     44 		PSTATUS *		printer;
     45 		RSTATUS *		request;
     46 		FSTATUS *		form;
     47 	}			arg;
     48 }			LATER;
     49 
     50 static LATER		LaterHead	= { 0 },
     51 			TempHead;
     52 
     53 static void		ev_interf(PSTATUS *);
     54 static void		ev_message(PSTATUS *);
     55 static void		ev_form_message(FSTATUS *);
     56 static int		ev_slowf(RSTATUS *);
     57 static int		ev_notify(RSTATUS *);
     58 
     59 static EXEC		*find_exec_slot(EXEC **);
     60 
     61 static char *_event_name(int event)
     62 {
     63 	static char *_names[] = {
     64 	"", "EV_SLOWF", "EV_INTERF", "EV_NOTIFY", "EV_LATER", "EV_ALARM",
     65 	"EV_MESSAGE", "EV_ENABLE", "EV_FORM_MESSAGE", NULL };
     66 
     67 	if ((event < 0) || (event > EV_FORM_MESSAGE))
     68 		return ("BAD_EVENT");
     69 	else
     70 		return (_names[event]);
     71 }
     72 
     73 /*
     74  * schedule() - SCHEDULE BY EVENT
     75  */
     76 
     77 /*VARARGS1*/
     78 void
     79 schedule(int event, ...)
     80 {
     81 	va_list			ap;
     82 
     83 	LATER *			plprev;
     84 	LATER *			pl;
     85 	LATER *			plnext	= 0;
     86 
     87 	register PSTATUS *	pps;
     88 	register RSTATUS *	prs;
     89 	register FSTATUS *	pfs;
     90 
     91 	int i;
     92 	/*
     93 	 * If we're in the process of shutting down, don't
     94 	 * schedule anything.
     95 	 */
     96 	syslog(LOG_DEBUG, "schedule(%s)", _event_name(event));
     97 
     98 	if (Shutdown)
     99 		return;
    100 
    101 	va_start (ap, event);
    102 
    103 	/*
    104 	 * If we're still in the process of starting up, don't start
    105 	 * anything! Schedule it for one tick later. While we're starting
    106 	 * ticks aren't counted, so the events won't be started.
    107 	 * HOWEVER, with a count of 1, a single EV_ALARM after we're
    108 	 * finished starting will be enough to clear all things scheduled
    109 	 * for later.
    110 	 */
    111 	if (Starting) {
    112 		switch (event) {
    113 
    114 		case EV_INTERF:
    115 		case EV_ENABLE:
    116 			pps = va_arg(ap, PSTATUS *);
    117 			schedule (EV_LATER, 1, event, pps);
    118 			goto Return;
    119 
    120 		case EV_SLOWF:
    121 		case EV_NOTIFY:
    122 			prs = va_arg(ap, RSTATUS *);
    123 			schedule (EV_LATER, 1, event, prs);
    124 			goto Return;
    125 
    126 		case EV_MESSAGE:
    127 			pps = va_arg(ap, PSTATUS *);
    128 			schedule (EV_LATER, 1, event, pps);
    129 			goto Return;
    130 
    131 		case EV_FORM_MESSAGE:
    132 			pfs = va_arg(ap, FSTATUS *);
    133 			schedule (EV_LATER, 1, event, pfs);
    134 			goto Return;
    135 
    136 		case EV_LATER:
    137 			/*
    138 			 * This is okay--in fact it may be us!
    139 			 */
    140 			break;
    141 
    142 		case EV_ALARM:
    143 			/*
    144 			 * The alarm will go off again, hold off for now.
    145 			 */
    146 			goto Return;
    147 
    148 		}
    149 	}
    150 
    151 	/*
    152 	 * Schedule something:
    153 	 */
    154 	switch (event) {
    155 
    156 	case EV_INTERF:
    157 		if ((pps = va_arg(ap, PSTATUS *)) != NULL)
    158 			ev_interf (pps);
    159 
    160 		else
    161 			for (i = 0; PStatus != NULL && PStatus[i] != NULL; i++)
    162 				ev_interf (PStatus[i]);
    163 
    164 		break;
    165 
    166 	/*
    167 	 * The EV_ENABLE event is used to get a printer going again
    168 	 * after waiting for a fault to be cleared. We used to use
    169 	 * just the EV_INTERF event, but this wasn't enough: For
    170 	 * requests that can go on several different printers (e.g.
    171 	 * queued for class, queued for ``any''), a printer is
    172 	 * arbitrarily assigned. The EV_INTERF event just checks
    173 	 * assignments, not possibilities, so a printer with no
    174 	 * assigned requests but still eligible to handle one or
    175 	 * more requests would never automatically start up again after
    176 	 * a fault. The EV_ENABLE event calls "enable()" which eventually
    177 	 * gets around to invoking the EV_INTERF event. However, it first
    178 	 * calls "queue_attract()" to get an eligible request assigned
    179 	 * so that things proceed. This also makes sense from the
    180 	 * following standpoint: The documented method of getting a
    181 	 * printer going, while it is waiting for auto-retry, is to
    182 	 * manually issue the enable command!
    183 	 *
    184 	 * Note: "enable()" will destroy the current record of the fault,
    185 	 * so if the fault is still with us any new alert will not include
    186 	 * the history of each repeated fault. This is a plus and a minus,
    187 	 * usually a minus: While a repeated fault may occasionally show
    188 	 * a varied record, usually the same reason is given each time;
    189 	 * before switching to EV_ENABLE we typically saw a boring, long
    190 	 * list of identical reasons.
    191 	 */
    192 	case EV_ENABLE:
    193 		if ((pps = va_arg(ap, PSTATUS *)) != NULL)
    194 			enable (pps);
    195 		else
    196 			for (i = 0; PStatus != NULL && PStatus[i] != NULL; i++)
    197 				enable (PStatus[i]);
    198 		break;
    199 
    200 	case EV_SLOWF:
    201 		if ((prs = va_arg(ap, RSTATUS *)) != NULL)
    202 			(void) ev_slowf (prs);
    203 		else
    204 			for (prs = Request_List; prs && ev_slowf(prs) != -1;
    205 				prs = prs->next);
    206 		break;
    207 
    208 	case EV_NOTIFY:
    209 		if ((prs = va_arg(ap, RSTATUS *)) != NULL)
    210 			(void) ev_notify (prs);
    211 		else
    212 			for (prs = Request_List; prs && ev_notify(prs) != -1;
    213 				prs = prs->next);
    214 		break;
    215 
    216 	case EV_MESSAGE:
    217 		pps = va_arg(ap, PSTATUS *);
    218 		ev_message(pps);
    219 		break;
    220 
    221 	case EV_FORM_MESSAGE:
    222 		pfs = va_arg(ap, FSTATUS *);
    223 		ev_form_message(pfs);
    224 		break;
    225 
    226 	case EV_LATER:
    227 		pl = (LATER *)Malloc(sizeof (LATER));
    228 
    229 		if (!LaterHead.next)
    230 			alarm (CLOCK_TICK);
    231 
    232 		pl->next = LaterHead.next;
    233 		LaterHead.next = pl;
    234 
    235 		pl->ticks = va_arg(ap, int);
    236 		pl->event = va_arg(ap, int);
    237 		switch (pl->event) {
    238 
    239 		case EV_MESSAGE:
    240 		case EV_INTERF:
    241 		case EV_ENABLE:
    242 			pl->arg.printer = va_arg(ap, PSTATUS *);
    243 			if (pl->arg.printer)
    244 				pl->arg.printer->status |= PS_LATER;
    245 			break;
    246 
    247 		case EV_FORM_MESSAGE:
    248 			pl->arg.form = va_arg(ap, FSTATUS *);
    249 			break;
    250 
    251 		case EV_SLOWF:
    252 		case EV_NOTIFY:
    253 			pl->arg.request = va_arg(ap, RSTATUS *);
    254 			break;
    255 
    256 		}
    257 		break;
    258 
    259 	case EV_ALARM:
    260 		Sig_Alrm = 0;
    261 
    262 		/*
    263 		 * The act of scheduling some of the ``laters'' may
    264 		 * cause new ``laters'' to be added to the list.
    265 		 * To ease the handling of the linked list, we first
    266 		 * run through the list and move all events ready to
    267 		 * be scheduled to another list. Then we schedule the
    268 		 * events off the new list. This leaves the main ``later''
    269 		 * list ready for new events.
    270 		 */
    271 		TempHead.next = 0;
    272 		for (pl = (plprev = &LaterHead)->next; pl; pl = plnext) {
    273 			plnext = pl->next;
    274 			if (--pl->ticks)
    275 				plprev = pl;
    276 			else {
    277 				plprev->next = plnext;
    278 
    279 				pl->next = TempHead.next;
    280 				TempHead.next = pl;
    281 			}
    282 		}
    283 
    284 		for (pl = TempHead.next; pl; pl = plnext) {
    285 			plnext = pl->next;
    286 			switch (pl->event) {
    287 
    288 			case EV_MESSAGE:
    289 			case EV_INTERF:
    290 			case EV_ENABLE:
    291 				pl->arg.printer->status &= ~PS_LATER;
    292 				schedule (pl->event, pl->arg.printer);
    293 				break;
    294 
    295 			case EV_FORM_MESSAGE:
    296 				schedule (pl->event, pl->arg.form);
    297 				break;
    298 
    299 			case EV_SLOWF:
    300 			case EV_NOTIFY:
    301 				schedule (pl->event, pl->arg.request);
    302 				break;
    303 
    304 			}
    305 			Free ((char *)pl);
    306 		}
    307 
    308 		if (LaterHead.next)
    309 			alarm (CLOCK_TICK);
    310 		break;
    311 
    312 	}
    313 
    314 Return:	va_end (ap);
    315 
    316 	return;
    317 }
    318 
    319 /*
    320  * maybe_schedule() - MAYBE SCHEDULE SOMETHING FOR A REQUEST
    321  */
    322 
    323 void
    324 maybe_schedule(RSTATUS *prs)
    325 {
    326 	/*
    327 	 * Use this routine if a request has been changed by some
    328 	 * means so that it is ready for filtering or printing,
    329 	 * but a previous filtering or printing process for this
    330 	 * request MAY NOT have finished yet. If a process is still
    331 	 * running, then the cleanup of that process will cause
    332 	 * "schedule()" to be called. Calling "schedule()" regardless
    333 	 * might make another request slip ahead of this request.
    334 	 */
    335 
    336 	/*
    337 	 * "schedule()" will refuse if this request is filtering.
    338 	 * It will also refuse if the request ``was'' filtering
    339 	 * but the filter was terminated in "validate_request()",
    340 	 * because we can not have heard from the filter process
    341 	 * yet. Also, when called with a particular request,
    342 	 * "schedule()" won't slip another request ahead.
    343 	 */
    344 	if (NEEDS_FILTERING(prs))
    345 		schedule (EV_SLOWF, prs);
    346 
    347 	else if (!(prs->request->outcome & RS_STOPPED))
    348 		schedule (EV_INTERF, prs->printer);
    349 
    350 	return;
    351 }
    352 
    353 static void
    354 ev_message(PSTATUS *pps)
    355 {
    356 	register RSTATUS	*prs;
    357 	char			toSelf;
    358 
    359 	syslog(LOG_DEBUG, "ev_message(%s)",
    360 	       (pps && pps->request && pps->request->req_file ?
    361 		pps->request->req_file : "NULL"));
    362 
    363 	toSelf = 0;
    364 	for (prs = Request_List; prs != NULL; prs = prs->next)
    365 		if (prs->printer == pps) {
    366 			note("prs (%d) pps (%d)\n", prs, pps);
    367 			if (!toSelf) {
    368 				toSelf = 1;
    369 				exec(EX_FAULT_MESSAGE, pps, prs);
    370 			}
    371 		}
    372 }
    373 
    374 static void
    375 ev_form_message_body(FSTATUS *pfs, RSTATUS *prs, char *toSelf, char ***sysList)
    376 {
    377 	syslog(LOG_DEBUG, "ev_form_message_body(%s, %d, 0x%x)",
    378 	      (pfs && pfs->form && pfs->form->name ? pfs->form->name : "NULL"),
    379 	      (toSelf ? *toSelf : 0),
    380 		sysList);
    381 
    382 	if (!*toSelf) {
    383 		*toSelf = 1;
    384 		exec(EX_FORM_MESSAGE, pfs);
    385 	}
    386 }
    387 
    388 static void
    389 ev_form_message(FSTATUS *pfs)
    390 {
    391 	register RSTATUS	*prs;
    392 	char **sysList;
    393 	char toSelf;
    394 
    395 	syslog(LOG_DEBUG, "ev_form_message(%s)",
    396 	       (pfs && pfs->form && pfs->form->name ?
    397 		pfs->form->name : "NULL"));
    398 
    399 	toSelf = 0;
    400 	sysList = NULL;
    401 
    402 	for (prs = Request_List; prs != NULL; prs = prs->next)
    403 		if (prs->form == pfs)
    404 			ev_form_message_body(pfs, prs, &toSelf, &sysList);
    405 
    406 	if (NewRequest && (NewRequest->form == pfs))
    407 		ev_form_message_body(pfs, NewRequest, &toSelf, &sysList);
    408 
    409 	freelist(sysList);
    410 }
    411 
    412 /*
    413  * ev_interf() - CHECK AND EXEC INTERFACE PROGRAM
    414  */
    415 
    416 /*
    417  * Macro to check if the request needs a print wheel or character set (S)
    418  * and the printer (P) has it mounted or can select it. Since the request
    419  * has already been approved for the printer, we don't have to check the
    420  * character set, just the mount. If the printer has selectable character
    421  * sets, there's nothing to check so the request is ready to print.
    422  */
    423 #define	MATCH(PRS, PPS) (\
    424 		!(PPS)->printer->daisy || \
    425 		!(PRS)->pwheel_name || \
    426 		!((PRS)->status & RSS_PWMAND) || \
    427 		STREQU((PRS)->pwheel_name, NAME_ANY) || \
    428 		((PPS)->pwheel_name && \
    429 		STREQU((PPS)->pwheel_name, (PRS)->pwheel_name)))
    430 
    431 
    432 static void
    433 ev_interf(PSTATUS *pps)
    434 {
    435 	register RSTATUS	*prs;
    436 
    437 	syslog(LOG_DEBUG, "ev_interf(%s)",
    438 	       (pps && pps->request && pps->request->req_file ?
    439 		pps->request->req_file : "NULL"));
    440 
    441 
    442 	/*
    443 	 * If the printer isn't tied up doing something
    444 	 * else, and isn't disabled, see if there is a request
    445 	 * waiting to print on it. Note: We don't include
    446 	 * PS_FAULTED here, because simply having a printer
    447 	 * fault (without also being disabled) isn't sufficient
    448 	 * to keep us from trying again. (In fact, we HAVE TO
    449 	 * try again, to see if the fault has gone away.)
    450 	 *
    451 	 * NOTE: If the printer is faulted but the filter controlling
    452 	 * the printer is waiting for the fault to clear, a
    453 	 * request will still be attached to the printer, as
    454 	 * evidenced by "pps->request", so we won't try to
    455 	 * schedule another request!
    456 	 */
    457 	if (pps->request || pps->status & (PS_DISABLED|PS_LATER|PS_BUSY))
    458 		return;
    459 
    460 	for (prs = Request_List; prs != NULL; prs = prs->next) {
    461 		if ((prs->printer == pps) && (qchk_waiting(prs)) &&
    462 		    isFormUsableOnPrinter(pps, prs->form) && MATCH(prs, pps)) {
    463 		/*
    464 		 * Just because the printer isn't busy and the
    465 		 * request is assigned to this printer, don't get the
    466 		 * idea that the request can't be printing (RS_ACTIVE),
    467 		 * because another printer may still have the request
    468 		 * attached but we've not yet heard from the child
    469 		 * process controlling that printer.
    470 		 *
    471 		 * We have the waiting request, we have
    472 		 * the ready (local) printer. If the exec fails
    473 		 * because the fork failed, schedule a
    474 		 * try later and claim we succeeded. The
    475 		 * later attempt will sort things out,
    476 		 * e.g. will re-schedule if the fork fails
    477 		 * again.
    478 		 */
    479 			pps->request = prs;
    480 			if (exec(EX_INTERF, pps) == 0) {
    481 				pps->status |= PS_BUSY;
    482 				return;
    483 			}
    484 			pps->request = 0;
    485 			if (errno == EAGAIN) {
    486 				load_str (&pps->dis_reason, CUZ_NOFORK);
    487 				schedule (EV_LATER, WHEN_FORK, EV_ENABLE, pps);
    488 				return;
    489 			}
    490 		}
    491 	}
    492 
    493 	return;
    494 }
    495 
    496 /*
    497  * ev_slowf() - CHECK AND EXEC SLOW FILTER
    498  */
    499 
    500 static int
    501 ev_slowf(RSTATUS *prs)
    502 {
    503 	register EXEC		*ep;
    504 
    505 	syslog(LOG_DEBUG, "ev_slowf(%s)",
    506 	       (prs && prs->req_file ? prs->req_file : "NULL"));
    507 
    508 	/*
    509 	 * Return -1 if no more can be executed (no more exec slots)
    510 	 * or if it's unwise to execute any more (fork failed).
    511 	 */
    512 
    513 	if (!(ep = find_exec_slot(Exec_Slow))) {
    514 		syslog(LOG_DEBUG, "ev_slowf(%s): no slot",
    515 	       		(prs && prs->req_file ? prs->req_file : "NULL"));
    516 		return (-1);
    517 	}
    518 
    519 	if (!(prs->request->outcome & (RS_DONE|RS_HELD|RS_ACTIVE)) &&
    520 	    NEEDS_FILTERING(prs)) {
    521 		(prs->exec = ep)->ex.request = prs;
    522 		if (exec(EX_SLOWF, prs) != 0) {
    523 			ep->ex.request = 0;
    524 			prs->exec = 0;
    525 			if (errno == EAGAIN) {
    526 				schedule (EV_LATER, WHEN_FORK, EV_SLOWF, prs);
    527 				return (-1);
    528 			}
    529 		}
    530 	}
    531 	return (0);
    532 }
    533 
    534 /*
    535  * ev_notify() - CHECK AND EXEC NOTIFICATION
    536  */
    537 
    538 static int
    539 ev_notify(RSTATUS *prs)
    540 {
    541 	register EXEC		*ep;
    542 
    543 	syslog(LOG_DEBUG, "ev_notify(%s)",
    544 	       (prs && prs->req_file ? prs->req_file : "NULL"));
    545 
    546 	/*
    547 	 * Return -1 if no more can be executed (no more exec slots)
    548 	 * or if it's unwise to execute any more (fork failed, already
    549 	 * sent one to remote side).
    550 	 */
    551 
    552 	/*
    553 	 * If the job came from a remote machine, we forward the
    554 	 * outcome of the request to the network manager for sending
    555 	 * to the remote side.
    556 	 */
    557 	if (prs->request->actions & ACT_NOTIFY) {
    558 		if (prs->request->outcome & RS_NOTIFY) {
    559 			prs->request->actions &= ~ACT_NOTIFY;
    560 			return (0);  /* but try another request */
    561 		}
    562 	/*
    563 	 * If the job didn't come from a remote system,
    564 	 * we'll try to start a process to send the notification
    565 	 * to the user. But we only allow so many notifications
    566 	 * to run at the same time, so we may not be able to
    567 	 * do it.
    568 	 */
    569 	} else if (!(ep = find_exec_slot(Exec_Notify)))
    570 		return (-1);
    571 
    572 	else if (prs->request->outcome & RS_NOTIFY &&
    573 		!(prs->request->outcome & RS_NOTIFYING)) {
    574 
    575 		(prs->exec = ep)->ex.request = prs;
    576 		if (exec(EX_NOTIFY, prs) != 0) {
    577 			ep->ex.request = 0;
    578 			prs->exec = 0;
    579 			if (errno == EAGAIN) {
    580 				schedule (EV_LATER, WHEN_FORK, EV_NOTIFY, prs);
    581 				return (-1);
    582 			}
    583 		}
    584 	}
    585 	return (0);
    586 }
    587 
    588 
    589 /*
    590  * find_exec_slot() - FIND AVAILABLE EXEC SLOT
    591  */
    592 
    593 static EXEC *
    594 find_exec_slot(EXEC **exec_table)
    595 {
    596 	int i;
    597 
    598 	for (i = 0; exec_table[i] != NULL; i++)
    599 		if (exec_table[i]->pid == 0)
    600 			return (exec_table[i]);
    601 
    602 	syslog(LOG_DEBUG, "find_exec_slot(0x%8.8x): after %d, no slots",
    603 			exec_table, i);
    604 	return (0);
    605 }
    606