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