1 /* 2 * Copyright (c) 1994 Adam Glass and Charles Hannum. All rights reserved. 3 * Copyright (c) 2013 Larisa Grigore <larisagrigore@gmail.com> 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Adam Glass and Charles 16 * Hannum. 17 * 4. The names of the authors may not be used to endorse or promote products 18 * derived from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <sys/types.h> 35 #include <sys/param.h> 36 #include <sys/vmmeter.h> 37 #include <sys/wait.h> 38 #include <sys/queue.h> 39 #include <sys/mman.h> 40 41 #include <err.h> 42 #include <errno.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 #include <fcntl.h> 48 #include <time.h> 49 50 #include "limits.h" 51 #include "perm.h" 52 #include "utilsd.h" 53 54 #include "shmd.h" 55 #include "sysvipc_hash.h" 56 #include "sysvipc_sockets.h" 57 58 static struct shminfo shminfo = { 59 // 0, 60 SHMMIN, 61 SHMMNI, 62 SHMSEG 63 // 0 64 }; 65 66 /* Shared memory.*/ 67 static int shm_last_free, shm_committed, shmalloced; 68 int shm_nused; 69 static struct shmid_ds *shmsegs; 70 71 /* Message queues.*/ 72 extern struct msginfo msginfo; 73 74 extern struct hashtable *clientshash; 75 76 static int 77 create_sysv_file(struct shmget_msg *msg, size_t size, 78 struct shmid_ds *shmseg) { 79 char filename[FILENAME_MAX]; 80 int fd; 81 void *addr; 82 int nsems; 83 struct semid_pool *sems; 84 struct msqid_pool *msgq; 85 key_t key = msg->key; 86 int i; 87 88 errno = 0; 89 90 switch(msg->type) { 91 case SHMGET: 92 sprintf(filename, "%s/%s_%ld", DIRPATH, SHM_NAME, key); 93 break; 94 case SEMGET: 95 sprintf(filename, "%s/%s_%ld", DIRPATH, SEM_NAME, key); 96 break; 97 case MSGGET: 98 sprintf(filename, "%s/%s_%ld", DIRPATH, MSG_NAME, key); 99 break; 100 case UNDOGET: 101 sprintf(filename, "%s/%s_%ld", DIRPATH, UNDO_NAME, key); 102 break; 103 default: 104 return (-EINVAL); 105 } 106 107 fd = open(filename, O_RDWR | O_CREAT, 0666); 108 if (fd < 0) { 109 sysvd_print_err("create sysv file: open\n"); 110 goto out; 111 } 112 113 ftruncate(fd, size); 114 115 switch(msg->type) { 116 case SEMGET: 117 /* Map the semaphore to initialize it. */ 118 addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 119 //TODO modify 0 for more sems on a page 120 if (!addr) { 121 sysvd_print_err("create sysv file: mmap"); 122 goto error; 123 } 124 125 /* There is no need for any lock because all clients 126 * that try to access this segment are blocked until 127 * it becames ~SHMSEG_REMOVED. */ 128 sems = (struct semid_pool*)addr; 129 nsems = (msg->size - sizeof(struct semid_pool)) / 130 sizeof(struct sem); 131 sysvd_print("allocate %d sems\n", nsems); 132 133 /* Init lock. */ 134 #ifdef SYSV_RWLOCK 135 sysv_rwlock_init(&sems->rwlock); 136 #else 137 sysv_mutex_init(&sems->mutex); 138 #endif 139 /* Credentials are kept in shmid_ds structure. */ 140 sems->ds.sem_perm.seq = shmseg->shm_perm.seq; 141 sems->ds.sem_nsems = nsems; 142 sems->ds.sem_otime = 0; 143 //sems->ds.sem_ctime = time(NULL); 144 //semtot += nsems; 145 sems->gen = 0; 146 147 /* Initialize each sem. */ 148 memset(sems->ds.sem_base, 0, nsems + sizeof(struct sem)); 149 150 #ifdef SYSV_SEMS 151 int l; 152 for (l=0; l < nsems; l++) 153 sysv_mutex_init(&sems->ds.sem_base[l].sem_mutex); 154 #endif 155 156 munmap(addr, size); 157 158 break; 159 case MSGGET: 160 /* Map the message queue to initialize it. */ 161 addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 162 if (!addr) { 163 sysvd_print_err("create sysv file: mmap"); 164 goto error; 165 } 166 167 /* There is no need for any lock because all clients 168 * that try to access this segment are blocked until 169 * it becames ~SHMSEG_REMOVED. */ 170 msgq = (struct msqid_pool*)addr; //TODO 171 /*sysvd_print("Attention!!! : %ld %ld %ld %ld\n", 172 sizeof(struct msqid_pool), 173 sizeof(msgq->msghdrs), 174 sizeof(msgq->msgmaps), 175 sizeof(msgq->msgpool));*/ 176 177 /* Init lock. */ 178 #ifdef SYSV_RWLOCK 179 sysv_rwlock_init(&msgq->rwlock); 180 #else 181 sysv_mutex_init(&msgq->mutex); 182 #endif 183 /* In kernel implementation, this was done globally. */ 184 for (i = 0; i < msginfo.msgseg; i++) { 185 if (i > 0) 186 msgq->msgmaps[i-1].next = i; 187 msgq->msgmaps[i].next = -1; /* implies entry is available */ 188 } 189 msgq->free_msgmaps = 0; 190 msgq->nfree_msgmaps = msginfo.msgseg; 191 192 for (i = 0; i < msginfo.msgtql; i++) { 193 msgq->msghdrs[i].msg_type = 0; 194 if (i > 0) 195 msgq->msghdrs[i-1].msg_next = i; 196 msgq->msghdrs[i].msg_next = -1; 197 } 198 msgq->free_msghdrs = 0; 199 200 /* Credentials are kept in shmid_ds structure. */ 201 msgq->ds.msg_perm.seq = shmseg->shm_perm.seq; 202 msgq->ds.first.msg_first_index = -1; 203 msgq->ds.last.msg_last_index = -1; 204 msgq->ds.msg_cbytes = 0; 205 msgq->ds.msg_qnum = 0; 206 msgq->ds.msg_qbytes = msginfo.msgmnb; 207 msgq->ds.msg_lspid = 0; 208 msgq->ds.msg_lrpid = 0; 209 msgq->ds.msg_stime = 0; 210 msgq->ds.msg_rtime = 0; 211 212 munmap(addr, size); 213 214 break; 215 default: 216 break; 217 } 218 219 unlink(filename); 220 out: 221 return (fd); 222 error: 223 close(fd); 224 return (-1); 225 } 226 227 /* Install for the client the file corresponding to fd. */ 228 static int 229 install_fd_client(pid_t pid, int fd) { 230 int ret; 231 struct client *cl = _hash_lookup(clientshash, pid); 232 if (!cl) { 233 sysvd_print_err("no client entry for pid = %d\n", pid); 234 return (-1); 235 } 236 237 ret = send_fd(cl->sock, fd); 238 if (ret < 0) { 239 sysvd_print_err("can not send fd to client %d\n", pid); 240 return (-1); 241 } 242 243 return (0); 244 } 245 246 static int 247 shm_find_segment_by_key(key_t key) 248 { 249 int i; 250 251 for (i = 0; i < shmalloced; i++) { 252 if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) && 253 shmsegs[i].shm_perm.key == key) 254 return (i); 255 } 256 return (-1); 257 } 258 259 static struct shmid_ds * 260 shm_find_segment_by_shmid(int shmid) 261 { 262 int segnum; 263 struct shmid_ds *shmseg; 264 265 segnum = IPCID_TO_IX(shmid); 266 if (segnum < 0 || segnum >= shmalloced) { 267 sysvd_print_err("segnum out of range\n"); 268 return (NULL); 269 } 270 271 shmseg = &shmsegs[segnum]; 272 if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED)) 273 != SHMSEG_ALLOCATED || 274 shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid)) { 275 sysvd_print("segment most probably removed\n"); 276 return (NULL); 277 } 278 return (shmseg); 279 } 280 281 /* Remove a shared memory segment. */ 282 static void 283 shm_deallocate_segment(int segnum) 284 { 285 size_t size; 286 struct shmid_ds *shmseg = &shmsegs[segnum]; 287 struct shm_handle *internal = 288 (struct shm_handle *)shmseg->shm_internal; 289 // int nsems; 290 291 sysvd_print("deallocate segment %d\n", segnum); 292 293 size = round_page(shmseg->shm_segsz); 294 295 #if 0 296 if (internal->type == SEMGET) { 297 nsems = (shmseg->shm_segsz - sizeof(struct semid_pool)) / 298 sizeof(struct sem); 299 semtot -= nsems; 300 sysvd_print("freed %d sems\n", nsems); 301 } 302 #endif 303 304 /* Close the corresponding file. */ 305 close(internal->fd); 306 307 /* Free other resources. */ 308 free(shmseg->shm_internal); 309 shmseg->shm_internal = NULL; 310 shm_committed -= btoc(size); 311 shm_nused--; 312 313 shmseg->shm_perm.mode = SHMSEG_FREE; 314 } 315 316 static void *map_seg(int); 317 static int munmap_seg(int, void *); 318 319 /* In sem and msg case notify the other processes that use it. */ 320 static void 321 mark_segment_removed(int shmid, int type) { 322 struct semid_pool *semaptr; 323 struct msqid_pool *msgq; 324 325 switch (type) { 326 case SEMGET: 327 semaptr = (struct semid_pool *)map_seg(shmid); 328 #ifdef SYSV_RWLOCK 329 sysv_rwlock_wrlock(&semaptr->rwlock); 330 #else 331 sysv_mutex_lock(&semaptr->mutex); 332 #endif 333 semaptr->gen = -1; 334 335 /* It is not necessary to wake waiting threads because 336 * if the group of semaphores is acquired by a thread, 337 * the smaptr lock is held, so it is impossible to 338 * reach this point. 339 */ 340 #ifdef SYSV_RWLOCK 341 sysv_rwlock_unlock(&semaptr->rwlock); 342 #else 343 sysv_mutex_unlock(&semaptr->mutex); 344 #endif 345 munmap_seg(shmid, semaptr); 346 break; 347 case MSGGET : 348 msgq = (struct msqid_pool*)map_seg(shmid); 349 #ifdef SYSV_RWLOCK 350 sysv_rwlock_wrlock(&msgq->rwlock); 351 #else 352 sysv_mutex_lock(&msgq->mutex); 353 #endif 354 msgq->gen = -1; 355 356 #ifdef SYSV_RWLOCK 357 sysv_rwlock_unlock(&msgq->rwlock); 358 #else 359 sysv_mutex_unlock(&msgq->mutex); 360 #endif 361 munmap_seg(shmid, msgq); 362 break; 363 default: 364 break; 365 } 366 } 367 368 /* Get the id of an existing shared memory segment. */ 369 static int 370 shmget_existing(struct shmget_msg *shmget_msg, int mode, 371 int segnum, struct cmsgcred *cred) 372 { 373 struct shmid_ds *shmseg; 374 int error; 375 376 shmseg = &shmsegs[segnum]; 377 if (shmseg->shm_perm.mode & SHMSEG_REMOVED) { 378 /* 379 * This segment is in the process of being allocated. Wait 380 * until it's done, and look the key up again (in case the 381 * allocation failed or it was freed). 382 */ 383 //TODO Maybe it will be necessary if the daemon is multithreading 384 /*shmseg->shm_perm.mode |= SHMSEG_WANTED; 385 error = tsleep((caddr_t)shmseg, PCATCH, "shmget", 0); 386 if (error) 387 return error; 388 return EAGAIN;*/ 389 } 390 if ((shmget_msg->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL)) 391 return (-EEXIST); 392 error = ipcperm(cred, &shmseg->shm_perm, mode); 393 if (error) 394 return (-error); 395 if (shmget_msg->size && (shmget_msg->size > shmseg->shm_segsz)) 396 return (-EINVAL); 397 return (IXSEQ_TO_IPCID(segnum, shmseg->shm_perm)); 398 } 399 400 /* Create a shared memory segment and return the id. */ 401 static int 402 shmget_allocate_segment(pid_t pid, struct shmget_msg *shmget_msg, 403 int mode, struct cmsgcred *cred) 404 { 405 int i, segnum, shmid; 406 size_t size; 407 struct shmid_ds *shmseg; 408 struct shm_handle *handle; 409 #if 0 410 /* It is possible after a process calls exec(). 411 * We don't create another segment but return the old one 412 * with all information. 413 * This segment is destroyed only when process dies. 414 * */ 415 if (shmget_msg->type == UNDOGET) { 416 struct client *cl= _hash_lookup(clientshash, pid); 417 if (cl->undoid != -1) 418 return cl->undoid; 419 } 420 #endif 421 if ((long)shmget_msg->size < shminfo.shmmin) 422 //|| (long)shmget_msg->size > shminfo.shmmax) 423 /* There is no need to check the max limit, 424 * the operating system do this for us. 425 */ 426 return (-EINVAL); 427 if (shm_nused >= shminfo.shmmni) /* any shmids left? */ 428 return (-ENOSPC); 429 430 /* Compute the size of the segment. */ 431 size = round_page(shmget_msg->size); 432 433 /* Find a free entry in the shmsegs vector. */ 434 if (shm_last_free < 0) { 435 // shmrealloc(); /* maybe expand the shmsegs[] array */ 436 for (i = 0; i < shmalloced; i++) { 437 if (shmsegs[i].shm_perm.mode & SHMSEG_FREE) 438 break; 439 } 440 if (i == shmalloced) { 441 sysvd_print("i == shmalloced\n"); 442 return (-ENOSPC); 443 } 444 segnum = i; 445 } else { 446 segnum = shm_last_free; 447 shm_last_free = -1; 448 } 449 shmseg = &shmsegs[segnum]; 450 /* 451 * In case we sleep in malloc(), mark the segment present but deleted 452 * so that noone else tries to create the same key. 453 */ 454 shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED; 455 shmseg->shm_perm.key = shmget_msg->key; 456 shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff; 457 458 /* Create the file for the shared memory segment. */ 459 handle = shmseg->shm_internal = malloc(sizeof(struct shm_handle)); 460 handle->type = shmget_msg->type; 461 handle->fd = create_sysv_file(shmget_msg, size, shmseg); 462 if (handle->fd == -1) { 463 free(handle); 464 handle = NULL; 465 shmseg->shm_perm.mode = SHMSEG_FREE; 466 shm_last_free = segnum; 467 errno = -ENFILE; 468 return (-1); 469 } 470 471 LIST_INIT(&handle->attached_list); 472 473 if (handle->fd < 0) { 474 free(shmseg->shm_internal); 475 shmseg->shm_internal = NULL; 476 shm_last_free = segnum; 477 shmseg->shm_perm.mode = SHMSEG_FREE; 478 return (-errno); 479 } 480 481 /* Get the id. */ 482 shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 483 484 shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cmcred_euid; 485 shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cmcred_gid; 486 shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) | 487 (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; 488 489 shmseg->shm_cpid = pid; 490 shmseg->shm_lpid = shmseg->shm_nattch = 0; 491 shmseg->shm_atime = shmseg->shm_dtime = 0; 492 shmseg->shm_ctime = time(NULL); 493 494 shmseg->shm_segsz = shmget_msg->size; 495 shm_committed += btoc(size); 496 shm_nused++; 497 498 if (shmseg->shm_perm.mode & SHMSEG_WANTED) { 499 /* 500 * Somebody else wanted this key while we were asleep. Wake 501 * them up now. 502 */ 503 shmseg->shm_perm.mode &= ~SHMSEG_WANTED; 504 //TODO multithreading 505 //wakeup((caddr_t)shmseg); 506 } 507 shmseg->shm_perm.mode &= ~SHMSEG_REMOVED; 508 509 if (shmget_msg->type == UNDOGET) { 510 /* The file is used by daemon when clients terminates 511 * and sem_undo resources must be cleaned. 512 */ 513 struct client *cl= _hash_lookup(clientshash, pid); 514 cl->undoid = shmid; 515 } 516 517 return (shmid); 518 } 519 520 /* Handle a shmget() request. */ 521 int 522 handle_shmget(pid_t pid, struct shmget_msg *shmget_msg, 523 struct cmsgcred *cred ) { 524 int segnum, mode, error; 525 struct shmid_ds *shmseg; 526 struct shm_handle *handle; 527 528 //if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL) 529 // return (ENOSYS); 530 mode = shmget_msg->shmflg & ACCESSPERMS; 531 532 sysvd_print("ask for key = %ld\n", shmget_msg->key); 533 shmget_msg->key = (shmget_msg->key & 0x3FFF) | 534 (shmget_msg->type << 30); 535 sysvd_print("ask for key = %ld\n", shmget_msg->key); 536 537 if (shmget_msg->key != IPC_PRIVATE) { 538 //again: 539 segnum = shm_find_segment_by_key(shmget_msg->key); 540 if (segnum >= 0) { 541 error = shmget_existing(shmget_msg, mode, segnum, cred); 542 //TODO if daemon is multithreading 543 //if (error == EAGAIN) 544 // goto again; 545 goto done; 546 } 547 if ((shmget_msg->shmflg & IPC_CREAT) == 0) { 548 error = -ENOENT; 549 goto done_err; 550 } 551 } 552 error = shmget_allocate_segment(pid, shmget_msg, mode, cred); 553 sysvd_print("allocate segment = %d\n", error); 554 done: 555 /* 556 * Install to th client the file corresponding to the 557 * shared memory segment. 558 * client_fd is the file descriptor added in the client 559 * files table. 560 */ 561 shmseg = shm_find_segment_by_shmid(error); 562 if (shmseg == NULL) { 563 sysvd_print_err("can not find segment by shmid\n"); 564 return (-1); 565 } 566 567 handle = (struct shm_handle *)shmseg->shm_internal; 568 if (install_fd_client(pid, handle->fd) != 0) 569 error = errno; 570 done_err: 571 return (error); 572 573 } 574 575 /* Handle a shmat() request. */ 576 int 577 handle_shmat(pid_t pid, struct shmat_msg *shmat_msg, 578 struct cmsgcred *cred ) { 579 int error; 580 int fd; 581 struct shmid_ds *shmseg; 582 struct pid_attached *pidatt; 583 struct shm_handle *handle; 584 size_t new_size = shmat_msg->size; 585 struct client *cl; 586 struct id_attached *idatt; 587 588 /*if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL) 589 return (ENOSYS); 590 591 again:*/ 592 shmseg = shm_find_segment_by_shmid(shmat_msg->shmid); 593 if (shmseg == NULL) { 594 sysvd_print_err("shmat error: segment was not found\n"); 595 error = EINVAL; 596 goto done; 597 } 598 error = ipcperm(cred, &shmseg->shm_perm, 599 (shmat_msg->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); 600 if (error) 601 goto done; 602 603 handle = shmseg->shm_internal; 604 605 if (shmat_msg->size > shmseg->shm_segsz) { 606 if (handle->type != UNDOGET) { 607 error = EINVAL; 608 goto done; 609 } 610 611 fd = ((struct shm_handle*)shmseg->shm_internal)->fd; 612 ftruncate(fd, round_page(new_size)); 613 shmseg->shm_segsz = new_size; 614 } 615 616 shmseg->shm_lpid = pid; 617 shmseg->shm_atime = time(NULL); 618 619 if (handle->type != UNDOGET) 620 shmseg->shm_nattch++; 621 else 622 shmseg->shm_nattch = 1; /* Only a process calls shmat and 623 only once. If it does it for more than once that is because 624 it called exec() and reinitialized the undo segment. */ 625 626 /* Insert the pid in the segment list of attaced pids. 627 * The list is checked in handle_shmdt so that only 628 * attached pids can dettached from this segment. 629 */ 630 sysvd_print("nattch = %d pid = %d\n", 631 shmseg->shm_nattch, pid); 632 633 pidatt = malloc(sizeof(*pidatt)); 634 pidatt->pid = pid; 635 LIST_INSERT_HEAD(&handle->attached_list, pidatt, link); 636 637 /* Add the segment at the list of attached segments of the client. 638 * It is used when the process finishes its execution. The daemon 639 * walks through the list to dettach the segments. 640 */ 641 idatt = malloc(sizeof(*idatt)); 642 idatt->shmid = shmat_msg->shmid; 643 cl = _hash_lookup(clientshash, pid); 644 LIST_INSERT_HEAD(&cl->ids_attached, idatt, link); 645 646 return (0); 647 done: 648 return (error); 649 } 650 651 /* Handle a shmdt() request. */ 652 int 653 handle_shmdt(pid_t pid, int shmid) { 654 struct shmid_ds *shmseg; 655 int segnum; 656 struct shm_handle *handle; 657 struct pid_attached *pidatt; 658 struct id_attached *idatt; 659 struct client *cl; 660 661 sysvd_print("shmdt pid %d shmid %d\n", pid, shmid); 662 /*if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL) 663 return (ENOSYS); 664 */ 665 666 segnum = IPCID_TO_IX(shmid); 667 shmseg = &shmsegs[segnum]; 668 handle = shmseg->shm_internal; 669 670 /* Check if pid is attached. */ 671 LIST_FOREACH(pidatt, &handle->attached_list, link) 672 if (pidatt->pid == pid) 673 break; 674 if (!pidatt) { 675 sysvd_print_err("process %d is not attached to %d (1)\n", 676 pid, shmid); 677 return (EINVAL); 678 } 679 LIST_REMOVE(pidatt, link); 680 681 /* Remove the segment from the list of attached segments of the pid.*/ 682 cl = _hash_lookup(clientshash, pid); 683 LIST_FOREACH(idatt, &cl->ids_attached, link) 684 if (idatt->shmid == shmid) 685 break; 686 if (!idatt) { 687 sysvd_print_err("process %d is not attached to %d (2)\n", 688 pid, shmid); 689 return (EINVAL); 690 } 691 LIST_REMOVE(idatt, link); 692 693 shmseg->shm_dtime = time(NULL); 694 695 /* If no other process attaced remove the segment. */ 696 if ((--shmseg->shm_nattch <= 0) && 697 (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { 698 shm_deallocate_segment(segnum); 699 shm_last_free = segnum; 700 } 701 702 return (0); 703 } 704 705 /* Handle a shmctl() request. */ 706 int 707 handle_shmctl(struct shmctl_msg *shmctl_msg, 708 struct cmsgcred *cred ) { 709 int error = 0; 710 struct shmid_ds *shmseg, *inbuf; 711 712 /* if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL) 713 return (ENOSYS); 714 */ 715 shmseg = shm_find_segment_by_shmid(shmctl_msg->shmid); 716 717 if (shmseg == NULL) { 718 error = EINVAL; 719 goto done; 720 } 721 722 switch (shmctl_msg->cmd) { 723 case IPC_STAT: 724 sysvd_print("IPC STAT\n"); 725 error = ipcperm(cred, &shmseg->shm_perm, IPC_R); 726 if (error) { 727 sysvd_print("IPC_STAT not allowed\n"); 728 break; 729 } 730 shmctl_msg->buf = *shmseg; 731 break; 732 case IPC_SET: 733 sysvd_print("IPC SET\n"); 734 error = ipcperm(cred, &shmseg->shm_perm, IPC_M); 735 if (error) { 736 sysvd_print("IPC_SET not allowed\n"); 737 break; 738 } 739 inbuf = &shmctl_msg->buf; 740 741 shmseg->shm_perm.uid = inbuf->shm_perm.uid; 742 shmseg->shm_perm.gid = inbuf->shm_perm.gid; 743 shmseg->shm_perm.mode = 744 (shmseg->shm_perm.mode & ~ACCESSPERMS) | 745 (inbuf->shm_perm.mode & ACCESSPERMS); 746 shmseg->shm_ctime = time(NULL); 747 break; 748 case IPC_RMID: 749 sysvd_print("IPC RMID shmid = %d\n", 750 shmctl_msg->shmid); 751 error = ipcperm(cred, &shmseg->shm_perm, IPC_M); 752 if (error) { 753 sysvd_print("IPC_RMID not allowed\n"); 754 break; 755 } 756 shmseg->shm_perm.key = IPC_PRIVATE; 757 shmseg->shm_perm.mode |= SHMSEG_REMOVED; 758 if (shmseg->shm_nattch <= 0) { 759 shm_deallocate_segment(IPCID_TO_IX(shmctl_msg->shmid)); 760 shm_last_free = IPCID_TO_IX(shmctl_msg->shmid); 761 } 762 else { 763 /* In sem and msg cases, other process must be 764 * noticed about the removal. */ 765 struct shm_handle *internal = 766 (struct shm_handle *)shmseg->shm_internal; 767 mark_segment_removed(shmctl_msg->shmid, 768 internal->type); 769 } 770 break; 771 #if 0 772 case SHM_LOCK: 773 case SHM_UNLOCK: 774 #endif 775 default: 776 error = EINVAL; 777 break; 778 } 779 done: 780 return (error); 781 782 } 783 784 /* Function used by daemon to map a sysv resource. */ 785 static void * 786 map_seg(int shmid) { 787 struct shmid_ds *shmseg; 788 struct shm_handle *internal; 789 790 int fd; 791 size_t size; 792 void *addr; 793 794 shmseg = shm_find_segment_by_shmid(shmid); 795 if (!shmseg) { 796 sysvd_print_err("map_seg error:" 797 "semid %d not found\n", shmid); 798 return (NULL); 799 } 800 801 internal = (struct shm_handle *)shmseg->shm_internal; 802 if (!internal) { 803 sysvd_print_err("map_seg error: internal for" 804 "semid %d not found\n", shmid); 805 return (NULL); 806 } 807 808 fd = internal->fd; 809 810 size = round_page(shmseg->shm_segsz); 811 812 addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 813 if (!addr) { 814 sysvd_print_err("map_seg: error mmap semid = %d\n", shmid); 815 return (NULL); 816 } 817 818 return (addr); 819 } 820 821 /* Function used by daemon to munmap a sysv resource. */ 822 static int 823 munmap_seg(int shmid, void *addr) { 824 struct shmid_ds *shmseg; 825 struct shm_handle *internal; 826 827 size_t size; 828 829 shmseg = shm_find_segment_by_shmid(shmid); 830 if (!shmseg) { 831 sysvd_print_err("munmap_seg error:" 832 "semid %d not found\n", shmid); 833 return (-1); 834 } 835 836 internal = (struct shm_handle *)shmseg->shm_internal; 837 if (!internal) { 838 sysvd_print_err("munmap_seg error: internal for" 839 "semid %d not found\n", shmid); 840 return (-1); 841 } 842 843 size = round_page(shmseg->shm_segsz); 844 munmap(addr, size); 845 846 return (0); 847 } 848 849 void 850 shminit(void) { 851 int i; 852 853 shmalloced = shminfo.shmmni; 854 shmsegs = malloc(shmalloced * sizeof(shmsegs[0])); 855 for (i = 0; i < shmalloced; i++) { 856 shmsegs[i].shm_perm.mode = SHMSEG_FREE; 857 shmsegs[i].shm_perm.seq = 0; 858 } 859 shm_last_free = 0; 860 shm_nused = 0; 861 shm_committed = 0; 862 863 /* 864 * msginfo.msgssz should be a power of two for efficiency reasons. 865 * It is also pretty silly if msginfo.msgssz is less than 8 866 * or greater than about 256 so ... 867 */ 868 i = 8; 869 while (i < 1024 && i != msginfo.msgssz) 870 i <<= 1; 871 if (i != msginfo.msgssz) { 872 sysvd_print_err("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz, 873 msginfo.msgssz); 874 sysvd_print_err("msginfo.msgssz not a small power of 2"); 875 exit(-1); 876 } 877 msginfo.msgmax = msginfo.msgseg * msginfo.msgssz; 878 } 879 880 /*static void 881 shmfree(void) { 882 free(shmsegs); 883 }*/ 884 885 int 886 semexit(int undoid) { 887 struct sem_undo *suptr; 888 struct sem *semptr; 889 struct shmid_ds *undoseg; 890 891 if (undoid < 0) { 892 return (-1); 893 } 894 895 undoseg = shm_find_segment_by_shmid(undoid); 896 /* The UNDO segment must be mapped by only one segment. */ 897 if (undoseg->shm_nattch != 1) { 898 sysvd_print_err("undo segment mapped by more" 899 "than one process\n"); 900 exit(-1); 901 } 902 903 suptr = (struct sem_undo *)map_seg(undoid); 904 if (suptr == NULL) { 905 sysvd_print_err("no %d undo segment found\n", undoid); 906 return (-1); 907 } 908 909 /* No locking mechanism is required because only the 910 * client and the daemon can access the UNDO segment. 911 * At this moment the client is disconnected so only 912 * the daemon can modify this segment. 913 */ 914 while (suptr->un_cnt) { 915 struct semid_pool *semaptr; 916 int semid; 917 int semnum; 918 int adjval; 919 int ix; 920 921 ix = suptr->un_cnt - 1; 922 semid = suptr->un_ent[ix].un_id; 923 semnum = suptr->un_ent[ix].un_num; 924 adjval = suptr->un_ent[ix].un_adjval; 925 926 semaptr = (struct semid_pool *)map_seg(semid); 927 if (!semaptr) { 928 return (-1); 929 } 930 931 /* Was it removed? */ 932 if (semaptr->gen == -1 || 933 semaptr->ds.sem_perm.seq != IPCID_TO_SEQ(semid) || 934 (semaptr->ds.sem_perm.mode & SHMSEG_ALLOCATED) == 0) { 935 --suptr->un_cnt; 936 sysvd_print_err("semexit - semid not allocated\n"); 937 continue; 938 } 939 if (semnum >= semaptr->ds.sem_nsems) { 940 --suptr->un_cnt; 941 sysvd_print_err("semexit - semnum out of range\n"); 942 continue; 943 } 944 945 #ifdef SYSV_RWLOCK 946 #ifdef SYSV_SEMS 947 sysv_rwlock_rdlock(&semaptr->rwlock); 948 #else 949 sysv_rwlock_wrlock(&semaptr->rwlock); 950 #endif //SYSV_SEMS 951 #else 952 sysv_mutex_lock(&semaptr->mutex); 953 /* Nobody can remove the semaphore beteen the check and the 954 * lock acquisition because it must first send a IPC_RMID 955 * to me and I will process that after finishing this function. 956 */ 957 #endif //SYSV_RWLOCK 958 semptr = &semaptr->ds.sem_base[semnum]; 959 #ifdef SYSV_SEMS 960 sysv_mutex_lock(&semptr->sem_mutex); 961 #endif 962 if (ix == suptr->un_cnt - 1 && 963 semid == suptr->un_ent[ix].un_id && 964 semnum == suptr->un_ent[ix].un_num && 965 adjval == suptr->un_ent[ix].un_adjval) { 966 --suptr->un_cnt; 967 968 if (adjval < 0) { 969 if (semptr->semval < -adjval) 970 semptr->semval = 0; 971 else 972 semptr->semval += adjval; 973 } else { 974 semptr->semval += adjval; 975 } 976 /* TODO multithreaded daemon: 977 * Check again if the semaphore was removed and do 978 * not wake anyone if it was.*/ 979 umtx_wakeup((int *)&semptr->semval, 0); 980 } 981 #ifdef SYSV_SEMS 982 sysv_mutex_unlock(&semptr->sem_mutex); 983 #endif 984 985 #ifdef SYSV_RWLOCK 986 sysv_rwlock_unlock(&semaptr->rwlock); 987 #else 988 sysv_mutex_unlock(&semaptr->mutex); 989 #endif 990 munmap_seg(semid, semaptr); 991 } 992 993 munmap_seg(undoid, suptr); 994 return (0); 995 } 996 997 void 998 shmexit(struct client *cl) { 999 struct id_attached *idatt; 1000 1001 while (!LIST_EMPTY(&cl->ids_attached)) { 1002 idatt = LIST_FIRST(&cl->ids_attached); 1003 handle_shmdt(cl->pid, idatt->shmid); 1004 } 1005 } 1006