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 "lpsched.h"
33 #include <syslog.h>
34 
35 static int	max_requests_needing_form_mounted ( FSTATUS * );
36 static int	max_requests_needing_pwheel_mounted ( char * );
37 
38 /**
39  ** queue_form() - ADD A REQUEST TO A FORM QUEUE
40  **/
41 
42 void
43 queue_form(RSTATUS *prs, FSTATUS *pfs)
44 {
45 	if ((prs->form = pfs) != NULL) {
46 		prs->form->requests++;
47 		if (prs->printer)
48 			check_form_alert (prs->form, (_FORM *)0);
49 	}
50 	return;
51 }
52 
53 /**
54  ** unqueue_form() - REMOVE A REQUEST FROM A FORM QUEUE
55  **/
56 
57 void
58 unqueue_form(RSTATUS *prs)
59 {
60 	FSTATUS *		pfs	= prs->form;
61 
62 	prs->form = 0;
63 	if (pfs) {
64 		pfs->requests--;
65 		if (prs->printer)
66 			check_form_alert (pfs, (_FORM *)0);
67 	}
68 	return;
69 }
70 
71 /**
72  ** queue_pwheel() - ADD A REQUEST TO A PRINT WHEEL QUEUE
73  **/
74 
75 void
76 queue_pwheel(RSTATUS *prs, char *name)
77 {
78 	if (name) {
79 		prs->pwheel_name = Strdup(name);
80 		/*
81 		 * Don't bother queueing the request for
82 		 * a print wheel if this request is destined for
83 		 * only this printer and the printer doesn't take
84 		 * print wheels.
85 		 */
86 		if (
87 			!one_printer_with_charsets(prs)
88 		     && (prs->pwheel = search_pwstatus(name))
89 		) {
90 			prs->pwheel->requests++;
91 			check_pwheel_alert (prs->pwheel, (PWHEEL *)0);
92 		}
93 	}
94 	return;
95 }
96 
97 /**
98  ** unqueue_pwheel() - REMOVE A REQUEST FROM A PRINT WHEEL QUEUE
99  **/
100 
101 void
102 unqueue_pwheel(RSTATUS *prs)
103 {
104 	PWSTATUS *		ppws	= prs->pwheel;
105 
106 	prs->pwheel = 0;
107 	unload_str (&(prs->pwheel_name));
108 	if (ppws) {
109 		ppws->requests--;
110 		check_pwheel_alert (ppws, (PWHEEL *)0);
111 	}
112 	return;
113 }
114 
115 /**
116  ** check_form_alert() - CHECK CHANGES TO MOUNT FORM ALERT
117  **/
118 
119 void
120 check_form_alert(FSTATUS *pfs, _FORM *pf)
121 {
122 	short			trigger,
123 				fire_off_alert	= 0;
124 
125 	int			requests_waiting;
126 
127 
128 	/*
129 	 * Call this routine whenever a requests has been queued
130 	 * or dequeued for a form, and whenever the form changes.
131 	 * If a pointer to a new _FORM is passed, the FSTATUS
132 	 * structure is updated with the changes. Use a second
133 	 * argument of 0 if no change.
134 	 *
135 	 * WARNING: It is valid to call this routine when adding
136 	 * a NEW form (not just changing it). Thus the members of
137 	 * the structure "pfs->form" may not be set.
138 	 * In this case, though, "pf" MUST be set, and there can
139 	 * be NO alert active.
140 	 */
141 
142 	syslog(LOG_DEBUG, "check_form_alert:\n");
143 	if (pfs)
144 		syslog(LOG_DEBUG, "check_form_alert: pfs->name <%s>\n",
145 			(pfs->form->name != NULL) ? pfs->form->name : "null");
146 	if (pf)
147 		syslog(LOG_DEBUG, "check_form_alert: pf->name <%s>\n",
148 			(pf->name != NULL) ? pf->name : "null");
149 
150 
151 	if (pf) {
152 		if ((trigger = pf->alert.Q) <= 0)
153 			trigger = 1;
154 	} else
155 		trigger = pfs->trigger;
156 
157 	if (Starting)
158 		goto Return;
159 
160 #define	OALERT	pfs->form->alert
161 #define NALERT	pf->alert
162 
163 	requests_waiting = max_requests_needing_form_mounted(pfs);
164 
165 	/*
166 	 * Cancel an active alert if the number of requests queued
167 	 * has dropped below the threshold (or the threshold has been
168 	 * raised), or if the alert command or period has changed.
169 	 * In the latter case we'll reactive the alert later.
170 	 */
171 	if (pfs->alert->active)
172 		if (!requests_waiting || requests_waiting < trigger)
173 			cancel_alert (A_FORM, pfs);
174 
175 		else if (
176 			pf
177 		     && (
178 				!SAME(NALERT.shcmd, OALERT.shcmd)
179 			     || NALERT.W != OALERT.W
180 			     || NALERT.Q != OALERT.Q
181 			)
182 		)
183 			cancel_alert (A_FORM, pfs);
184 
185 	/*
186 	 * If we still have the condition for an alert, we'll fire
187 	 * one off. It is possible the alert is still running, but
188 	 * that's okay. First, we may want to change the alert message;
189 	 * second, the "alert()" routine doesn't execute an alert
190 	 * if it is already running.
191 	 */
192 	if (trigger > 0 && requests_waiting >= trigger)
193 		if ((pf && NALERT.shcmd) || OALERT.shcmd)
194 			fire_off_alert = 1;
195 
196 #undef	OALERT
197 #undef	NALERT
198 
199 Return:	if (pf) {
200 
201 		 pfs->form = pf;
202 
203 		pfs->trigger = trigger;
204 	}
205 
206 	/*
207 	 * Have to do this after updating the changes.
208 	 */
209 	if (fire_off_alert)
210 		alert (A_FORM, pfs);
211 
212 	return;
213 }
214 
215 /**
216  ** check_pwheel_alert() - CHECK CHANGES TO MOUNT PRINTWHEEL ALERT
217  **/
218 
219 void
220 check_pwheel_alert(PWSTATUS *ppws, PWHEEL *ppw)
221 {
222 	short			trigger,
223 				fire_off_alert	= 0;
224 	int			requests_waiting;
225 
226 
227 	/*
228 	 * Call this routine whenever a request has been queued
229 	 * or dequeued for a print-wheel, and whenever the print-wheel
230 	 * changes. If a pointer to a new PWHEEL is passed, the
231 	 * PWSTATUS structure is updated with the changes. Use a
232 	 * second argument of 0 if no change.
233 	 *
234 	 * WARNING: It is valid to call this routine when adding
235 	 * a NEW print wheel (not just changing it). Thus the members
236 	 * of the structure "ppws->pwheel" may not be set.
237 	 * In this case, though, "ppw" MUST be set, and there can
238 	 * be NO alert active.
239 	 */
240 
241 	if (ppw) {
242 		if ((trigger = ppw->alert.Q) <= 0)
243 			trigger = 1;
244 	} else
245 		trigger = ppws->trigger;
246 
247 	if (Starting)
248 		goto Return;
249 
250 #define	OALERT	ppws->pwheel->alert
251 #define NALERT	ppw->alert
252 
253 	requests_waiting = max_requests_needing_pwheel_mounted(ppws->pwheel->name);
254 
255 	/*
256 	 * Cancel an active alert if the number of requests queued
257 	 * has dropped below the threshold (or the threshold has been
258 	 * raised), or if the alert command or period has changed.
259 	 * In the latter case we'll reactive the alert later.
260 	 */
261 	if (ppws->alert->active)
262 		if (!requests_waiting || requests_waiting < trigger)
263 			cancel_alert (A_PWHEEL, ppws);
264 
265 		else if (
266 			ppw
267 		     && (
268 				!SAME(NALERT.shcmd, OALERT.shcmd)
269 			     || NALERT.W != OALERT.W
270 			     || NALERT.Q != OALERT.Q
271 			)
272 		)
273 			cancel_alert (A_PWHEEL, ppws);
274 
275 	/*
276 	 * If we still have the condition for an alert, we'll fire
277 	 * one off. It is possible the alert is still running, but
278 	 * that's okay. First, we may want to change the alert message;
279 	 * second, the "alert()" routine doesn't execute an alert
280 	 * if it is already running.
281 	 */
282 	if (trigger > 0 && requests_waiting >= trigger)
283 		if ((ppw && NALERT.shcmd) || OALERT.shcmd)
284 			fire_off_alert = 1;
285 
286 #undef	OALERT
287 #undef	NALERT
288 
289 Return:	if (ppw) {
290 
291 		ppws->pwheel = ppw;
292 		ppws->trigger = trigger;
293 	}
294 
295 	/*
296 	 * Have to do this after updating the changes.
297 	 */
298 	if (fire_off_alert)
299 		alert (A_PWHEEL, ppws);
300 
301 	return;
302 }
303 
304 static int
305 trayWithForm(PSTATUS *pps, FSTATUS *pfs, int startingTray, int checkAvail)
306 {
307 	int i;
308 	PFSTATUS *ppfs;
309 
310 	ppfs = pps->forms;
311 	if (startingTray < 0)
312 		startingTray = 0;
313 
314 	if (ppfs) {
315 		for (i = startingTray; i < pps->numForms; i++)
316 			if ((!checkAvail || ppfs[i].isAvailable) &&
317 			    (ppfs[i].form == pfs))
318 					return(i);
319 	}
320 	else if (!pfs)
321 		/* no form request matches no form mounted */
322 		return(0);
323 
324 	return(-1);
325 }
326 
327 char *
328 allTraysWithForm(PSTATUS *pps, FSTATUS *pfs)
329 {
330 
331 	int tray = 0;
332 	char *ptr, *p;
333 	char trayList[MAX_INPUT];
334 	int n;
335 
336 	ptr = trayList;
337 	if (pfs && pfs->form && pfs->form->paper)
338 		p = pfs->form->paper;
339 	else
340 		p = "";
341 
342 	n = sizeof (trayList);
343 	snprintf(ptr, n, "LP_TRAY_ARG=%s:", p);
344 
345 	ptr += strlen(ptr);
346 	n -= strlen(ptr);
347 
348 	while ((tray = trayWithForm(pps, pfs, tray, 1)) > 0) {
349 		tray++;
350 		snprintf(ptr, n, "%d,", tray);
351 		ptr += strlen(ptr);
352 		n -= strlen(ptr);
353 	}
354 	if (*(ptr-1) == ',')
355 		*(ptr-1) = 0;
356 
357 	putenv(trayList);
358 	return(NULL);
359 }
360 
361 int
362 isFormUsableOnPrinter(PSTATUS *pps, FSTATUS *pfs)
363 {
364 	return (trayWithForm(pps,pfs,0,1) >= 0 );
365 }
366 int
367 isFormMountedOnPrinter(PSTATUS *pps, FSTATUS *pfs)
368 {
369 	return (trayWithForm(pps,pfs,0,0) >= 0 );
370 }
371 
372 /**
373  ** max_requests_needing_form_mounted()
374  ** max_requests_needing_pwheel_mounted()
375  **/
376 
377 static int
378 max_requests_needing_form_mounted(FSTATUS *pfs)
379 {
380 	PSTATUS *		pps;
381 	RSTATUS *		prs;
382 	int			max	= 0;
383 	int			i;
384 
385 	/*
386 	 * For each printer that doesn't have this form mounted,
387 	 * count the number of requests needing this form and
388 	 * assigned to the printer. Find the maximum across all such
389 	 * printers. Sorry, the code actually has a different loop
390 	 * (it steps through the requests) but the description of what
391 	 * happens below is easier to understand as given. (Looping
392 	 * through the printers would result in #printers x #requests
393 	 * steps, whereas this entails #requests steps.)
394 	 */
395 	for (i = 0; PStatus != NULL && PStatus[i] != NULL; i++)
396 		PStatus[i]->nrequests = 0;
397 
398 	for (prs = Request_List; prs != NULL; prs = prs->next)
399 		if ((prs->form == pfs) && ((pps = prs->printer) != NULL) &&
400 	    	    (!isFormMountedOnPrinter(pps,pfs)) &&
401 		    (++pps->nrequests >= max))
402 			max = pps->nrequests;
403 
404 	if (NewRequest)
405 		if (((pps = NewRequest->printer) != NULL) &&
406 		    (!isFormMountedOnPrinter(pps,pfs)))
407 			if (++pps->nrequests >= max)
408 				max = pps->nrequests;
409 	return (max);
410 }
411 
412 static int
413 max_requests_needing_pwheel_mounted(char *pwheel_name)
414 {
415 	PSTATUS *		pps;
416 	RSTATUS *		prs;
417 	int			max	= 0;
418 	int			i;
419 
420 
421 	/*
422 	 * For each printer that doesn't have this print-wheel mounted,
423 	 * count the number of requests needing this print-wheel and
424 	 * assigned to the printer. Find the maximum across all such
425 	 * printers. Sorry, the code actually has a different loop
426 	 * (it steps through the requests) but the description of what
427 	 * happens below is easier to understand as given. (Looping
428 	 * through the printers would result in #printers x #requests
429 	 * steps, whereas this entails #requests steps.)
430 	 */
431 	for (i = 0; PStatus != NULL && PStatus[i] != NULL; i++)
432 		PStatus[i]->nrequests = 0;
433 
434 	for (prs = Request_List; prs != NULL; prs = prs->next)
435 		if ((prs->pwheel_name != NULL) &&
436 		    (STREQU(prs->pwheel_name, pwheel_name)) &&
437 		    ((pps = prs->printer) != NULL) && pps->printer->daisy &&
438 		    (!SAME(pps->pwheel_name, pwheel_name)))
439 			if (++pps->nrequests >= max)
440 				max = pps->nrequests;
441 
442 	if (NewRequest)
443 		if (
444 			((pps = NewRequest->printer) != NULL)
445 		     && pps->printer->daisy
446 		     && !SAME(pps->pwheel_name, pwheel_name)
447 		)
448 			if (++pps->nrequests >= max)
449 				max = pps->nrequests;
450 	return (max);
451 }
452 
453 /**
454  ** one_printer_with_charsets()
455  **/
456 
457 int
458 one_printer_with_charsets(RSTATUS *prs)
459 {
460 	/*
461 	 * This little function answers the question: Is a request
462 	 * that needs a character set destined for a particular
463 	 * printer that has selectable character sets instead of
464 	 * mountable print wheels?
465 	 */
466 	return (
467 	    STREQU(prs->request->destination, prs->printer->printer->name)
468 	 && !prs->printer->printer->daisy
469 	);
470 }
471