tmpfs - Move dnode lock to improve unlink performance
[dragonfly.git] / usr.sbin / sysvipcd / shmd.c
blob84c5df79daf6a57e7218a64fa06b8b3be19bb616
1 /*
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
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.
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>
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>
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>
49 #include "limits.h"
50 #include "perm.h"
51 #include "utilsd.h"
53 #include "shmd.h"
54 #include "sysvipc_hash.h"
55 #include "sysvipc_sockets.h"
57 static struct shminfo shminfo = {
58 // 0,
59 SHMMIN,
60 SHMMNI,
61 SHMSEG
62 // 0
65 /* Shared memory.*/
66 static int shm_last_free, shm_committed, shmalloced;
67 int shm_nused;
68 static struct shmid_ds *shmsegs;
70 /* Message queues.*/
71 extern struct msginfo msginfo;
73 extern struct hashtable *clientshash;
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;
87 errno = 0;
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);
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;
112 ftruncate(fd, size);
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;
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);
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;
146 /* Initialize each sem. */
147 memset(sems->ds.sem_base, 0, nsems + sizeof(struct sem));
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
155 munmap(addr, size);
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;
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));*/
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 */
188 msgq->free_msgmaps = 0;
189 msgq->nfree_msgmaps = msginfo.msgseg;
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;
197 msgq->free_msghdrs = 0;
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;
211 munmap(addr, size);
213 break;
214 default:
215 break;
218 unlink(filename);
219 out:
220 return (fd);
221 error:
222 close(fd);
223 return (-1);
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);
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);
242 return (0);
245 static int
246 shm_find_segment_by_key(key_t key)
248 int i;
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);
255 return (-1);
258 static struct shmid_ds *
259 shm_find_segment_by_shmid(int shmid)
261 int segnum;
262 struct shmid_ds *shmseg;
264 segnum = IPCID_TO_IX(shmid);
265 if (segnum < 0 || segnum >= shmalloced) {
266 sysvd_print_err("segnum out of range\n");
267 return (NULL);
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);
277 return (shmseg);
280 /* Remove a shared memory segment. */
281 static void
282 shm_deallocate_segment(int segnum)
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;
290 sysvd_print("deallocate segment %d\n", segnum);
292 size = round_page(shmseg->shm_segsz);
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);
301 #endif
303 /* Close the corresponding file. */
304 close(internal->fd);
306 /* Free other resources. */
307 free(shmseg->shm_internal);
308 shmseg->shm_internal = NULL;
309 shm_committed -= btoc(size);
310 shm_nused--;
312 shmseg->shm_perm.mode = SHMSEG_FREE;
315 static void *map_seg(int);
316 static int munmap_seg(int, void *);
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;
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;
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.
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;
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;
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)
372 struct shmid_ds *shmseg;
373 int error;
375 shmseg = &shmsegs[segnum];
376 if (shmseg->shm_perm.mode & SHMSEG_REMOVED) {
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).
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;*/
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));
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)
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;
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.
425 return (-EINVAL);
426 if (shm_nused >= shminfo.shmmni) /* any shmids left? */
427 return (-ENOSPC);
429 /* Compute the size of the segment. */
430 size = round_page(shmget_msg->size);
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;
439 if (i == shmalloced) {
440 sysvd_print("i == shmalloced\n");
441 return (-ENOSPC);
443 segnum = i;
444 } else {
445 segnum = shm_last_free;
446 shm_last_free = -1;
448 shmseg = &shmsegs[segnum];
450 * In case we sleep in malloc(), mark the segment present but deleted
451 * so that noone else tries to create the same key.
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;
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);
470 LIST_INIT(&handle->attached_list);
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);
480 /* Get the id. */
481 shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm);
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;
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);
493 shmseg->shm_segsz = shmget_msg->size;
494 shm_committed += btoc(size);
495 shm_nused++;
497 if (shmseg->shm_perm.mode & SHMSEG_WANTED) {
499 * Somebody else wanted this key while we were asleep. Wake
500 * them up now.
502 shmseg->shm_perm.mode &= ~SHMSEG_WANTED;
503 //TODO multithreading
504 //wakeup((caddr_t)shmseg);
506 shmseg->shm_perm.mode &= ~SHMSEG_REMOVED;
508 if (shmget_msg->type == UNDOGET) {
509 /* The file is used by daemon when clients terminates
510 * and sem_undo resources must be cleaned.
512 struct client *cl= _hash_lookup(clientshash, pid);
513 cl->undoid = shmid;
516 return (shmid);
519 /* Handle a shmget() request. */
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;
527 //if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL)
528 // return (ENOSYS);
529 mode = shmget_msg->shmflg & ACCESSPERMS;
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);
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;
546 if ((shmget_msg->shmflg & IPC_CREAT) == 0) {
547 error = -ENOENT;
548 goto done_err;
551 error = shmget_allocate_segment(pid, shmget_msg, mode, cred);
552 sysvd_print("allocate segment = %d\n", error);
553 done:
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.
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);
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);
574 /* Handle a shmat() request. */
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;
587 /*if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL)
588 return (ENOSYS);
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;
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;
602 handle = shmseg->shm_internal;
604 if (shmat_msg->size > shmseg->shm_segsz) {
605 if (handle->type != UNDOGET) {
606 error = EINVAL;
607 goto done;
610 fd = ((struct shm_handle*)shmseg->shm_internal)->fd;
611 ftruncate(fd, round_page(new_size));
612 shmseg->shm_segsz = new_size;
615 shmseg->shm_lpid = pid;
616 shmseg->shm_atime = time(NULL);
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. */
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.
629 sysvd_print("nattch = %d pid = %d\n",
630 shmseg->shm_nattch, pid);
632 pidatt = malloc(sizeof(*pidatt));
633 pidatt->pid = pid;
634 LIST_INSERT_HEAD(&handle->attached_list, pidatt, link);
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.
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);
645 return (0);
646 done:
647 return (error);
650 /* Handle a shmdt() request. */
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;
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);
665 segnum = IPCID_TO_IX(shmid);
666 shmseg = &shmsegs[segnum];
667 handle = shmseg->shm_internal;
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);
678 LIST_REMOVE(pidatt, link);
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);
690 LIST_REMOVE(idatt, link);
692 shmseg->shm_dtime = time(NULL);
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;
701 return (0);
704 /* Handle a shmctl() request. */
706 handle_shmctl(struct shmctl_msg *shmctl_msg,
707 struct cmsgcred *cred ) {
708 int error = 0;
709 struct shmid_ds *shmseg, *inbuf;
711 /* if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL)
712 return (ENOSYS);
714 shmseg = shm_find_segment_by_shmid(shmctl_msg->shmid);
716 if (shmseg == NULL) {
717 error = EINVAL;
718 goto done;
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;
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;
738 inbuf = &shmctl_msg->buf;
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;
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);
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);
769 break;
770 #if 0
771 case SHM_LOCK:
772 case SHM_UNLOCK:
773 #endif
774 default:
775 error = EINVAL;
776 break;
778 done:
779 return (error);
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;
789 int fd;
790 size_t size;
791 void *addr;
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);
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);
807 fd = internal->fd;
809 size = round_page(shmseg->shm_segsz);
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);
817 return (addr);
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;
826 size_t size;
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);
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);
842 size = round_page(shmseg->shm_segsz);
843 munmap(addr, size);
845 return (0);
848 void
849 shminit(void) {
850 int i;
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;
858 shm_last_free = 0;
859 shm_nused = 0;
860 shm_committed = 0;
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 ...
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);
876 msginfo.msgmax = msginfo.msgseg * msginfo.msgssz;
879 /*static void
880 shmfree(void) {
881 free(shmsegs);
885 semexit(int undoid) {
886 struct sem_undo *suptr;
887 struct sem *semptr;
888 struct shmid_ds *undoseg;
890 if (undoid < 0) {
891 return (-1);
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);
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);
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.
913 while (suptr->un_cnt) {
914 struct semid_pool *semaptr;
915 int semid;
916 int semnum;
917 int adjval;
918 int ix;
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;
925 semaptr = (struct semid_pool *)map_seg(semid);
926 if (!semaptr) {
927 return (-1);
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;
938 if (semnum >= semaptr->ds.sem_nsems) {
939 --suptr->un_cnt;
940 sysvd_print_err("semexit - semnum out of range\n");
941 continue;
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.
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;
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;
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);
980 #ifdef SYSV_SEMS
981 sysv_mutex_unlock(&semptr->sem_mutex);
982 #endif
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);
992 munmap_seg(undoid, suptr);
993 return (0);
996 void
997 shmexit(struct client *cl) {
998 struct id_attached *idatt;
1000 while (!LIST_EMPTY(&cl->ids_attached)) {
1001 idatt = LIST_FIRST(&cl->ids_attached);
1002 handle_shmdt(cl->pid, idatt->shmid);