text: /*
   * 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);
  }
  