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