if_iwm - Factor out firmware station handling into if_iwm_sta.c.
[dragonfly.git] / usr.sbin / sysvipcd / shmd.c
blob62301dd1319b6e6472a8c87c4a81d1c582d0439e
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/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>
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>
50 #include "limits.h"
51 #include "perm.h"
52 #include "utilsd.h"
54 #include "shmd.h"
55 #include "sysvipc_hash.h"
56 #include "sysvipc_sockets.h"
58 static struct shminfo shminfo = {
59 // 0,
60 SHMMIN,
61 SHMMNI,
62 SHMSEG
63 // 0
66 /* Shared memory.*/
67 static int shm_last_free, shm_committed, shmalloced;
68 int shm_nused;
69 static struct shmid_ds *shmsegs;
71 /* Message queues.*/
72 extern struct msginfo msginfo;
74 extern struct hashtable *clientshash;
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;
88 errno = 0;
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);
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;
113 ftruncate(fd, size);
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;
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);
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;
147 /* Initialize each sem. */
148 memset(sems->ds.sem_base, 0, nsems + sizeof(struct sem));
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
156 munmap(addr, size);
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;
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));*/
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 */
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;
194 if (i > 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;
212 munmap(addr, size);
214 break;
215 default:
216 break;
219 unlink(filename);
220 out:
221 return (fd);
222 error:
223 close(fd);
224 return (-1);
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);
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);
243 return (0);
246 static int
247 shm_find_segment_by_key(key_t key)
249 int i;
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);
256 return (-1);
259 static struct shmid_ds *
260 shm_find_segment_by_shmid(int shmid)
262 int segnum;
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");
268 return (NULL);
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);
278 return (shmseg);
281 /* Remove a shared memory segment. */
282 static void
283 shm_deallocate_segment(int segnum)
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;
291 sysvd_print("deallocate segment %d\n", segnum);
293 size = round_page(shmseg->shm_segsz);
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);
302 #endif
304 /* Close the corresponding file. */
305 close(internal->fd);
307 /* Free other resources. */
308 free(shmseg->shm_internal);
309 shmseg->shm_internal = NULL;
310 shm_committed -= btoc(size);
311 shm_nused--;
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. */
320 static void
321 mark_segment_removed(int shmid, int type) {
322 struct semid_pool *semaptr;
323 struct msqid_pool *msgq;
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;
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.
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;
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;
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)
373 struct shmid_ds *shmseg;
374 int error;
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);
386 if (error)
387 return error;
388 return EAGAIN;*/
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));
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)
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;
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.
426 return (-EINVAL);
427 if (shm_nused >= shminfo.shmmni) /* any shmids left? */
428 return (-ENOSPC);
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)
438 break;
440 if (i == shmalloced) {
441 sysvd_print("i == shmalloced\n");
442 return (-ENOSPC);
444 segnum = i;
445 } else {
446 segnum = shm_last_free;
447 shm_last_free = -1;
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) {
463 free(handle);
464 handle = NULL;
465 shmseg->shm_perm.mode = SHMSEG_FREE;
466 shm_last_free = segnum;
467 errno = -ENFILE;
468 return (-1);
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;
478 return (-errno);
481 /* Get the id. */
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);
496 shm_nused++;
498 if (shmseg->shm_perm.mode & SHMSEG_WANTED) {
500 * Somebody else wanted this key while we were asleep. Wake
501 * them up now.
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);
514 cl->undoid = shmid;
517 return (shmid);
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)
529 // return (ENOSYS);
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) {
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;
547 if ((shmget_msg->shmflg & IPC_CREAT) == 0) {
548 error = -ENOENT;
549 goto done_err;
552 error = shmget_allocate_segment(pid, shmget_msg, mode, cred);
553 sysvd_print("allocate segment = %d\n", error);
554 done:
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.
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);
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);
575 /* Handle a shmat() request. */
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;
588 /*if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL)
589 return (ENOSYS);
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;
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;
603 handle = shmseg->shm_internal;
605 if (shmat_msg->size > shmseg->shm_segsz) {
606 if (handle->type != UNDOGET) {
607 error = EINVAL;
608 goto done;
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++;
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. */
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));
634 pidatt->pid = pid;
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);
646 return (0);
647 done:
648 return (error);
651 /* Handle a shmdt() request. */
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;
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);
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)
673 break;
674 if (!pidatt) {
675 sysvd_print_err("process %d is not attached to %d (1)\n",
676 pid, shmid);
677 return (EINVAL);
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)
685 break;
686 if (!idatt) {
687 sysvd_print_err("process %d is not attached to %d (2)\n",
688 pid, shmid);
689 return (EINVAL);
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;
702 return (0);
705 /* Handle a shmctl() request. */
707 handle_shmctl(struct shmctl_msg *shmctl_msg,
708 struct cmsgcred *cred ) {
709 int error = 0;
710 struct shmid_ds *shmseg, *inbuf;
712 /* if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL)
713 return (ENOSYS);
715 shmseg = shm_find_segment_by_shmid(shmctl_msg->shmid);
717 if (shmseg == NULL) {
718 error = EINVAL;
719 goto done;
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;
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;
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);
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;
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);
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);
770 break;
771 #if 0
772 case SHM_LOCK:
773 case SHM_UNLOCK:
774 #endif
775 default:
776 error = EINVAL;
777 break;
779 done:
780 return (error);
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;
790 int fd;
791 size_t size;
792 void *addr;
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);
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);
808 fd = internal->fd;
810 size = round_page(shmseg->shm_segsz);
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);
818 return (addr);
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;
827 size_t size;
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);
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);
843 size = round_page(shmseg->shm_segsz);
844 munmap(addr, size);
846 return (0);
849 void
850 shminit(void) {
851 int i;
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;
859 shm_last_free = 0;
860 shm_nused = 0;
861 shm_committed = 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 ...
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);
877 msginfo.msgmax = msginfo.msgseg * msginfo.msgssz;
880 /*static void
881 shmfree(void) {
882 free(shmsegs);
886 semexit(int undoid) {
887 struct sem_undo *suptr;
888 struct sem *semptr;
889 struct shmid_ds *undoseg;
891 if (undoid < 0) {
892 return (-1);
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);
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);
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;
916 int semid;
917 int semnum;
918 int adjval;
919 int ix;
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);
927 if (!semaptr) {
928 return (-1);
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;
939 if (semnum >= semaptr->ds.sem_nsems) {
940 --suptr->un_cnt;
941 sysvd_print_err("semexit - semnum out of range\n");
942 continue;
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.
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;
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;
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);
981 #ifdef SYSV_SEMS
982 sysv_mutex_unlock(&semptr->sem_mutex);
983 #endif
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);
993 munmap_seg(undoid, suptr);
994 return (0);
997 void
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);