2 * Copyright (c) 1994 Adam Glass and Charles Hannum. All rights reserved.
3 * Copyright (c) 2013 Larisa Grigore <larisagrigore@gmail.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
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.
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.
32 #include <sys/types.h>
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/vmmeter.h>
38 #include <sys/queue.h>
55 #include "sysvipc_hash.h"
56 #include "sysvipc_sockets.h"
58 static struct shminfo shminfo
= {
67 static int shm_last_free
, shm_committed
, shmalloced
;
69 static struct shmid_ds
*shmsegs
;
72 extern struct msginfo msginfo
;
74 extern struct hashtable
*clientshash
;
77 create_sysv_file(struct shmget_msg
*msg
, size_t size
,
78 struct shmid_ds
*shmseg
) {
79 char filename
[FILENAME_MAX
];
83 struct semid_pool
*sems
;
84 struct msqid_pool
*msgq
;
92 sprintf(filename
, "%s/%s_%ld", DIRPATH
, SHM_NAME
, key
);
95 sprintf(filename
, "%s/%s_%ld", DIRPATH
, SEM_NAME
, key
);
98 sprintf(filename
, "%s/%s_%ld", DIRPATH
, MSG_NAME
, key
);
101 sprintf(filename
, "%s/%s_%ld", DIRPATH
, UNDO_NAME
, key
);
107 fd
= open(filename
, O_RDWR
| O_CREAT
, 0666);
109 sysvd_print_err("create sysv file: open\n");
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
121 sysvd_print_err("create sysv file: mmap");
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
)) /
131 sysvd_print("allocate %d sems\n", nsems
);
135 sysv_rwlock_init(&sems
->rwlock
);
137 sysv_mutex_init(&sems
->mutex
);
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);
147 /* Initialize each sem. */
148 memset(sems
->ds
.sem_base
, 0, nsems
+ sizeof(struct sem
));
152 for (l
=0; l
< nsems
; l
++)
153 sysv_mutex_init(&sems
->ds
.sem_base
[l
].sem_mutex
);
160 /* Map the message queue to initialize it. */
161 addr
= mmap(NULL
, size
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, 0);
163 sysvd_print_err("create sysv file: mmap");
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));*/
179 sysv_rwlock_init(&msgq
->rwlock
);
181 sysv_mutex_init(&msgq
->mutex
);
183 /* In kernel implementation, this was done globally. */
184 for (i
= 0; i
< msginfo
.msgseg
; i
++) {
186 msgq
->msgmaps
[i
-1].next
= i
;
187 msgq
->msgmaps
[i
].next
= -1; /* implies entry is available */
189 msgq
->free_msgmaps
= 0;
190 msgq
->nfree_msgmaps
= msginfo
.msgseg
;
192 for (i
= 0; i
< msginfo
.msgtql
; i
++) {
193 msgq
->msghdrs
[i
].msg_type
= 0;
195 msgq
->msghdrs
[i
-1].msg_next
= i
;
196 msgq
->msghdrs
[i
].msg_next
= -1;
198 msgq
->free_msghdrs
= 0;
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;
227 /* Install for the client the file corresponding to fd. */
229 install_fd_client(pid_t pid
, int fd
) {
231 struct client
*cl
= _hash_lookup(clientshash
, pid
);
233 sysvd_print_err("no client entry for pid = %d\n", pid
);
237 ret
= send_fd(cl
->sock
, fd
);
239 sysvd_print_err("can not send fd to client %d\n", pid
);
247 shm_find_segment_by_key(key_t key
)
251 for (i
= 0; i
< shmalloced
; i
++) {
252 if ((shmsegs
[i
].shm_perm
.mode
& SHMSEG_ALLOCATED
) &&
253 shmsegs
[i
].shm_perm
.key
== key
)
259 static struct shmid_ds
*
260 shm_find_segment_by_shmid(int shmid
)
263 struct shmid_ds
*shmseg
;
265 segnum
= IPCID_TO_IX(shmid
);
266 if (segnum
< 0 || segnum
>= shmalloced
) {
267 sysvd_print_err("segnum out of range\n");
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");
281 /* Remove a shared memory segment. */
283 shm_deallocate_segment(int segnum
)
286 struct shmid_ds
*shmseg
= &shmsegs
[segnum
];
287 struct shm_handle
*internal
=
288 (struct shm_handle
*)shmseg
->shm_internal
;
291 sysvd_print("deallocate segment %d\n", segnum
);
293 size
= round_page(shmseg
->shm_segsz
);
296 if (internal
->type
== SEMGET
) {
297 nsems
= (shmseg
->shm_segsz
- sizeof(struct semid_pool
)) /
300 sysvd_print("freed %d sems\n", nsems
);
304 /* Close the corresponding file. */
307 /* Free other resources. */
308 free(shmseg
->shm_internal
);
309 shmseg
->shm_internal
= NULL
;
310 shm_committed
-= btoc(size
);
313 shmseg
->shm_perm
.mode
= SHMSEG_FREE
;
316 static void *map_seg(int);
317 static int munmap_seg(int, void *);
319 /* In sem and msg case notify the other processes that use it. */
321 mark_segment_removed(int shmid
, int type
) {
322 struct semid_pool
*semaptr
;
323 struct msqid_pool
*msgq
;
327 semaptr
= (struct semid_pool
*)map_seg(shmid
);
329 sysv_rwlock_wrlock(&semaptr
->rwlock
);
331 sysv_mutex_lock(&semaptr
->mutex
);
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
341 sysv_rwlock_unlock(&semaptr
->rwlock
);
343 sysv_mutex_unlock(&semaptr
->mutex
);
345 munmap_seg(shmid
, semaptr
);
348 msgq
= (struct msqid_pool
*)map_seg(shmid
);
350 sysv_rwlock_wrlock(&msgq
->rwlock
);
352 sysv_mutex_lock(&msgq
->mutex
);
357 sysv_rwlock_unlock(&msgq
->rwlock
);
359 sysv_mutex_unlock(&msgq
->mutex
);
361 munmap_seg(shmid
, msgq
);
368 /* Get the id of an existing shared memory segment. */
370 shmget_existing(struct shmget_msg
*shmget_msg
, int mode
,
371 int segnum
, struct cmsgcred
*cred
)
373 struct shmid_ds
*shmseg
;
376 shmseg
= &shmsegs
[segnum
];
377 if (shmseg
->shm_perm
.mode
& SHMSEG_REMOVED
) {
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).
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);
390 if ((shmget_msg
->shmflg
& (IPC_CREAT
| IPC_EXCL
)) == (IPC_CREAT
| IPC_EXCL
))
392 error
= ipcperm(cred
, &shmseg
->shm_perm
, mode
);
395 if (shmget_msg
->size
&& (shmget_msg
->size
> shmseg
->shm_segsz
))
397 return (IXSEQ_TO_IPCID(segnum
, shmseg
->shm_perm
));
400 /* Create a shared memory segment and return the id. */
402 shmget_allocate_segment(pid_t pid
, struct shmget_msg
*shmget_msg
,
403 int mode
, struct cmsgcred
*cred
)
405 int i
, segnum
, shmid
;
407 struct shmid_ds
*shmseg
;
408 struct shm_handle
*handle
;
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.
415 if (shmget_msg
->type
== UNDOGET
) {
416 struct client
*cl
= _hash_lookup(clientshash
, pid
);
417 if (cl
->undoid
!= -1)
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.
427 if (shm_nused
>= shminfo
.shmmni
) /* any shmids left? */
430 /* Compute the size of the segment. */
431 size
= round_page(shmget_msg
->size
);
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
)
440 if (i
== shmalloced
) {
441 sysvd_print("i == shmalloced\n");
446 segnum
= shm_last_free
;
449 shmseg
= &shmsegs
[segnum
];
451 * In case we sleep in malloc(), mark the segment present but deleted
452 * so that noone else tries to create the same key.
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;
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) {
465 shmseg
->shm_perm
.mode
= SHMSEG_FREE
;
466 shm_last_free
= segnum
;
471 LIST_INIT(&handle
->attached_list
);
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
;
482 shmid
= IXSEQ_TO_IPCID(segnum
, shmseg
->shm_perm
);
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
;
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
);
494 shmseg
->shm_segsz
= shmget_msg
->size
;
495 shm_committed
+= btoc(size
);
498 if (shmseg
->shm_perm
.mode
& SHMSEG_WANTED
) {
500 * Somebody else wanted this key while we were asleep. Wake
503 shmseg
->shm_perm
.mode
&= ~SHMSEG_WANTED
;
504 //TODO multithreading
505 //wakeup((caddr_t)shmseg);
507 shmseg
->shm_perm
.mode
&= ~SHMSEG_REMOVED
;
509 if (shmget_msg
->type
== UNDOGET
) {
510 /* The file is used by daemon when clients terminates
511 * and sem_undo resources must be cleaned.
513 struct client
*cl
= _hash_lookup(clientshash
, pid
);
520 /* Handle a shmget() request. */
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
;
528 //if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL)
530 mode
= shmget_msg
->shmflg
& ACCESSPERMS
;
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
);
537 if (shmget_msg
->key
!= IPC_PRIVATE
) {
539 segnum
= shm_find_segment_by_key(shmget_msg
->key
);
541 error
= shmget_existing(shmget_msg
, mode
, segnum
, cred
);
542 //TODO if daemon is multithreading
543 //if (error == EAGAIN)
547 if ((shmget_msg
->shmflg
& IPC_CREAT
) == 0) {
552 error
= shmget_allocate_segment(pid
, shmget_msg
, mode
, cred
);
553 sysvd_print("allocate segment = %d\n", error
);
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
561 shmseg
= shm_find_segment_by_shmid(error
);
562 if (shmseg
== NULL
) {
563 sysvd_print_err("can not find segment by shmid\n");
567 handle
= (struct shm_handle
*)shmseg
->shm_internal
;
568 if (install_fd_client(pid
, handle
->fd
) != 0)
575 /* Handle a shmat() request. */
577 handle_shmat(pid_t pid
, struct shmat_msg
*shmat_msg
,
578 struct cmsgcred
*cred
) {
581 struct shmid_ds
*shmseg
;
582 struct pid_attached
*pidatt
;
583 struct shm_handle
*handle
;
584 size_t new_size
= shmat_msg
->size
;
586 struct id_attached
*idatt
;
588 /*if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL)
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");
598 error
= ipcperm(cred
, &shmseg
->shm_perm
,
599 (shmat_msg
->shmflg
& SHM_RDONLY
) ? IPC_R
: IPC_R
|IPC_W
);
603 handle
= shmseg
->shm_internal
;
605 if (shmat_msg
->size
> shmseg
->shm_segsz
) {
606 if (handle
->type
!= UNDOGET
) {
611 fd
= ((struct shm_handle
*)shmseg
->shm_internal
)->fd
;
612 ftruncate(fd
, round_page(new_size
));
613 shmseg
->shm_segsz
= new_size
;
616 shmseg
->shm_lpid
= pid
;
617 shmseg
->shm_atime
= time(NULL
);
619 if (handle
->type
!= UNDOGET
)
620 shmseg
->shm_nattch
++;
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. */
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.
630 sysvd_print("nattch = %d pid = %d\n",
631 shmseg
->shm_nattch
, pid
);
633 pidatt
= malloc(sizeof(*pidatt
));
635 LIST_INSERT_HEAD(&handle
->attached_list
, pidatt
, link
);
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.
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
);
651 /* Handle a shmdt() request. */
653 handle_shmdt(pid_t pid
, int shmid
) {
654 struct shmid_ds
*shmseg
;
656 struct shm_handle
*handle
;
657 struct pid_attached
*pidatt
;
658 struct id_attached
*idatt
;
661 sysvd_print("shmdt pid %d shmid %d\n", pid
, shmid
);
662 /*if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL)
666 segnum
= IPCID_TO_IX(shmid
);
667 shmseg
= &shmsegs
[segnum
];
668 handle
= shmseg
->shm_internal
;
670 /* Check if pid is attached. */
671 LIST_FOREACH(pidatt
, &handle
->attached_list
, link
)
672 if (pidatt
->pid
== pid
)
675 sysvd_print_err("process %d is not attached to %d (1)\n",
679 LIST_REMOVE(pidatt
, link
);
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
)
687 sysvd_print_err("process %d is not attached to %d (2)\n",
691 LIST_REMOVE(idatt
, link
);
693 shmseg
->shm_dtime
= time(NULL
);
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
;
705 /* Handle a shmctl() request. */
707 handle_shmctl(struct shmctl_msg
*shmctl_msg
,
708 struct cmsgcred
*cred
) {
710 struct shmid_ds
*shmseg
, *inbuf
;
712 /* if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL)
715 shmseg
= shm_find_segment_by_shmid(shmctl_msg
->shmid
);
717 if (shmseg
== NULL
) {
722 switch (shmctl_msg
->cmd
) {
724 sysvd_print("IPC STAT\n");
725 error
= ipcperm(cred
, &shmseg
->shm_perm
, IPC_R
);
727 sysvd_print("IPC_STAT not allowed\n");
730 shmctl_msg
->buf
= *shmseg
;
733 sysvd_print("IPC SET\n");
734 error
= ipcperm(cred
, &shmseg
->shm_perm
, IPC_M
);
736 sysvd_print("IPC_SET not allowed\n");
739 inbuf
= &shmctl_msg
->buf
;
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
);
749 sysvd_print("IPC RMID shmid = %d\n",
751 error
= ipcperm(cred
, &shmseg
->shm_perm
, IPC_M
);
753 sysvd_print("IPC_RMID not allowed\n");
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
);
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
,
784 /* Function used by daemon to map a sysv resource. */
787 struct shmid_ds
*shmseg
;
788 struct shm_handle
*internal
;
794 shmseg
= shm_find_segment_by_shmid(shmid
);
796 sysvd_print_err("map_seg error:"
797 "semid %d not found\n", shmid
);
801 internal
= (struct shm_handle
*)shmseg
->shm_internal
;
803 sysvd_print_err("map_seg error: internal for"
804 "semid %d not found\n", shmid
);
810 size
= round_page(shmseg
->shm_segsz
);
812 addr
= mmap(NULL
, size
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, 0);
814 sysvd_print_err("map_seg: error mmap semid = %d\n", shmid
);
821 /* Function used by daemon to munmap a sysv resource. */
823 munmap_seg(int shmid
, void *addr
) {
824 struct shmid_ds
*shmseg
;
825 struct shm_handle
*internal
;
829 shmseg
= shm_find_segment_by_shmid(shmid
);
831 sysvd_print_err("munmap_seg error:"
832 "semid %d not found\n", shmid
);
836 internal
= (struct shm_handle
*)shmseg
->shm_internal
;
838 sysvd_print_err("munmap_seg error: internal for"
839 "semid %d not found\n", shmid
);
843 size
= round_page(shmseg
->shm_segsz
);
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;
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 ...
869 while (i
< 1024 && i
!= msginfo
.msgssz
)
871 if (i
!= msginfo
.msgssz
) {
872 sysvd_print_err("msginfo.msgssz=%d (0x%x)\n", msginfo
.msgssz
,
874 sysvd_print_err("msginfo.msgssz not a small power of 2");
877 msginfo
.msgmax
= msginfo
.msgseg
* msginfo
.msgssz
;
886 semexit(int undoid
) {
887 struct sem_undo
*suptr
;
889 struct shmid_ds
*undoseg
;
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");
903 suptr
= (struct sem_undo
*)map_seg(undoid
);
905 sysvd_print_err("no %d undo segment found\n", undoid
);
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.
914 while (suptr
->un_cnt
) {
915 struct semid_pool
*semaptr
;
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
;
926 semaptr
= (struct semid_pool
*)map_seg(semid
);
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) {
936 sysvd_print_err("semexit - semid not allocated\n");
939 if (semnum
>= semaptr
->ds
.sem_nsems
) {
941 sysvd_print_err("semexit - semnum out of range\n");
947 sysv_rwlock_rdlock(&semaptr
->rwlock
);
949 sysv_rwlock_wrlock(&semaptr
->rwlock
);
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.
958 semptr
= &semaptr
->ds
.sem_base
[semnum
];
960 sysv_mutex_lock(&semptr
->sem_mutex
);
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
) {
969 if (semptr
->semval
< -adjval
)
972 semptr
->semval
+= adjval
;
974 semptr
->semval
+= adjval
;
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);
982 sysv_mutex_unlock(&semptr
->sem_mutex
);
986 sysv_rwlock_unlock(&semaptr
->rwlock
);
988 sysv_mutex_unlock(&semaptr
->mutex
);
990 munmap_seg(semid
, semaptr
);
993 munmap_seg(undoid
, suptr
);
998 shmexit(struct client
*cl
) {
999 struct id_attached
*idatt
;
1001 while (!LIST_EMPTY(&cl
->ids_attached
)) {
1002 idatt
= LIST_FIRST(&cl
->ids_attached
);
1003 handle_shmdt(cl
->pid
, idatt
->shmid
);