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