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