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