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