/* * 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 */ # include # include # include # include # include # include # include #include #include #include #include # 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 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); }