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 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 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 * 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 * 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 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