1 /* $FreeBSD$ */ 2 3 /* 4 * Implementation of SVID semaphores 5 * 6 * Author: Daniel Boulet 7 * 8 * This software is provided ``AS IS'' without any warranties of any kind. 9 */ 10 11 #include "opt_sysvipc.h" 12 13 #include <sys/param.h> 14 #include <sys/systm.h> 15 #include <sys/sysproto.h> 16 #include <sys/kernel.h> 17 #include <sys/proc.h> 18 #include <sys/sem.h> 19 #include <sys/syscall.h> 20 #include <sys/sysent.h> 21 #include <sys/sysctl.h> 22 #include <sys/malloc.h> 23 #include <sys/jail.h> 24 25 static MALLOC_DEFINE(M_SEM, "sem", "SVID compatible semaphores"); 26 27 static void seminit __P((void)); 28 static int sysvsem_modload __P((struct module *, int, void *)); 29 static int semunload __P((void)); 30 static void semexit_myhook __P((struct proc *p)); 31 32 #ifndef _SYS_SYSPROTO_H_ 33 struct __semctl_args; 34 int __semctl __P((struct proc *p, struct __semctl_args *uap)); 35 struct semget_args; 36 int semget __P((struct proc *p, struct semget_args *uap)); 37 struct semop_args; 38 int semop __P((struct proc *p, struct semop_args *uap)); 39 #endif 40 41 static struct sem_undo *semu_alloc __P((struct proc *p)); 42 static int semundo_adjust __P((struct proc *p, struct sem_undo **supptr, 43 int semid, int semnum, int adjval)); 44 static void semundo_clear __P((int semid, int semnum)); 45 46 /* XXX casting to (sy_call_t *) is bogus, as usual. */ 47 static sy_call_t *semcalls[] = { 48 (sy_call_t *)__semctl, (sy_call_t *)semget, 49 (sy_call_t *)semop 50 }; 51 52 static int semtot = 0; 53 static struct semid_ds *sema; /* semaphore id pool */ 54 static struct sem *sem; /* semaphore pool */ 55 static struct sem_undo *semu_list; /* list of active undo structures */ 56 static int *semu; /* undo structure pool */ 57 58 struct sem { 59 u_short semval; /* semaphore value */ 60 pid_t sempid; /* pid of last operation */ 61 u_short semncnt; /* # awaiting semval > cval */ 62 u_short semzcnt; /* # awaiting semval = 0 */ 63 }; 64 65 /* 66 * Undo structure (one per process) 67 */ 68 struct sem_undo { 69 struct sem_undo *un_next; /* ptr to next active undo structure */ 70 struct proc *un_proc; /* owner of this structure */ 71 short un_cnt; /* # of active entries */ 72 struct undo { 73 short un_adjval; /* adjust on exit values */ 74 short un_num; /* semaphore # */ 75 int un_id; /* semid */ 76 } un_ent[1]; /* undo entries */ 77 }; 78 79 /* 80 * Configuration parameters 81 */ 82 #ifndef SEMMNI 83 #define SEMMNI 10 /* # of semaphore identifiers */ 84 #endif 85 #ifndef SEMMNS 86 #define SEMMNS 60 /* # of semaphores in system */ 87 #endif 88 #ifndef SEMUME 89 #define SEMUME 10 /* max # of undo entries per process */ 90 #endif 91 #ifndef SEMMNU 92 #define SEMMNU 30 /* # of undo structures in system */ 93 #endif 94 95 /* shouldn't need tuning */ 96 #ifndef SEMMAP 97 #define SEMMAP 30 /* # of entries in semaphore map */ 98 #endif 99 #ifndef SEMMSL 100 #define SEMMSL SEMMNS /* max # of semaphores per id */ 101 #endif 102 #ifndef SEMOPM 103 #define SEMOPM 100 /* max # of operations per semop call */ 104 #endif 105 106 #define SEMVMX 32767 /* semaphore maximum value */ 107 #define SEMAEM 16384 /* adjust on exit max value */ 108 109 /* 110 * Due to the way semaphore memory is allocated, we have to ensure that 111 * SEMUSZ is properly aligned. 112 */ 113 114 #define SEM_ALIGN(bytes) (((bytes) + (sizeof(long) - 1)) & ~(sizeof(long) - 1)) 115 116 /* actual size of an undo structure */ 117 #define SEMUSZ SEM_ALIGN(offsetof(struct sem_undo, un_ent[SEMUME])) 118 119 /* 120 * Macro to find a particular sem_undo vector 121 */ 122 #define SEMU(ix) ((struct sem_undo *)(((intptr_t)semu)+ix * seminfo.semusz)) 123 124 /* 125 * semaphore info struct 126 */ 127 struct seminfo seminfo = { 128 SEMMAP, /* # of entries in semaphore map */ 129 SEMMNI, /* # of semaphore identifiers */ 130 SEMMNS, /* # of semaphores in system */ 131 SEMMNU, /* # of undo structures in system */ 132 SEMMSL, /* max # of semaphores per id */ 133 SEMOPM, /* max # of operations per semop call */ 134 SEMUME, /* max # of undo entries per process */ 135 SEMUSZ, /* size in bytes of undo structure */ 136 SEMVMX, /* semaphore maximum value */ 137 SEMAEM /* adjust on exit max value */ 138 }; 139 140 SYSCTL_DECL(_kern_ipc); 141 SYSCTL_INT(_kern_ipc, OID_AUTO, semmap, CTLFLAG_RW, &seminfo.semmap, 0, ""); 142 SYSCTL_INT(_kern_ipc, OID_AUTO, semmni, CTLFLAG_RD, &seminfo.semmni, 0, ""); 143 SYSCTL_INT(_kern_ipc, OID_AUTO, semmns, CTLFLAG_RD, &seminfo.semmns, 0, ""); 144 SYSCTL_INT(_kern_ipc, OID_AUTO, semmnu, CTLFLAG_RD, &seminfo.semmnu, 0, ""); 145 SYSCTL_INT(_kern_ipc, OID_AUTO, semmsl, CTLFLAG_RW, &seminfo.semmsl, 0, ""); 146 SYSCTL_INT(_kern_ipc, OID_AUTO, semopm, CTLFLAG_RD, &seminfo.semopm, 0, ""); 147 SYSCTL_INT(_kern_ipc, OID_AUTO, semume, CTLFLAG_RD, &seminfo.semume, 0, ""); 148 SYSCTL_INT(_kern_ipc, OID_AUTO, semusz, CTLFLAG_RD, &seminfo.semusz, 0, ""); 149 SYSCTL_INT(_kern_ipc, OID_AUTO, semvmx, CTLFLAG_RW, &seminfo.semvmx, 0, ""); 150 SYSCTL_INT(_kern_ipc, OID_AUTO, semaem, CTLFLAG_RW, &seminfo.semaem, 0, ""); 151 152 #if 0 153 RO seminfo.semmap /* SEMMAP unused */ 154 RO seminfo.semmni 155 RO seminfo.semmns 156 RO seminfo.semmnu /* undo entries per system */ 157 RW seminfo.semmsl 158 RO seminfo.semopm /* SEMOPM unused */ 159 RO seminfo.semume 160 RO seminfo.semusz /* param - derived from SEMUME for per-proc sizeof */ 161 RO seminfo.semvmx /* SEMVMX unused - user param */ 162 RO seminfo.semaem /* SEMAEM unused - user param */ 163 #endif 164 165 static void 166 seminit(void) 167 { 168 register int i; 169 170 sem = malloc(sizeof(struct sem) * seminfo.semmns, M_SEM, M_WAITOK); 171 if (sem == NULL) 172 panic("sem is NULL"); 173 sema = malloc(sizeof(struct semid_ds) * seminfo.semmni, M_SEM, M_WAITOK); 174 if (sema == NULL) 175 panic("sema is NULL"); 176 semu = malloc(seminfo.semmnu * seminfo.semusz, M_SEM, M_WAITOK); 177 if (semu == NULL) 178 panic("semu is NULL"); 179 180 for (i = 0; i < seminfo.semmni; i++) { 181 sema[i].sem_base = 0; 182 sema[i].sem_perm.mode = 0; 183 } 184 for (i = 0; i < seminfo.semmnu; i++) { 185 register struct sem_undo *suptr = SEMU(i); 186 suptr->un_proc = NULL; 187 } 188 semu_list = NULL; 189 semexit_hook = &semexit_myhook; 190 } 191 192 static int 193 semunload(void) 194 { 195 196 if (semtot != 0) 197 return (EBUSY); 198 199 free(sem, M_SEM); 200 free(sema, M_SEM); 201 free(semu, M_SEM); 202 semexit_hook = NULL; 203 return (0); 204 } 205 206 static int 207 sysvsem_modload(struct module *module, int cmd, void *arg) 208 { 209 int error = 0; 210 211 switch (cmd) { 212 case MOD_LOAD: 213 seminit(); 214 break; 215 case MOD_UNLOAD: 216 error = semunload(); 217 break; 218 case MOD_SHUTDOWN: 219 break; 220 default: 221 error = EINVAL; 222 break; 223 } 224 return (error); 225 } 226 227 static moduledata_t sysvsem_mod = { 228 "sysvsem", 229 &sysvsem_modload, 230 NULL 231 }; 232 233 SYSCALL_MODULE_HELPER(semsys, 5); 234 SYSCALL_MODULE_HELPER(__semctl, 4); 235 SYSCALL_MODULE_HELPER(semget, 3); 236 SYSCALL_MODULE_HELPER(semop, 3); 237 238 DECLARE_MODULE(sysvsem, sysvsem_mod, 239 SI_SUB_SYSV_SEM, SI_ORDER_FIRST); 240 MODULE_VERSION(sysvsem, 1); 241 242 /* 243 * Entry point for all SEM calls 244 */ 245 int 246 semsys(p, uap) 247 struct proc *p; 248 /* XXX actually varargs. */ 249 struct semsys_args /* { 250 u_int which; 251 int a2; 252 int a3; 253 int a4; 254 int a5; 255 } */ *uap; 256 { 257 258 if (!jail_sysvipc_allowed && p->p_prison != NULL) 259 return (ENOSYS); 260 261 if (uap->which >= sizeof(semcalls)/sizeof(semcalls[0])) 262 return (EINVAL); 263 return ((*semcalls[uap->which])(p, &uap->a2)); 264 } 265 266 /* 267 * Allocate a new sem_undo structure for a process 268 * (returns ptr to structure or NULL if no more room) 269 */ 270 271 static struct sem_undo * 272 semu_alloc(p) 273 struct proc *p; 274 { 275 register int i; 276 register struct sem_undo *suptr; 277 register struct sem_undo **supptr; 278 int attempt; 279 280 /* 281 * Try twice to allocate something. 282 * (we'll purge any empty structures after the first pass so 283 * two passes are always enough) 284 */ 285 286 for (attempt = 0; attempt < 2; attempt++) { 287 /* 288 * Look for a free structure. 289 * Fill it in and return it if we find one. 290 */ 291 292 for (i = 0; i < seminfo.semmnu; i++) { 293 suptr = SEMU(i); 294 if (suptr->un_proc == NULL) { 295 suptr->un_next = semu_list; 296 semu_list = suptr; 297 suptr->un_cnt = 0; 298 suptr->un_proc = p; 299 return(suptr); 300 } 301 } 302 303 /* 304 * We didn't find a free one, if this is the first attempt 305 * then try to free some structures. 306 */ 307 308 if (attempt == 0) { 309 /* All the structures are in use - try to free some */ 310 int did_something = 0; 311 312 supptr = &semu_list; 313 while ((suptr = *supptr) != NULL) { 314 if (suptr->un_cnt == 0) { 315 suptr->un_proc = NULL; 316 *supptr = suptr->un_next; 317 did_something = 1; 318 } else 319 supptr = &(suptr->un_next); 320 } 321 322 /* If we didn't free anything then just give-up */ 323 if (!did_something) 324 return(NULL); 325 } else { 326 /* 327 * The second pass failed even though we freed 328 * something after the first pass! 329 * This is IMPOSSIBLE! 330 */ 331 panic("semu_alloc - second attempt failed"); 332 } 333 } 334 return (NULL); 335 } 336 337 /* 338 * Adjust a particular entry for a particular proc 339 */ 340 341 static int 342 semundo_adjust(p, supptr, semid, semnum, adjval) 343 register struct proc *p; 344 struct sem_undo **supptr; 345 int semid, semnum; 346 int adjval; 347 { 348 register struct sem_undo *suptr; 349 register struct undo *sunptr; 350 int i; 351 352 /* Look for and remember the sem_undo if the caller doesn't provide 353 it */ 354 355 suptr = *supptr; 356 if (suptr == NULL) { 357 for (suptr = semu_list; suptr != NULL; 358 suptr = suptr->un_next) { 359 if (suptr->un_proc == p) { 360 *supptr = suptr; 361 break; 362 } 363 } 364 if (suptr == NULL) { 365 if (adjval == 0) 366 return(0); 367 suptr = semu_alloc(p); 368 if (suptr == NULL) 369 return(ENOSPC); 370 *supptr = suptr; 371 } 372 } 373 374 /* 375 * Look for the requested entry and adjust it (delete if adjval becomes 376 * 0). 377 */ 378 sunptr = &suptr->un_ent[0]; 379 for (i = 0; i < suptr->un_cnt; i++, sunptr++) { 380 if (sunptr->un_id != semid || sunptr->un_num != semnum) 381 continue; 382 if (adjval == 0) 383 sunptr->un_adjval = 0; 384 else 385 sunptr->un_adjval += adjval; 386 if (sunptr->un_adjval == 0) { 387 suptr->un_cnt--; 388 if (i < suptr->un_cnt) 389 suptr->un_ent[i] = 390 suptr->un_ent[suptr->un_cnt]; 391 } 392 return(0); 393 } 394 395 /* Didn't find the right entry - create it */ 396 if (adjval == 0) 397 return(0); 398 if (suptr->un_cnt != seminfo.semume) { 399 sunptr = &suptr->un_ent[suptr->un_cnt]; 400 suptr->un_cnt++; 401 sunptr->un_adjval = adjval; 402 sunptr->un_id = semid; sunptr->un_num = semnum; 403 } else 404 return(EINVAL); 405 return(0); 406 } 407 408 static void 409 semundo_clear(semid, semnum) 410 int semid, semnum; 411 { 412 register struct sem_undo *suptr; 413 414 for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) { 415 register struct undo *sunptr = &suptr->un_ent[0]; 416 register int i = 0; 417 418 while (i < suptr->un_cnt) { 419 if (sunptr->un_id == semid) { 420 if (semnum == -1 || sunptr->un_num == semnum) { 421 suptr->un_cnt--; 422 if (i < suptr->un_cnt) { 423 suptr->un_ent[i] = 424 suptr->un_ent[suptr->un_cnt]; 425 continue; 426 } 427 } 428 if (semnum != -1) 429 break; 430 } 431 i++, sunptr++; 432 } 433 } 434 } 435 436 /* 437 * Note that the user-mode half of this passes a union, not a pointer 438 */ 439 #ifndef _SYS_SYSPROTO_H_ 440 struct __semctl_args { 441 int semid; 442 int semnum; 443 int cmd; 444 union semun *arg; 445 }; 446 #endif 447 448 int 449 __semctl(p, uap) 450 struct proc *p; 451 register struct __semctl_args *uap; 452 { 453 int semid = uap->semid; 454 int semnum = uap->semnum; 455 int cmd = uap->cmd; 456 union semun *arg = uap->arg; 457 union semun real_arg; 458 struct ucred *cred = p->p_ucred; 459 int i, rval, eval; 460 struct semid_ds sbuf; 461 register struct semid_ds *semaptr; 462 463 #ifdef SEM_DEBUG 464 printf("call to semctl(%d, %d, %d, 0x%x)\n", semid, semnum, cmd, arg); 465 #endif 466 467 if (!jail_sysvipc_allowed && p->p_prison != NULL) 468 return (ENOSYS); 469 470 semid = IPCID_TO_IX(semid); 471 if (semid < 0 || semid >= seminfo.semmsl) 472 return(EINVAL); 473 474 semaptr = &sema[semid]; 475 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || 476 semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) 477 return(EINVAL); 478 479 eval = 0; 480 rval = 0; 481 482 switch (cmd) { 483 case IPC_RMID: 484 if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_M))) 485 return(eval); 486 semaptr->sem_perm.cuid = cred->cr_uid; 487 semaptr->sem_perm.uid = cred->cr_uid; 488 semtot -= semaptr->sem_nsems; 489 for (i = semaptr->sem_base - sem; i < semtot; i++) 490 sem[i] = sem[i + semaptr->sem_nsems]; 491 for (i = 0; i < seminfo.semmni; i++) { 492 if ((sema[i].sem_perm.mode & SEM_ALLOC) && 493 sema[i].sem_base > semaptr->sem_base) 494 sema[i].sem_base -= semaptr->sem_nsems; 495 } 496 semaptr->sem_perm.mode = 0; 497 semundo_clear(semid, -1); 498 wakeup((caddr_t)semaptr); 499 break; 500 501 case IPC_SET: 502 if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_M))) 503 return(eval); 504 if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 505 return(eval); 506 if ((eval = copyin(real_arg.buf, (caddr_t)&sbuf, 507 sizeof(sbuf))) != 0) 508 return(eval); 509 semaptr->sem_perm.uid = sbuf.sem_perm.uid; 510 semaptr->sem_perm.gid = sbuf.sem_perm.gid; 511 semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) | 512 (sbuf.sem_perm.mode & 0777); 513 semaptr->sem_ctime = time_second; 514 break; 515 516 case IPC_STAT: 517 if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_R))) 518 return(eval); 519 if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 520 return(eval); 521 eval = copyout((caddr_t)semaptr, real_arg.buf, 522 sizeof(struct semid_ds)); 523 break; 524 525 case GETNCNT: 526 if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_R))) 527 return(eval); 528 if (semnum < 0 || semnum >= semaptr->sem_nsems) 529 return(EINVAL); 530 rval = semaptr->sem_base[semnum].semncnt; 531 break; 532 533 case GETPID: 534 if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_R))) 535 return(eval); 536 if (semnum < 0 || semnum >= semaptr->sem_nsems) 537 return(EINVAL); 538 rval = semaptr->sem_base[semnum].sempid; 539 break; 540 541 case GETVAL: 542 if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_R))) 543 return(eval); 544 if (semnum < 0 || semnum >= semaptr->sem_nsems) 545 return(EINVAL); 546 rval = semaptr->sem_base[semnum].semval; 547 break; 548 549 case GETALL: 550 if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_R))) 551 return(eval); 552 if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 553 return(eval); 554 for (i = 0; i < semaptr->sem_nsems; i++) { 555 eval = copyout((caddr_t)&semaptr->sem_base[i].semval, 556 &real_arg.array[i], sizeof(real_arg.array[0])); 557 if (eval != 0) 558 break; 559 } 560 break; 561 562 case GETZCNT: 563 if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_R))) 564 return(eval); 565 if (semnum < 0 || semnum >= semaptr->sem_nsems) 566 return(EINVAL); 567 rval = semaptr->sem_base[semnum].semzcnt; 568 break; 569 570 case SETVAL: 571 if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_W))) 572 return(eval); 573 if (semnum < 0 || semnum >= semaptr->sem_nsems) 574 return(EINVAL); 575 if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 576 return(eval); 577 semaptr->sem_base[semnum].semval = real_arg.val; 578 semundo_clear(semid, semnum); 579 wakeup((caddr_t)semaptr); 580 break; 581 582 case SETALL: 583 if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_W))) 584 return(eval); 585 if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 586 return(eval); 587 for (i = 0; i < semaptr->sem_nsems; i++) { 588 eval = copyin(&real_arg.array[i], 589 (caddr_t)&semaptr->sem_base[i].semval, 590 sizeof(real_arg.array[0])); 591 if (eval != 0) 592 break; 593 } 594 semundo_clear(semid, -1); 595 wakeup((caddr_t)semaptr); 596 break; 597 598 default: 599 return(EINVAL); 600 } 601 602 if (eval == 0) 603 p->p_retval[0] = rval; 604 return(eval); 605 } 606 607 #ifndef _SYS_SYSPROTO_H_ 608 struct semget_args { 609 key_t key; 610 int nsems; 611 int semflg; 612 }; 613 #endif 614 615 int 616 semget(p, uap) 617 struct proc *p; 618 register struct semget_args *uap; 619 { 620 int semid, eval; 621 int key = uap->key; 622 int nsems = uap->nsems; 623 int semflg = uap->semflg; 624 struct ucred *cred = p->p_ucred; 625 626 #ifdef SEM_DEBUG 627 printf("semget(0x%x, %d, 0%o)\n", key, nsems, semflg); 628 #endif 629 630 if (!jail_sysvipc_allowed && p->p_prison != NULL) 631 return (ENOSYS); 632 633 if (key != IPC_PRIVATE) { 634 for (semid = 0; semid < seminfo.semmni; semid++) { 635 if ((sema[semid].sem_perm.mode & SEM_ALLOC) && 636 sema[semid].sem_perm.key == key) 637 break; 638 } 639 if (semid < seminfo.semmni) { 640 #ifdef SEM_DEBUG 641 printf("found public key\n"); 642 #endif 643 if ((eval = ipcperm(p, &sema[semid].sem_perm, 644 semflg & 0700))) 645 return(eval); 646 if (nsems > 0 && sema[semid].sem_nsems < nsems) { 647 #ifdef SEM_DEBUG 648 printf("too small\n"); 649 #endif 650 return(EINVAL); 651 } 652 if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) { 653 #ifdef SEM_DEBUG 654 printf("not exclusive\n"); 655 #endif 656 return(EEXIST); 657 } 658 goto found; 659 } 660 } 661 662 #ifdef SEM_DEBUG 663 printf("need to allocate the semid_ds\n"); 664 #endif 665 if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) { 666 if (nsems <= 0 || nsems > seminfo.semmsl) { 667 #ifdef SEM_DEBUG 668 printf("nsems out of range (0<%d<=%d)\n", nsems, 669 seminfo.semmsl); 670 #endif 671 return(EINVAL); 672 } 673 if (nsems > seminfo.semmns - semtot) { 674 #ifdef SEM_DEBUG 675 printf("not enough semaphores left (need %d, got %d)\n", 676 nsems, seminfo.semmns - semtot); 677 #endif 678 return(ENOSPC); 679 } 680 for (semid = 0; semid < seminfo.semmni; semid++) { 681 if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0) 682 break; 683 } 684 if (semid == seminfo.semmni) { 685 #ifdef SEM_DEBUG 686 printf("no more semid_ds's available\n"); 687 #endif 688 return(ENOSPC); 689 } 690 #ifdef SEM_DEBUG 691 printf("semid %d is available\n", semid); 692 #endif 693 sema[semid].sem_perm.key = key; 694 sema[semid].sem_perm.cuid = cred->cr_uid; 695 sema[semid].sem_perm.uid = cred->cr_uid; 696 sema[semid].sem_perm.cgid = cred->cr_gid; 697 sema[semid].sem_perm.gid = cred->cr_gid; 698 sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC; 699 sema[semid].sem_perm.seq = 700 (sema[semid].sem_perm.seq + 1) & 0x7fff; 701 sema[semid].sem_nsems = nsems; 702 sema[semid].sem_otime = 0; 703 sema[semid].sem_ctime = time_second; 704 sema[semid].sem_base = &sem[semtot]; 705 semtot += nsems; 706 bzero(sema[semid].sem_base, 707 sizeof(sema[semid].sem_base[0])*nsems); 708 #ifdef SEM_DEBUG 709 printf("sembase = 0x%x, next = 0x%x\n", sema[semid].sem_base, 710 &sem[semtot]); 711 #endif 712 } else { 713 #ifdef SEM_DEBUG 714 printf("didn't find it and wasn't asked to create it\n"); 715 #endif 716 return(ENOENT); 717 } 718 719 found: 720 p->p_retval[0] = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm); 721 return(0); 722 } 723 724 #ifndef _SYS_SYSPROTO_H_ 725 struct semop_args { 726 int semid; 727 struct sembuf *sops; 728 int nsops; 729 }; 730 #endif 731 732 int 733 semop(p, uap) 734 struct proc *p; 735 register struct semop_args *uap; 736 { 737 int semid = uap->semid; 738 int nsops = uap->nsops; 739 struct sembuf sops[MAX_SOPS]; 740 register struct semid_ds *semaptr; 741 register struct sembuf *sopptr; 742 register struct sem *semptr; 743 struct sem_undo *suptr = NULL; 744 int i, j, eval; 745 int do_wakeup, do_undos; 746 747 #ifdef SEM_DEBUG 748 printf("call to semop(%d, 0x%x, %d)\n", semid, sops, nsops); 749 #endif 750 751 if (!jail_sysvipc_allowed && p->p_prison != NULL) 752 return (ENOSYS); 753 754 semid = IPCID_TO_IX(semid); /* Convert back to zero origin */ 755 756 if (semid < 0 || semid >= seminfo.semmsl) 757 return(EINVAL); 758 759 semaptr = &sema[semid]; 760 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) 761 return(EINVAL); 762 if (semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) 763 return(EINVAL); 764 765 if ((eval = ipcperm(p, &semaptr->sem_perm, IPC_W))) { 766 #ifdef SEM_DEBUG 767 printf("eval = %d from ipaccess\n", eval); 768 #endif 769 return(eval); 770 } 771 772 if (nsops > MAX_SOPS) { 773 #ifdef SEM_DEBUG 774 printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS, nsops); 775 #endif 776 return(E2BIG); 777 } 778 779 if ((eval = copyin(uap->sops, &sops, nsops * sizeof(sops[0]))) != 0) { 780 #ifdef SEM_DEBUG 781 printf("eval = %d from copyin(%08x, %08x, %d)\n", eval, 782 uap->sops, &sops, nsops * sizeof(sops[0])); 783 #endif 784 return(eval); 785 } 786 787 /* 788 * Loop trying to satisfy the vector of requests. 789 * If we reach a point where we must wait, any requests already 790 * performed are rolled back and we go to sleep until some other 791 * process wakes us up. At this point, we start all over again. 792 * 793 * This ensures that from the perspective of other tasks, a set 794 * of requests is atomic (never partially satisfied). 795 */ 796 do_undos = 0; 797 798 for (;;) { 799 do_wakeup = 0; 800 801 for (i = 0; i < nsops; i++) { 802 sopptr = &sops[i]; 803 804 if (sopptr->sem_num >= semaptr->sem_nsems) 805 return(EFBIG); 806 807 semptr = &semaptr->sem_base[sopptr->sem_num]; 808 809 #ifdef SEM_DEBUG 810 printf("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n", 811 semaptr, semaptr->sem_base, semptr, 812 sopptr->sem_num, semptr->semval, sopptr->sem_op, 813 (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait"); 814 #endif 815 816 if (sopptr->sem_op < 0) { 817 if (semptr->semval + sopptr->sem_op < 0) { 818 #ifdef SEM_DEBUG 819 printf("semop: can't do it now\n"); 820 #endif 821 break; 822 } else { 823 semptr->semval += sopptr->sem_op; 824 if (semptr->semval == 0 && 825 semptr->semzcnt > 0) 826 do_wakeup = 1; 827 } 828 if (sopptr->sem_flg & SEM_UNDO) 829 do_undos = 1; 830 } else if (sopptr->sem_op == 0) { 831 if (semptr->semval > 0) { 832 #ifdef SEM_DEBUG 833 printf("semop: not zero now\n"); 834 #endif 835 break; 836 } 837 } else { 838 if (semptr->semncnt > 0) 839 do_wakeup = 1; 840 semptr->semval += sopptr->sem_op; 841 if (sopptr->sem_flg & SEM_UNDO) 842 do_undos = 1; 843 } 844 } 845 846 /* 847 * Did we get through the entire vector? 848 */ 849 if (i >= nsops) 850 goto done; 851 852 /* 853 * No ... rollback anything that we've already done 854 */ 855 #ifdef SEM_DEBUG 856 printf("semop: rollback 0 through %d\n", i-1); 857 #endif 858 for (j = 0; j < i; j++) 859 semaptr->sem_base[sops[j].sem_num].semval -= 860 sops[j].sem_op; 861 862 /* 863 * If the request that we couldn't satisfy has the 864 * NOWAIT flag set then return with EAGAIN. 865 */ 866 if (sopptr->sem_flg & IPC_NOWAIT) 867 return(EAGAIN); 868 869 if (sopptr->sem_op == 0) 870 semptr->semzcnt++; 871 else 872 semptr->semncnt++; 873 874 #ifdef SEM_DEBUG 875 printf("semop: good night!\n"); 876 #endif 877 eval = tsleep((caddr_t)semaptr, (PZERO - 4) | PCATCH, 878 "semwait", 0); 879 #ifdef SEM_DEBUG 880 printf("semop: good morning (eval=%d)!\n", eval); 881 #endif 882 883 suptr = NULL; /* sem_undo may have been reallocated */ 884 885 if (eval != 0) 886 return(EINTR); 887 #ifdef SEM_DEBUG 888 printf("semop: good morning!\n"); 889 #endif 890 891 /* 892 * Make sure that the semaphore still exists 893 */ 894 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || 895 semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) 896 return(EIDRM); 897 898 /* 899 * The semaphore is still alive. Readjust the count of 900 * waiting processes. 901 */ 902 if (sopptr->sem_op == 0) 903 semptr->semzcnt--; 904 else 905 semptr->semncnt--; 906 } 907 908 done: 909 /* 910 * Process any SEM_UNDO requests. 911 */ 912 if (do_undos) { 913 for (i = 0; i < nsops; i++) { 914 /* 915 * We only need to deal with SEM_UNDO's for non-zero 916 * op's. 917 */ 918 int adjval; 919 920 if ((sops[i].sem_flg & SEM_UNDO) == 0) 921 continue; 922 adjval = sops[i].sem_op; 923 if (adjval == 0) 924 continue; 925 eval = semundo_adjust(p, &suptr, semid, 926 sops[i].sem_num, -adjval); 927 if (eval == 0) 928 continue; 929 930 /* 931 * Oh-Oh! We ran out of either sem_undo's or undo's. 932 * Rollback the adjustments to this point and then 933 * rollback the semaphore ups and down so we can return 934 * with an error with all structures restored. We 935 * rollback the undo's in the exact reverse order that 936 * we applied them. This guarantees that we won't run 937 * out of space as we roll things back out. 938 */ 939 for (j = i - 1; j >= 0; j--) { 940 if ((sops[j].sem_flg & SEM_UNDO) == 0) 941 continue; 942 adjval = sops[j].sem_op; 943 if (adjval == 0) 944 continue; 945 if (semundo_adjust(p, &suptr, semid, 946 sops[j].sem_num, adjval) != 0) 947 panic("semop - can't undo undos"); 948 } 949 950 for (j = 0; j < nsops; j++) 951 semaptr->sem_base[sops[j].sem_num].semval -= 952 sops[j].sem_op; 953 954 #ifdef SEM_DEBUG 955 printf("eval = %d from semundo_adjust\n", eval); 956 #endif 957 return(eval); 958 } /* loop through the sops */ 959 } /* if (do_undos) */ 960 961 /* We're definitely done - set the sempid's */ 962 for (i = 0; i < nsops; i++) { 963 sopptr = &sops[i]; 964 semptr = &semaptr->sem_base[sopptr->sem_num]; 965 semptr->sempid = p->p_pid; 966 } 967 968 /* Do a wakeup if any semaphore was up'd. */ 969 if (do_wakeup) { 970 #ifdef SEM_DEBUG 971 printf("semop: doing wakeup\n"); 972 #endif 973 wakeup((caddr_t)semaptr); 974 #ifdef SEM_DEBUG 975 printf("semop: back from wakeup\n"); 976 #endif 977 } 978 #ifdef SEM_DEBUG 979 printf("semop: done\n"); 980 #endif 981 p->p_retval[0] = 0; 982 return(0); 983 } 984 985 /* 986 * Go through the undo structures for this process and apply the adjustments to 987 * semaphores. 988 */ 989 static void 990 semexit_myhook(p) 991 struct proc *p; 992 { 993 register struct sem_undo *suptr; 994 register struct sem_undo **supptr; 995 int did_something; 996 997 did_something = 0; 998 999 /* 1000 * Go through the chain of undo vectors looking for one 1001 * associated with this process. 1002 */ 1003 1004 for (supptr = &semu_list; (suptr = *supptr) != NULL; 1005 supptr = &suptr->un_next) { 1006 if (suptr->un_proc == p) 1007 break; 1008 } 1009 1010 if (suptr == NULL) 1011 return; 1012 1013 #ifdef SEM_DEBUG 1014 printf("proc @%08x has undo structure with %d entries\n", p, 1015 suptr->un_cnt); 1016 #endif 1017 1018 /* 1019 * If there are any active undo elements then process them. 1020 */ 1021 if (suptr->un_cnt > 0) { 1022 int ix; 1023 1024 for (ix = 0; ix < suptr->un_cnt; ix++) { 1025 int semid = suptr->un_ent[ix].un_id; 1026 int semnum = suptr->un_ent[ix].un_num; 1027 int adjval = suptr->un_ent[ix].un_adjval; 1028 struct semid_ds *semaptr; 1029 1030 semaptr = &sema[semid]; 1031 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) 1032 panic("semexit - semid not allocated"); 1033 if (semnum >= semaptr->sem_nsems) 1034 panic("semexit - semnum out of range"); 1035 1036 #ifdef SEM_DEBUG 1037 printf("semexit: %08x id=%d num=%d(adj=%d) ; sem=%d\n", 1038 suptr->un_proc, suptr->un_ent[ix].un_id, 1039 suptr->un_ent[ix].un_num, 1040 suptr->un_ent[ix].un_adjval, 1041 semaptr->sem_base[semnum].semval); 1042 #endif 1043 1044 if (adjval < 0) { 1045 if (semaptr->sem_base[semnum].semval < -adjval) 1046 semaptr->sem_base[semnum].semval = 0; 1047 else 1048 semaptr->sem_base[semnum].semval += 1049 adjval; 1050 } else 1051 semaptr->sem_base[semnum].semval += adjval; 1052 1053 wakeup((caddr_t)semaptr); 1054 #ifdef SEM_DEBUG 1055 printf("semexit: back from wakeup\n"); 1056 #endif 1057 } 1058 } 1059 1060 /* 1061 * Deallocate the undo vector. 1062 */ 1063 #ifdef SEM_DEBUG 1064 printf("removing vector\n"); 1065 #endif 1066 suptr->un_proc = NULL; 1067 *supptr = suptr->un_next; 1068 } 1069