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
48static int		NumEvents = 0;
49static int		NumCons = 0;
50static int		ConsSize= 0;
51static int		NumNewCons = 0;
52static MESG **		Connections = NULL;
53static struct pollfd *	PollFdList = NULL;
54
55int
56mlisteninit(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
86int
87mlistenadd(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
137MESG *
138mlistenreset ( 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
164MESG *
165mlisten()
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
527int
528mon_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