xref: /illumos-gate/usr/src/cmd/lp/lib/msgs/mlisten.c (revision 4c87aefe8930bd07275b8dd2e96ea5f24d93a52e)
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  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.9	*/
31 # include	<unistd.h>
32 # include	<stdlib.h>
33 # include	<string.h>
34 # include	<poll.h>
35 # include	<stropts.h>
36 # include	<fcntl.h>
37 # include	<errno.h>
38 #include	<syslog.h>
39 #include <user_attr.h>
40 #include <secdb.h>
41 #include <pwd.h>
42 
43 # include	"lp.h"
44 # include	"msgs.h"
45 
46 #define TURN_ON(X,F)	(void)Fcntl(X, F_SETFL, (Fcntl(X, F_GETFL, 0)|(F)))
47 
48 static int		NumEvents = 0;
49 static int		NumCons = 0;
50 static int		ConsSize= 0;
51 static int		NumNewCons = 0;
52 static MESG **		Connections = NULL;
53 static struct pollfd *	PollFdList = NULL;
54 
55 int
56 mlisteninit(MESG * md)
57 {
58     if (md == NULL)
59     {
60 	errno = EINVAL;
61 	return(-1);
62     }
63 
64     if (ConsSize > 0)
65     {
66 	errno = EBUSY;
67 	return(-1);
68     }
69 
70     ConsSize = 20;
71     Connections = (MESG **) Malloc(ConsSize * MDSIZE);
72     PollFdList = (struct pollfd*) Malloc(ConsSize * sizeof(struct pollfd));
73     if (Connections == NULL || PollFdList == NULL)
74     {
75 	errno = ENOMEM;
76 	return(-1);
77     }
78     Connections[0] = md;
79     PollFdList[0].fd = md->readfd;
80     PollFdList[0].events = POLLIN;
81     PollFdList[0].revents = 0;
82     NumCons = 1;
83     return(0);
84 }
85 
86 int
87 mlistenadd(MESG * md, short events)
88 {
89     int			slack;
90     struct pollfd *	fdp;
91 
92     /*
93     **	See if we have room in the connection table.
94     **	Realloc(3) the table if the number of connections
95     **	changes by more than 20.
96     */
97 
98     slack = ConsSize - (NumCons + NumNewCons + 1);
99 
100     if (slack < 0)
101     {
102 	ConsSize += 20;
103 	Connections = (MESG **) Realloc(Connections, ConsSize * MDSIZE);
104 	PollFdList = (struct pollfd*) Realloc(PollFdList, ConsSize * sizeof(struct pollfd));
105 	if (Connections == NULL || PollFdList == NULL)
106 	{
107 	    errno = ENOMEM;
108 	    return(-1);
109 	}
110     }
111 
112     if (slack > 20)
113     {
114 	ConsSize -= 20;
115 	Connections = (MESG **) Realloc(Connections, ConsSize * MDSIZE);
116 	PollFdList = (struct pollfd*) Realloc(PollFdList, ConsSize * sizeof(struct pollfd));
117 	if (Connections == NULL || PollFdList == NULL)
118 	{
119 	    errno = ENOMEM;
120 	    return(-1);
121 	}
122     }
123 
124     fdp = PollFdList + (NumCons + NumNewCons);
125     fdp->fd = md->readfd;
126     fdp->events = events;
127     fdp->revents = 0;
128 
129     /*
130     **	Now add the entry to the connection table
131     **	NumCons will be updated above.
132     */
133     Connections[NumCons + NumNewCons++] = md;
134     return(0);
135 }
136 
137 MESG *
138 mlistenreset ( void )	/* funcdef */
139 {
140     int		x;
141     MESG *	md;
142 
143     if (ConsSize == 0)
144 	return(NULL);
145 
146     ConsSize = 0;
147 
148     for (x = 1; x < NumCons; x++)
149 	(void) mdisconnect(Connections[x]);
150 
151     md = Connections[0];
152 
153     Free(Connections);
154     Free(PollFdList);
155 
156     Connections = NULL;
157     PollFdList = NULL;
158     NumCons = 0;
159     NumNewCons = 0;
160     NumEvents = 0;
161     return(md);
162 }
163 
164 MESG *
165 mlisten()
166 {
167     extern uid_t	Lp_Uid;
168 
169     MESG *		mdp;
170     MESG *		md;
171     MQUE *		p;
172     int			flag = 0;
173     int			disconacts;
174     int			x;
175     int			y;
176     struct pollfd *	fdp;
177     struct strrecvfd	recbuf;
178 #if defined(NOCONNLD)
179     struct strbuf	ctl;
180     char		cbuff[MSGMAX];
181 #endif
182 
183 #if defined(NOCONNLD)
184     /*
185     **	Set up buffer for receiving messages.
186     */
187     ctl.buf = cbuff;
188     ctl.maxlen = sizeof (cbuff);
189 #endif
190 
191     /*
192     **	This loop exists to return control to poll after the
193     **	result of poll yeilds no new connections or serviceable
194     **	messages.
195     */
196     for (;;)
197     {
198 	/*
199 	**	If there are no unserviced events pending, call poll(2)
200 	**	and wait for a message or connection.
201 	**	NumEvents may be -1 in the event of an interrupt, hence
202 	**	<= 0
203 	*/
204 	if (NumEvents <= 0)
205 	{
206 	    /*
207 	    **	Add new connections, if any, reset connection counter
208 	    */
209 	    NumCons += NumNewCons;
210 	    NumNewCons = 0;
211 
212 	    if (NumCons <= 0)
213 	    {
214 		errno = EINTR;
215 		return(NULL);
216 	    }
217 
218 	    /*
219 	    **	Scan the connection table and remove any holes
220 	    */
221 	    for (x = 0; x < NumCons; x++)
222 	    {
223 		mdp = Connections[x];
224 
225 		/*
226 		**	Disconnected, clear the node and compress the
227 		**	tables.  If the disconnect called any
228 		**	on_discon functions (disconacts > 0), return
229 		**	because there may be something to clean up.
230 		**	Finally, decrement <x> so that the next node
231 		**	doesn't get missed.
232 		*/
233 		if (mdp->readfd == -1)
234 		{
235 		    disconacts = mdisconnect(mdp);
236 		    NumCons--;
237 		    for (y = x; y < (NumCons + NumNewCons); y++)
238 		    {
239 			Connections[y] = Connections[y + 1];
240 			PollFdList[y] = PollFdList[y + 1];
241 		    }
242 		    if (disconacts > 0)
243 		    {
244 			errno = EINTR;
245 			return(NULL);
246 		    }
247 		    else
248 			x--;
249 		} else {
250 		    /*
251 		     * We are in "mlisten", POLLIN is always set.  We'll look
252 		     * at POLLOUT possibility when mque is non-NULL.
253 		     */
254 		    PollFdList[x].events = POLLIN;
255 		    if (mdp->mque)
256 			PollFdList[x].events |= POLLOUT;
257 		}
258 	    }
259 
260 	    /*
261 	    **	Wait for a message or a connection.
262 	    **	This call may be interrupted by alarms used
263 	    **	elsewhere, so if poll fails, return NULL and
264 	    **	set errno to EAGAIN.
265 	    */
266 	    if ((NumEvents = poll(PollFdList, NumCons, -1)) < 0)
267 	    {
268 		errno = EAGAIN;
269 		return(NULL);
270 	    }
271 	}
272 
273 	for (x = 0; x < NumCons; x++)
274 	{
275 	    mdp = Connections[x];
276 	    fdp = PollFdList + x;
277 
278 	    if (fdp->revents == 0)
279 		continue;
280 
281 	    switch (mdp->type) {
282 	    case MD_MASTER:
283 		/*
284 		**	Only valid revent is: POLLIN
285 		*/
286 		if (fdp->revents != POLLIN)
287 		{
288 		    errno = EINVAL;
289 		    return(NULL);
290 		}
291 
292 		/*
293 		**	Retrieve the file descriptor
294 		*/
295 		if (ioctl(mdp->readfd, I_RECVFD, &recbuf) != 0)
296 		{
297 		    if (errno == EINTR)
298 		    {
299 			errno = EAGAIN;
300 			return(NULL);
301 		    }
302 		    if (errno == ENXIO)
303 		    {
304 			fdp->revents = 0;
305 			NumEvents--;
306 			continue;
307 		    }
308 #if defined(NOCONNLD)
309 		    if (errno == EBADMSG)
310 			while (Getmsg(mdp, &ctl, &ctl, &flag) >= 0);
311 #endif
312 		    return(NULL);
313 		}
314 
315 		TURN_ON(recbuf.fd, O_NDELAY);
316 		/*
317 		**	Now, create the message descriptor
318 		**	and populate it with what we know.
319 		*/
320 		if ((md = (MESG *)Malloc(MDSIZE)) == NULL)
321 		{
322 		    errno = ENOMEM;
323 		    return(NULL);
324 		}
325 
326 		memset(md, 0, sizeof (MESG));
327 		md->gid = recbuf.gid;
328 		md->readfd = md->writefd = recbuf.fd;
329 		md->state = MDS_IDLE;
330 		md->type = MD_UNKNOWN;
331 		md->uid = recbuf.uid;
332 
333 		/*
334 		 * Determine if a print administrator is contacting lpsched.
335 		 * currently, root, lp and users with the "solaris.print.admin"
336 		 * privilege are print administrators
337 		 */
338 		md->admin = (md->uid == 0 || md->uid == Lp_Uid);
339 		if (md->admin == 0) {
340 			struct passwd *pw = NULL;
341 
342 			if ((pw = getpwuid(md->uid)) != NULL)
343 				md->admin = chkauthattr("solaris.print.admin",
344 							pw->pw_name);
345 		}
346 
347 		get_peer_label(md->readfd, &md->slabel);
348 
349 		if (mlistenadd(md, POLLIN) != 0)
350 		    return(NULL);
351 
352 		ResetFifoBuffer (md->readfd);
353 		/*
354 		**	Reset fdp because mlistenadd may have
355 		**	realloc()ed PollFdList and changed its
356 		**	physical location.
357 		*/
358 		fdp = PollFdList + x;
359 
360 		/*
361 		**	Clear the event that brought us here,
362 		**	decrement the event counter, and get the
363 		**	next event.
364 		*/
365 		fdp->revents = 0;
366 		NumEvents--;
367 		break;
368 
369 	    case MD_CHILD:
370 		/*
371 		**	If this connection is a child process, just
372 		**	save the event and return the message descriptor
373 		*/
374 
375 		if (fdp->revents & POLLOUT) {
376 			if (mdp->mque) {
377 				if (mflush(mdp) < 0) {
378 					syslog(LOG_DEBUG,
379 						"MD_CHILD mflush failed");
380 				}
381 			}
382 		}
383 
384 		if (fdp->revents & POLLIN) {
385 			mdp->event = fdp->revents;
386 			NumEvents--;
387 			fdp->revents = 0;
388 			return (mdp);		/* we are in listening mode */
389 		}
390 
391 		NumEvents--;
392 		fdp->revents = 0;
393 		break;
394 
395 	    default:
396 		    /*
397 		    **	POLLNVAL means this client disconnected and
398 		    **	all messages have been processed.
399 		    */
400 		    if (fdp->revents & POLLNVAL) /* disconnected & no msg */
401 		    {
402 			if (mdp->readfd >= 0) {
403 				Close (mdp->readfd);
404 				if (mdp->writefd == mdp->readfd)
405 					mdp->writefd = -1;
406 				mdp->readfd = -1;
407 			}
408 			fdp->revents = 0;
409 			NumEvents--;
410 			continue;
411 		    }
412 
413 		    /*
414 		    **	POLLERR means an error message is on the
415 		    **	stream.  Since this is totally unexpected,
416 		    **	the assumption is made that this stream will
417 		    **	be flagged POLLNVAL next time through poll
418 		    **	and will be removed at that time.
419 		    */
420 		    if (fdp->revents & POLLERR)	/* uh, oh! */
421 		    {
422 			if (mdp->readfd >= 0) {
423 				Close (mdp->readfd);
424 				if (mdp->writefd == mdp->readfd)
425 					mdp->writefd = -1;
426 				mdp->readfd = -1;
427 			}
428 			NumEvents--;
429 			fdp->revents = 0;
430 			continue;
431 		    }
432 
433 
434 		    /*
435 		    **	POLLHUP means the client aborted the call.
436 		    **	The client is not removed, because there may
437 		    **	still be messages on the stream.
438 		    */
439 		    if (fdp->revents & POLLHUP)	/* disconnected */
440 		    {
441 			NumEvents--;
442 			fdp->revents = 0;
443 			/*
444 			 * MORE: This is odd. Why are we closing the
445 			 * stream if there ``may still be messages''???
446 			 */
447 			if (mdp->readfd >= 0) {
448 				Close (mdp->readfd);
449 				if (mdp->writefd == mdp->readfd)
450 					mdp->writefd = -1;
451 				mdp->readfd = -1;
452 			}
453 			continue;
454 
455 			/*
456 			 * MORE: Why is this here??
457 			 *
458 			if (mdp->type == MD_SYS_FIFO)
459 			    (void) Close(mdp->writefd);
460 
461 			mdp->writefd = -1;
462 
463 			if (fdp->revents == POLLHUP)
464 			{
465 			    NumEvents--;
466 			    fdp->revents = 0;
467 			    (void) Close(mdp->readfd);
468 			    mdp->readfd = -1;
469 			    continue;
470 			}
471 			 *
472 			 */
473 		    }
474 		    /*
475 		    **	POLLOUT means that the client had a full
476 		    **	stream and messages became backlogged and
477 		    **	now the stream is empty.  So the queued msgs
478 		    **	are sent with putmsg(2)
479 		    */
480 		    if (fdp->revents & POLLOUT)
481 		    {
482 			if (mdp->mque == NULL)
483 			{
484 			    NumEvents--;
485 			    fdp->revents = 0;
486 			    continue;
487 			}
488 			while (mdp->mque) {
489 			    if (Putmsg(mdp, NULL, mdp->mque->dat, 0))
490 				break;	/* failed for some reason */
491 			    p = mdp->mque;
492 			    mdp->mque = p->next;
493 			    Free(p->dat->buf);
494 			    Free(p->dat);
495 			    Free(p);
496 			}
497 			NumEvents--;
498 			fdp->revents = 0;
499 			continue;
500 		    }
501 
502 		    /*
503 		    **	POLLIN means that there is a message on the
504 		    **	stream.
505 		    **	Return the message descriptor to the caller
506 		    **	so that the message may be received and
507 		    **	processed.
508 		    */
509 		    if (fdp->revents & POLLIN)	/* got a message */
510 		    {
511 			NumEvents--;
512 			mdp->event = fdp->revents;
513 			fdp->revents = 0;
514 			if (mdp->type == MD_UNKNOWN)
515 			    mdp->type = MD_STREAM;
516 			return(mdp);
517 		    }
518 		    break;
519 	    }
520 	}
521     }
522 }
523 
524 # define	VOID_FUNC_PTR		void (*)()
525 # define	PTR_TO_VOID_FUNC_PTR	void (**)()
526 
527 int
528 mon_discon(MESG * md, void (*fn)())
529 {
530     int		size = 2;
531     void	(**fnp) ();
532 
533     if (md->on_discon)
534     {
535 	for (fnp = md->on_discon; *fnp; fnp++)
536 	    size++;
537 	if ((md->on_discon = (PTR_TO_VOID_FUNC_PTR) Realloc (md->on_discon, size * sizeof(VOID_FUNC_PTR))) == NULL)
538 	{
539 	    errno = ENOMEM;
540 	    return(-1);
541 	}
542     }
543     else
544 	if ((md->on_discon = (PTR_TO_VOID_FUNC_PTR) Malloc (size * sizeof(VOID_FUNC_PTR))) == NULL)
545 	{
546 	    errno = ENOMEM;
547 	    return(-1);
548 	}
549 
550     size--;
551     md->on_discon[size] = NULL;
552     size--;
553     md->on_discon[size] = fn;
554     return(0);
555 }
556