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