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