1 #define __USE_MISC 2 3 #include <minix/vm.h> 4 5 #include "inc.h" 6 7 struct waiting { 8 endpoint_t who; /* who is waiting */ 9 int val; /* value he/she is waiting for */ 10 }; 11 12 struct semaphore { 13 unsigned short semval; /* semaphore value */ 14 unsigned short semzcnt; /* # waiting for zero */ 15 unsigned short semncnt; /* # waiting for increase */ 16 struct waiting *zlist; /* process waiting for zero */ 17 struct waiting *nlist; /* process waiting for increase */ 18 pid_t sempid; /* process that did last op */ 19 }; 20 21 struct sem_struct { 22 key_t key; 23 int id; 24 struct semid_ds semid_ds; 25 struct semaphore sems[SEMMSL]; 26 }; 27 28 static struct sem_struct sem_list[SEMMNI]; 29 static int sem_list_nr = 0; 30 31 static struct sem_struct *sem_find_key(key_t key) 32 { 33 int i; 34 if (key == IPC_PRIVATE) 35 return NULL; 36 for (i = 0; i < sem_list_nr; i++) 37 if (sem_list[i].key == key) 38 return sem_list+i; 39 return NULL; 40 } 41 42 static struct sem_struct *sem_find_id(int id) 43 { 44 int i; 45 for (i = 0; i < sem_list_nr; i++) 46 if (sem_list[i].id == id) 47 return sem_list+i; 48 return NULL; 49 } 50 51 /*===========================================================================* 52 * do_semget * 53 *===========================================================================*/ 54 int do_semget(message *m) 55 { 56 key_t key; 57 int nsems, flag, id; 58 struct sem_struct *sem; 59 60 key = m->m_lc_ipc_semget.key; 61 nsems = m->m_lc_ipc_semget.nr; 62 flag = m->m_lc_ipc_semget.flag; 63 64 if ((sem = sem_find_key(key))) { 65 if ((flag & IPC_CREAT) && (flag & IPC_EXCL)) 66 return EEXIST; 67 if (!check_perm(&sem->semid_ds.sem_perm, who_e, flag)) 68 return EACCES; 69 if (nsems > sem->semid_ds.sem_nsems) 70 return EINVAL; 71 id = sem->id; 72 } else { 73 if (!(flag & IPC_CREAT)) 74 return ENOENT; 75 if (nsems < 0 || nsems >= SEMMSL) 76 return EINVAL; 77 if (sem_list_nr == SEMMNI) 78 return ENOSPC; 79 80 /* create a new semaphore set */ 81 sem = &sem_list[sem_list_nr]; 82 memset(sem, 0, sizeof(struct sem_struct)); 83 sem->semid_ds.sem_perm.cuid = 84 sem->semid_ds.sem_perm.uid = getnuid(who_e); 85 sem->semid_ds.sem_perm.cgid = 86 sem->semid_ds.sem_perm.gid = getngid(who_e); 87 sem->semid_ds.sem_perm.mode = flag & 0777; 88 sem->semid_ds.sem_nsems = nsems; 89 sem->semid_ds.sem_otime = 0; 90 sem->semid_ds.sem_ctime = time(NULL); 91 sem->id = id = identifier++; 92 sem->key = key; 93 94 sem_list_nr++; 95 } 96 97 m->m_lc_ipc_semget.retid = id; 98 return OK; 99 } 100 101 static void send_message_to_process(endpoint_t who, int ret, int ignore) 102 { 103 message m; 104 105 m.m_type = ret; 106 ipc_sendnb(who, &m); 107 } 108 109 static void remove_semaphore(struct sem_struct *sem) 110 { 111 int i, nr; 112 113 nr = sem->semid_ds.sem_nsems; 114 115 for (i = 0; i < nr; i++) { 116 if (sem->sems[i].zlist) 117 free(sem->sems[i].zlist); 118 if (sem->sems[i].nlist) 119 free(sem->sems[i].nlist); 120 } 121 122 for (i = 0; i < sem_list_nr; i++) { 123 if (&sem_list[i] == sem) 124 break; 125 } 126 127 if (i < sem_list_nr && --sem_list_nr != i) 128 sem_list[i] = sem_list[sem_list_nr]; 129 } 130 131 #if 0 132 static void show_semaphore(void) 133 { 134 int i, j, k; 135 136 for (i = 0; i < sem_list_nr; i++) { 137 int nr = sem_list[i].semid_ds.sem_nsems; 138 139 printf("===== [%d] =====\n", i); 140 for (j = 0; j < nr; j++) { 141 struct semaphore *semaphore = &sem_list[i].sems[j]; 142 143 if (!semaphore->semzcnt && !semaphore->semncnt) 144 continue; 145 146 printf(" (%d): ", semaphore->semval); 147 if (semaphore->semzcnt) { 148 printf("zero("); 149 for (k = 0; k < semaphore->semzcnt; k++) 150 printf("%d,", semaphore->zlist[k].who); 151 printf(") "); 152 } 153 if (semaphore->semncnt) { 154 printf("incr("); 155 for (k = 0; k < semaphore->semncnt; k++) 156 printf("%d-%d,", 157 semaphore->nlist[k].who, semaphore->nlist[k].val); 158 printf(")"); 159 } 160 printf("\n"); 161 } 162 } 163 printf("\n"); 164 } 165 #endif 166 167 static void remove_process(endpoint_t pt) 168 { 169 int i; 170 171 for (i = 0; i < sem_list_nr; i++) { 172 struct sem_struct *sem = &sem_list[i]; 173 int nr = sem->semid_ds.sem_nsems; 174 int j; 175 176 for (j = 0; j < nr; j++) { 177 struct semaphore *semaphore = &sem->sems[j]; 178 int k; 179 180 for (k = 0; k < semaphore->semzcnt; k++) { 181 endpoint_t who_waiting = semaphore->zlist[k].who; 182 183 if (who_waiting == pt) { 184 /* remove this slot first */ 185 memmove(semaphore->zlist+k, semaphore->zlist+k+1, 186 sizeof(struct waiting) * (semaphore->semzcnt-k-1)); 187 --semaphore->semzcnt; 188 /* then send message to the process */ 189 send_message_to_process(who_waiting, EINTR, 1); 190 191 break; 192 } 193 } 194 195 for (k = 0; k < semaphore->semncnt; k++) { 196 endpoint_t who_waiting = semaphore->nlist[k].who; 197 198 if (who_waiting == pt) { 199 /* remove it first */ 200 memmove(semaphore->nlist+k, semaphore->nlist+k+1, 201 sizeof(struct waiting) * (semaphore->semncnt-k-1)); 202 --semaphore->semncnt; 203 /* send the message to the process */ 204 send_message_to_process(who_waiting, EINTR, 1); 205 206 break; 207 } 208 } 209 } 210 } 211 } 212 213 static void update_one_semaphore(struct sem_struct *sem, int is_remove) 214 { 215 int i, j, nr; 216 struct semaphore *semaphore; 217 endpoint_t who; 218 219 nr = sem->semid_ds.sem_nsems; 220 221 if (is_remove) { 222 for (i = 0; i < nr; i++) { 223 semaphore = &sem->sems[i]; 224 225 for (j = 0; j < semaphore->semzcnt; j++) 226 send_message_to_process(semaphore->zlist[j].who, EIDRM, 0); 227 for (j = 0; j < semaphore->semncnt; j++) 228 send_message_to_process(semaphore->nlist[j].who, EIDRM, 0); 229 } 230 231 remove_semaphore(sem); 232 return; 233 } 234 235 for (i = 0; i < nr; i++) { 236 semaphore = &sem->sems[i]; 237 238 if (semaphore->zlist && !semaphore->semval) { 239 /* choose one process, policy: FIFO. */ 240 who = semaphore->zlist[0].who; 241 242 memmove(semaphore->zlist, semaphore->zlist+1, 243 sizeof(struct waiting) * (semaphore->semzcnt-1)); 244 --semaphore->semzcnt; 245 246 send_message_to_process(who, OK, 0); 247 } 248 249 if (semaphore->nlist) { 250 for (j = 0; j < semaphore->semncnt; j++) { 251 if (semaphore->nlist[j].val <= semaphore->semval) { 252 semaphore->semval -= semaphore->nlist[j].val; 253 who = semaphore->nlist[j].who; 254 255 memmove(semaphore->nlist+j, semaphore->nlist+j+1, 256 sizeof(struct waiting) * (semaphore->semncnt-j-1)); 257 --semaphore->semncnt; 258 259 send_message_to_process(who, OK, 0); 260 261 /* choose only one process */ 262 break; 263 } 264 } 265 } 266 } 267 } 268 269 static void update_semaphores(void) 270 { 271 int i; 272 273 for (i = 0; i < sem_list_nr; i++) 274 update_one_semaphore(sem_list+i, 0 /* not remove */); 275 } 276 277 /*===========================================================================* 278 * do_semctl * 279 *===========================================================================*/ 280 int do_semctl(message *m) 281 { 282 int r, i; 283 long opt = 0; 284 uid_t uid; 285 int id, num, cmd, val; 286 unsigned short *buf; 287 struct semid_ds *ds, tmp_ds; 288 struct sem_struct *sem; 289 290 id = m->m_lc_ipc_semctl.id; 291 num = m->m_lc_ipc_semctl.num; 292 cmd = m->m_lc_ipc_semctl.cmd; 293 294 if (cmd == IPC_STAT || cmd == IPC_SET || cmd == IPC_INFO || 295 cmd == SEM_INFO || cmd == SEM_STAT || cmd == GETALL || 296 cmd == SETALL || cmd == SETVAL) 297 opt = m->m_lc_ipc_semctl.opt; 298 299 if (!(sem = sem_find_id(id))) { 300 return EINVAL; 301 } 302 303 /* IPC_SET and IPC_RMID as its own permission check */ 304 if (cmd != IPC_SET && cmd != IPC_RMID) { 305 /* check read permission */ 306 if (!check_perm(&sem->semid_ds.sem_perm, who_e, 0444)) 307 return EACCES; 308 } 309 310 switch (cmd) { 311 case IPC_STAT: 312 ds = (struct semid_ds *) opt; 313 if (!ds) 314 return EFAULT; 315 r = sys_datacopy(SELF, (vir_bytes) &sem->semid_ds, 316 who_e, (vir_bytes) ds, sizeof(struct semid_ds)); 317 if (r != OK) 318 return EINVAL; 319 break; 320 case IPC_SET: 321 uid = getnuid(who_e); 322 if (uid != sem->semid_ds.sem_perm.cuid && 323 uid != sem->semid_ds.sem_perm.uid && 324 uid != 0) 325 return EPERM; 326 ds = (struct semid_ds *) opt; 327 r = sys_datacopy(who_e, (vir_bytes) ds, 328 SELF, (vir_bytes) &tmp_ds, sizeof(struct semid_ds)); 329 if (r != OK) 330 return EINVAL; 331 sem->semid_ds.sem_perm.uid = tmp_ds.sem_perm.uid; 332 sem->semid_ds.sem_perm.gid = tmp_ds.sem_perm.gid; 333 sem->semid_ds.sem_perm.mode &= ~0777; 334 sem->semid_ds.sem_perm.mode |= tmp_ds.sem_perm.mode & 0666; 335 sem->semid_ds.sem_ctime = time(NULL); 336 break; 337 case IPC_RMID: 338 uid = getnuid(who_e); 339 if (uid != sem->semid_ds.sem_perm.cuid && 340 uid != sem->semid_ds.sem_perm.uid && 341 uid != 0) 342 return EPERM; 343 /* awaken all processes block in semop 344 * and remove the semaphore set. 345 */ 346 update_one_semaphore(sem, 1); 347 break; 348 case IPC_INFO: 349 break; 350 case SEM_INFO: 351 break; 352 case SEM_STAT: 353 break; 354 case GETALL: 355 buf = malloc(sizeof(unsigned short) * sem->semid_ds.sem_nsems); 356 if (!buf) 357 return ENOMEM; 358 for (i = 0; i < sem->semid_ds.sem_nsems; i++) 359 buf[i] = sem->sems[i].semval; 360 r = sys_datacopy(SELF, (vir_bytes) buf, 361 who_e, (vir_bytes) opt, 362 sizeof(unsigned short) * sem->semid_ds.sem_nsems); 363 if (r != OK) 364 return EINVAL; 365 free(buf); 366 break; 367 case GETNCNT: 368 if (num < 0 || num >= sem->semid_ds.sem_nsems) 369 return EINVAL; 370 m->m_lc_ipc_semctl.ret = sem->sems[num].semncnt; 371 break; 372 case GETPID: 373 if (num < 0 || num >= sem->semid_ds.sem_nsems) 374 return EINVAL; 375 m->m_lc_ipc_semctl.ret = sem->sems[num].sempid; 376 break; 377 case GETVAL: 378 if (num < 0 || num >= sem->semid_ds.sem_nsems) 379 return EINVAL; 380 m->m_lc_ipc_semctl.ret = sem->sems[num].semval; 381 break; 382 case GETZCNT: 383 if (num < 0 || num >= sem->semid_ds.sem_nsems) 384 return EINVAL; 385 m->m_lc_ipc_semctl.ret = sem->sems[num].semzcnt; 386 break; 387 case SETALL: 388 buf = malloc(sizeof(unsigned short) * sem->semid_ds.sem_nsems); 389 if (!buf) 390 return ENOMEM; 391 r = sys_datacopy(who_e, (vir_bytes) opt, 392 SELF, (vir_bytes) buf, 393 sizeof(unsigned short) * sem->semid_ds.sem_nsems); 394 if (r != OK) 395 return EINVAL; 396 #ifdef DEBUG_SEM 397 printf("SEMCTL: SETALL: opt: %lu\n", (vir_bytes) opt); 398 for (i = 0; i < sem->semid_ds.sem_nsems; i++) 399 printf("SEMCTL: SETALL val: [%d] %d\n", i, buf[i]); 400 #endif 401 for (i = 0; i < sem->semid_ds.sem_nsems; i++) { 402 if (buf[i] > SEMVMX) { 403 free(buf); 404 update_semaphores(); 405 return ERANGE; 406 } 407 sem->sems[i].semval = buf[i]; 408 } 409 free(buf); 410 /* awaken if possible */ 411 update_semaphores(); 412 break; 413 case SETVAL: 414 val = (int) opt; 415 /* check write permission */ 416 if (!check_perm(&sem->semid_ds.sem_perm, who_e, 0222)) 417 return EACCES; 418 if (num < 0 || num >= sem->semid_ds.sem_nsems) 419 return EINVAL; 420 if (val < 0 || val > SEMVMX) 421 return ERANGE; 422 sem->sems[num].semval = val; 423 #ifdef DEBUG_SEM 424 printf("SEMCTL: SETVAL: %d %d\n", num, val); 425 #endif 426 sem->semid_ds.sem_ctime = time(NULL); 427 /* awaken if possible */ 428 update_semaphores(); 429 break; 430 default: 431 return EINVAL; 432 } 433 434 return OK; 435 } 436 437 /*===========================================================================* 438 * do_semop * 439 *===========================================================================*/ 440 int do_semop(message *m) 441 { 442 int id, i, j, r; 443 struct sembuf *sops; 444 unsigned int nsops; 445 struct sem_struct *sem; 446 int no_reply = 0; 447 448 id = m->m_lc_ipc_semop.id; 449 nsops = m->m_lc_ipc_semop.size; 450 451 r = EINVAL; 452 if (!(sem = sem_find_id(id))) 453 goto out; 454 455 if (nsops <= 0) 456 goto out; 457 458 r = E2BIG; 459 if (nsops > SEMOPM) 460 goto out; 461 462 /* check for read permission */ 463 r = EACCES; 464 if (!check_perm(&sem->semid_ds.sem_perm, who_e, 0444)) 465 goto out; 466 467 /* get the array from user application */ 468 r = ENOMEM; 469 sops = malloc(sizeof(struct sembuf) * nsops); 470 if (!sops) 471 goto out_free; 472 r = sys_datacopy(who_e, (vir_bytes) m->m_lc_ipc_semop.ops, 473 SELF, (vir_bytes) sops, 474 sizeof(struct sembuf) * nsops); 475 if (r != OK) { 476 r = EINVAL; 477 goto out_free; 478 } 479 480 #ifdef DEBUG_SEM 481 for (i = 0; i < nsops; i++) 482 printf("SEMOP: num:%d op:%d flg:%d\n", 483 sops[i].sem_num, sops[i].sem_op, sops[i].sem_flg); 484 #endif 485 /* check for value range */ 486 r = EFBIG; 487 for (i = 0; i < nsops; i++) 488 if (sops[i].sem_num >= sem->semid_ds.sem_nsems) 489 goto out_free; 490 491 /* check for duplicate number */ 492 r = EINVAL; 493 for (i = 0; i < nsops; i++) 494 for (j = i + 1; j < nsops; j++) 495 if (sops[i].sem_num == sops[j].sem_num) 496 goto out_free; 497 498 /* check for EAGAIN error */ 499 r = EAGAIN; 500 for (i = 0; i < nsops; i++) { 501 int op_n, val; 502 503 op_n = sops[i].sem_op; 504 val = sem->sems[sops[i].sem_num].semval; 505 506 if ((sops[i].sem_flg & IPC_NOWAIT) && 507 ((!op_n && val) || 508 (op_n < 0 && 509 -op_n > val))) 510 goto out_free; 511 512 } 513 /* there will be no errors left, so we can go ahead */ 514 for (i = 0; i < nsops; i++) { 515 struct semaphore *s; 516 int op_n; 517 518 s = &sem->sems[sops[i].sem_num]; 519 op_n = sops[i].sem_op; 520 521 s->sempid = getnpid(who_e); 522 523 if (op_n > 0) { 524 /* check for alter permission */ 525 r = EACCES; 526 if (!check_perm(&sem->semid_ds.sem_perm, who_e, 0222)) 527 goto out_free; 528 s->semval += sops[i].sem_op; 529 } else if (!op_n) { 530 if (s->semval) { 531 /* put the process asleep */ 532 s->semzcnt++; 533 s->zlist = realloc(s->zlist, sizeof(struct waiting) * s->semzcnt); 534 if (!s->zlist) { 535 printf("IPC: zero waiting list lost...\n"); 536 break; 537 } 538 s->zlist[s->semzcnt-1].who = who_e; 539 s->zlist[s->semzcnt-1].val = op_n; 540 541 #ifdef DEBUG_SEM 542 printf("SEMOP: Put into sleep... %d\n", who_e); 543 #endif 544 no_reply++; 545 } 546 } else { 547 /* check for alter permission */ 548 r = EACCES; 549 if (!check_perm(&sem->semid_ds.sem_perm, who_e, 0222)) 550 goto out_free; 551 if (s->semval >= -op_n) 552 s->semval += op_n; 553 else { 554 /* put the process asleep */ 555 s->semncnt++; 556 s->nlist = realloc(s->nlist, sizeof(struct waiting) * s->semncnt); 557 if (!s->nlist) { 558 printf("IPC: increase waiting list lost...\n"); 559 break; 560 } 561 s->nlist[s->semncnt-1].who = who_e; 562 s->nlist[s->semncnt-1].val = -op_n; 563 564 no_reply++; 565 } 566 } 567 } 568 569 r = OK; 570 out_free: 571 free(sops); 572 out: 573 /* if we reach here by errors 574 * or with no errors but we should reply back. 575 */ 576 if (r != OK || !no_reply) { 577 m->m_type = r; 578 579 ipc_sendnb(who_e, m); 580 } 581 582 /* awaken process if possible */ 583 update_semaphores(); 584 585 return 0; 586 } 587 588 /*===========================================================================* 589 * is_sem_nil * 590 *===========================================================================*/ 591 int is_sem_nil(void) 592 { 593 return (sem_list_nr == 0); 594 } 595 596 /*===========================================================================* 597 * sem_process_vm_notify * 598 *===========================================================================*/ 599 void sem_process_vm_notify(void) 600 { 601 endpoint_t pt; 602 int r; 603 604 while ((r = vm_query_exit(&pt)) >= 0) { 605 /* for each enpoint 'pt', check whether it's waiting... */ 606 remove_process(pt); 607 608 if (r == 0) 609 break; 610 } 611 if (r < 0) 612 printf("IPC: query exit error!\n"); 613 } 614 615