view usr/src/cmd/lp/lib/msgs/mlisten.c @ 4:1a15d5aaf794

synchronized with onnv_86 (6202) in onnv-gate
author Koji Uno <koji.uno@sun.com>
date Mon, 31 Aug 2009 14:38:03 +0900
parents c9caec207d52
children
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/


#pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.9	*/
# include	<unistd.h>
# include	<stdlib.h>
# include	<string.h>
# include	<poll.h>
# include	<stropts.h>
# include	<fcntl.h>
# include	<errno.h>
#include	<syslog.h>
#include <user_attr.h>
#include <secdb.h>
#include <pwd.h>

# include	"lp.h"
# include	"msgs.h"

#define TURN_ON(X,F)	(void)Fcntl(X, F_SETFL, (Fcntl(X, F_GETFL, 0)|(F)))

static int		NumEvents = 0;
static int		NumCons = 0;
static int		ConsSize= 0;
static int		NumNewCons = 0;
static MESG **		Connections = NULL;
static struct pollfd *	PollFdList = NULL;

int
mlisteninit(MESG * md)
{
    if (md == NULL)
    {
	errno = EINVAL;
	return(-1);
    }

    if (ConsSize > 0)
    {
	errno = EBUSY;
	return(-1);
    }

    ConsSize = 20;
    Connections = (MESG **) Malloc(ConsSize * MDSIZE);
    PollFdList = (struct pollfd*) Malloc(ConsSize * sizeof(struct pollfd));
    if (Connections == NULL || PollFdList == NULL)
    {
	errno = ENOMEM;
	return(-1);
    }
    Connections[0] = md;
    PollFdList[0].fd = md->readfd;
    PollFdList[0].events = POLLIN;
    PollFdList[0].revents = 0;
    NumCons = 1;
    return(0);
}

int
mlistenadd(MESG * md, short events)
{
    int			slack;
    struct pollfd *	fdp;

    /*
    **	See if we have room in the connection table.
    **	Realloc(3) the table if the number of connections
    **	changes by more than 20.
    */

    slack = ConsSize - (NumCons + NumNewCons + 1);

    if (slack < 0)
    {
	ConsSize += 20;
	Connections = (MESG **) Realloc(Connections, ConsSize * MDSIZE);
	PollFdList = (struct pollfd*) Realloc(PollFdList, ConsSize * sizeof(struct pollfd));
	if (Connections == NULL || PollFdList == NULL)
	{
	    errno = ENOMEM;
	    return(-1);
	}
    }

    if (slack > 20)
    {
	ConsSize -= 20;
	Connections = (MESG **) Realloc(Connections, ConsSize * MDSIZE);
	PollFdList = (struct pollfd*) Realloc(PollFdList, ConsSize * sizeof(struct pollfd));
	if (Connections == NULL || PollFdList == NULL)
	{
	    errno = ENOMEM;
	    return(-1);
	}
    }

    fdp = PollFdList + (NumCons + NumNewCons);
    fdp->fd = md->readfd;
    fdp->events = events;
    fdp->revents = 0;

    /*
    **	Now add the entry to the connection table
    **	NumCons will be updated above.
    */
    Connections[NumCons + NumNewCons++] = md;
    return(0);
}

MESG *
mlistenreset ( void )	/* funcdef */
{
    int		x;
    MESG *	md;

    if (ConsSize == 0)
	return(NULL);

    ConsSize = 0;

    for (x = 1; x < NumCons; x++)
	(void) mdisconnect(Connections[x]);

    md = Connections[0];

    Free(Connections);
    Free(PollFdList);

    Connections = NULL;
    PollFdList = NULL;
    NumCons = 0;
    NumNewCons = 0;
    NumEvents = 0;
    return(md);
}

MESG *
mlisten()
{
    extern uid_t	Lp_Uid;

    MESG *		mdp;
    MESG *		md;
    MQUE *		p;
    int			flag = 0;
    int			disconacts;
    int			x;
    int			y;
    struct pollfd *	fdp;
    struct strrecvfd	recbuf;
#if defined(NOCONNLD)
    struct strbuf	ctl;
    char		cbuff[MSGMAX];
#endif

#if defined(NOCONNLD)
    /*
    **	Set up buffer for receiving messages.
    */
    ctl.buf = cbuff;
    ctl.maxlen = sizeof (cbuff);
#endif

    /*
    **	This loop exists to return control to poll after the
    **	result of poll yeilds no new connections or serviceable
    **	messages.
    */
    for (;;)
    {
	/*
	**	If there are no unserviced events pending, call poll(2)
	**	and wait for a message or connection.
	**	NumEvents may be -1 in the event of an interrupt, hence
	**	<= 0
	*/
	if (NumEvents <= 0)
	{
	    /*
	    **	Add new connections, if any, reset connection counter
	    */
	    NumCons += NumNewCons;
	    NumNewCons = 0;

	    if (NumCons <= 0)
	    {
		errno = EINTR;
		return(NULL);
	    }
	    
	    /*
	    **	Scan the connection table and remove any holes
	    */
	    for (x = 0; x < NumCons; x++)
	    {
		mdp = Connections[x];

		/*
		**	Disconnected, clear the node and compress the
		**	tables.  If the disconnect called any
		**	on_discon functions (disconacts > 0), return
		**	because there may be something to clean up.
		**	Finally, decrement <x> so that the next node
		**	doesn't get missed.
		*/
		if (mdp->readfd == -1)
		{
		    disconacts = mdisconnect(mdp);
		    NumCons--;
		    for (y = x; y < (NumCons + NumNewCons); y++)
		    {
			Connections[y] = Connections[y + 1];
			PollFdList[y] = PollFdList[y + 1];
		    }
		    if (disconacts > 0)
		    {
			errno = EINTR;
			return(NULL);
		    }
		    else
			x--;
		} else {
		    /*
		     * We are in "mlisten", POLLIN is always set.  We'll look
		     * at POLLOUT possibility when mque is non-NULL.
		     */
		    PollFdList[x].events = POLLIN;
		    if (mdp->mque)
			PollFdList[x].events |= POLLOUT;
		}
	    }

	    /*
	    **	Wait for a message or a connection.
	    **	This call may be interrupted by alarms used
	    **	elsewhere, so if poll fails, return NULL and
	    **	set errno to EAGAIN.
	    */
	    if ((NumEvents = poll(PollFdList, NumCons, -1)) < 0)
	    {
		errno = EAGAIN;
		return(NULL);
	    }
	}

	for (x = 0; x < NumCons; x++)
	{
	    mdp = Connections[x];
	    fdp = PollFdList + x;

	    if (fdp->revents == 0)
		continue;

	    switch (mdp->type) {
	    case MD_MASTER:
		/*
		**	Only valid revent is: POLLIN
		*/
		if (fdp->revents != POLLIN)
		{
		    errno = EINVAL;
		    return(NULL);
		}

		/*
		**	Retrieve the file descriptor
		*/
		if (ioctl(mdp->readfd, I_RECVFD, &recbuf) != 0)
		{
		    if (errno == EINTR)
		    {
			errno = EAGAIN;
			return(NULL);
		    }
		    if (errno == ENXIO)
		    {
			fdp->revents = 0;
			NumEvents--;
			continue;
		    }
#if defined(NOCONNLD)
		    if (errno == EBADMSG)
			while (Getmsg(mdp, &ctl, &ctl, &flag) >= 0);
#endif
		    return(NULL);
		}

		TURN_ON(recbuf.fd, O_NDELAY);
		/*
		**	Now, create the message descriptor
		**	and populate it with what we know.
		*/
		if ((md = (MESG *)Malloc(MDSIZE)) == NULL)
		{
		    errno = ENOMEM;
		    return(NULL);
		}

		memset(md, 0, sizeof (MESG));
		md->gid = recbuf.gid;
		md->readfd = md->writefd = recbuf.fd;
		md->state = MDS_IDLE;
		md->type = MD_UNKNOWN;
		md->uid = recbuf.uid;

		/*
		 * Determine if a print administrator is contacting lpsched.
		 * currently, root, lp and users with the "solaris.print.admin"
		 * privilege are print administrators
		 */
		md->admin = (md->uid == 0 || md->uid == Lp_Uid);
		if (md->admin == 0) { 
			struct passwd *pw = NULL;

			if ((pw = getpwuid(md->uid)) != NULL)
				md->admin = chkauthattr("solaris.print.admin",
							pw->pw_name);
		}
	
		get_peer_label(md->readfd, &md->slabel);

		if (mlistenadd(md, POLLIN) != 0)
		    return(NULL);

		ResetFifoBuffer (md->readfd);
		/*
		**	Reset fdp because mlistenadd may have
		**	realloc()ed PollFdList and changed its
		**	physical location.
		*/
		fdp = PollFdList + x;

		/*
		**	Clear the event that brought us here,
		**	decrement the event counter, and get the
		**	next event.
		*/
		fdp->revents = 0;
		NumEvents--;
		break;

	    case MD_CHILD:
		/*
		**	If this connection is a child process, just
		**	save the event and return the message descriptor
		*/

		if (fdp->revents & POLLOUT) {
			if (mdp->mque) {
				if (mflush(mdp) < 0) {
					syslog(LOG_DEBUG,
						"MD_CHILD mflush failed");
				}
			}
		}

		if (fdp->revents & POLLIN) {
			mdp->event = fdp->revents;
			NumEvents--;
			fdp->revents = 0;
			return (mdp);		/* we are in listening mode */
		}

		NumEvents--;
		fdp->revents = 0;
		break;

	    default:
		    /*
		    **	POLLNVAL means this client disconnected and
		    **	all messages have been processed.
		    */
		    if (fdp->revents & POLLNVAL) /* disconnected & no msg */
		    {
			if (mdp->readfd >= 0) {
				Close (mdp->readfd);
				if (mdp->writefd == mdp->readfd)
					mdp->writefd = -1;
				mdp->readfd = -1;
			}
			fdp->revents = 0;
			NumEvents--;
			continue;
		    }

		    /*
		    **	POLLERR means an error message is on the 
		    **	stream.  Since this is totally unexpected,
		    **	the assumption is made that this stream will
		    **	be flagged POLLNVAL next time through poll
		    **	and will be removed at that time.
		    */
		    if (fdp->revents & POLLERR)	/* uh, oh! */
		    {
			if (mdp->readfd >= 0) {
				Close (mdp->readfd);
				if (mdp->writefd == mdp->readfd)
					mdp->writefd = -1;
				mdp->readfd = -1;
			}
			NumEvents--;
			fdp->revents = 0;
			continue;
		    }


		    /*
		    **	POLLHUP means the client aborted the call.
		    **	The client is not removed, because there may
		    **	still be messages on the stream.
		    */
		    if (fdp->revents & POLLHUP)	/* disconnected */
		    {
			NumEvents--;
			fdp->revents = 0;
			/*
			 * MORE: This is odd. Why are we closing the
			 * stream if there ``may still be messages''???
			 */
			if (mdp->readfd >= 0) {
				Close (mdp->readfd);
				if (mdp->writefd == mdp->readfd)
					mdp->writefd = -1;
				mdp->readfd = -1;
			}
			continue;

			/*
			 * MORE: Why is this here??
			 *
			if (mdp->type == MD_SYS_FIFO)
			    (void) Close(mdp->writefd);

			mdp->writefd = -1;

			if (fdp->revents == POLLHUP)
			{
			    NumEvents--;
			    fdp->revents = 0;
			    (void) Close(mdp->readfd);
			    mdp->readfd = -1;
			    continue;
			}
			 *
			 */
		    }
		    /*
		    **	POLLOUT means that the client had a full
		    **	stream and messages became backlogged and
		    **	now the stream is empty.  So the queued msgs
		    **	are sent with putmsg(2)
		    */
		    if (fdp->revents & POLLOUT)
		    {
			if (mdp->mque == NULL)
			{
			    NumEvents--;
			    fdp->revents = 0;
			    continue;
			}
			while (mdp->mque) {
			    if (Putmsg(mdp, NULL, mdp->mque->dat, 0))
				break;	/* failed for some reason */
			    p = mdp->mque;
			    mdp->mque = p->next;
			    Free(p->dat->buf);
			    Free(p->dat);
			    Free(p);
			}
			NumEvents--;
			fdp->revents = 0;
			continue;
		    }

		    /*
		    **	POLLIN means that there is a message on the
		    **	stream.
		    **	Return the message descriptor to the caller
		    **	so that the message may be received and
		    **	processed.
		    */
		    if (fdp->revents & POLLIN)	/* got a message */
		    {
			NumEvents--;
			mdp->event = fdp->revents;
			fdp->revents = 0;
			if (mdp->type == MD_UNKNOWN)
			    mdp->type = MD_STREAM;
			return(mdp);
		    }
		    break;
	    }
	}
    }
}

# define	VOID_FUNC_PTR		void (*)()
# define	PTR_TO_VOID_FUNC_PTR	void (**)()

int
mon_discon(MESG * md, void (*fn)())
{
    int		size = 2;
    void	(**fnp) ();

    if (md->on_discon)
    {
	for (fnp = md->on_discon; *fnp; fnp++)
	    size++;
	if ((md->on_discon = (PTR_TO_VOID_FUNC_PTR) Realloc (md->on_discon, size * sizeof(VOID_FUNC_PTR))) == NULL)
	{
	    errno = ENOMEM;
	    return(-1);
	}
    }
    else
	if ((md->on_discon = (PTR_TO_VOID_FUNC_PTR) Malloc (size * sizeof(VOID_FUNC_PTR))) == NULL)
	{
	    errno = ENOMEM;
	    return(-1);
	}
    
    size--;
    md->on_discon[size] = NULL;
    size--;
    md->on_discon[size] = fn;
    return(0);
}