Merge branch 'vendor/HOSTAPD'
[dragonfly.git] / lib / libc / sysvipc / shm.c
1 /*
2  * Copyright (c) 2013 Larisa Grigore <larisagrigore@gmail.com>.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  * 3. All advertising materials mentioning features or use of this software
13  *    must display the following acknowledgement:
14  *      This product includes software developed by Adam Glass and Charles
15  *      Hannum.
16  * 4. The names of the authors may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "namespace.h"
32 #include <sys/param.h>
33 #include <sys/queue.h>
34 #include <sys/mman.h>
35 #include <sys/shm.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <err.h>
41 #include <pthread.h>
42 #include <unistd.h>
43 #include "un-namespace.h"
44
45 #include "sysvipc_lock.h"
46 #include "sysvipc_ipc.h"
47 #include "sysvipc_sockets.h"
48 #include "sysvipc_shm.h"
49 #include "sysvipc_hash.h"
50
51 #define SYSV_MUTEX_LOCK(x)              if (__isthreaded) _pthread_mutex_lock(x)
52 #define SYSV_MUTEX_UNLOCK(x)    if (__isthreaded) _pthread_mutex_unlock(x)
53 #define SYSV_MUTEX_DESTROY(x)   if (__isthreaded) _pthread_mutex_destroy(x)
54
55 struct hashtable *shmres = NULL;
56 struct hashtable *shmaddrs = NULL;
57 pthread_mutex_t lock_resources = PTHREAD_MUTEX_INITIALIZER;
58
59 /* File descriptor used to communicate with the daemon. */
60 extern int daemon_fd;
61 /* Structure used to save semaphore operation with SEMUNDO flag. */
62 extern struct sem_undo *undos;
63
64 static int
65 shminit(void) {
66         if (shmres) {
67                 errno = EPERM;
68                 return (-1);
69         }
70
71         shmres = _hash_init(MAXSIZE);
72         if (!shmres)
73                 goto out_resources;
74
75         shmaddrs = _hash_init(MAXSIZE);
76         if (!shmaddrs)
77                 goto out_addrs;
78
79         return 0;
80
81 out_addrs:
82         _hash_destroy(shmres);
83 out_resources:
84         return -1;
85 }
86
87 /*static int
88 shmexit(void) {
89         if (!shmres)
90                 return -EPERM;
91
92         _hash_destroy(shmres);
93         _hash_destroy(shmaddrs);
94         SYSV_MUTEX_DESTROY(lock_resources);
95
96         return 0;
97 }*/
98
99 /* Init sysv ipc resources and those used for shared memory. */
100 static int
101 shmcheck(void) {
102         int ret;
103
104         /* Init sysv resources. */
105         if ((ret = sysvinit()) != 0)
106                 return (ret);
107         /* Init resorces used for shared memory. */
108         if ((ret = shminit()) < 0)
109                 return (ret);
110         return (0);
111 }
112
113 /* Check if sysv ipc resources are initialized. */
114 static int
115 is_shm_started(void) {
116         if (!is_sysvinit())
117                 return (0);
118         if (!shmres)
119                 return (0);
120         return (1);
121 }
122
123 /* OBS: It is used only a rwlock for both hashtables and
124  * socket. I've made that choice because is I considered to
125  * be much expensive to acquire/release more than one especially
126  * as the daemon is not multithreading.
127  */
128
129 /* This function has another parameter apart from shmget.
130  * The parameter has information about the type of sysv
131  * ipc resource (shm, sem, msg, undo).
132  * The undo segment is used for sem ops with UNDO flag set.
133  */
134 int
135 _shmget(key_t key, size_t size, int shmflg, int type) {
136         struct shmget_msg msg;
137         struct shm_data *data;
138         int shmid, fd;
139         int flags;
140
141         SYSV_MUTEX_LOCK(&lock_resources);
142         if (shmcheck() < 0) {
143                 sysv_print_err("init sysv ipc\n");
144                 goto done;
145         }
146
147         msg.key = key;
148         msg.size = size;
149         msg.shmflg = shmflg;
150         msg.type = type;
151
152         send_message(daemon_fd, type, (char *)&msg, sizeof(msg));
153
154         /* Accept a file installed by the daemon.
155          * The file is used as shared memory. */
156         fd = receive_fd(daemon_fd);
157         if (fd < 0) {
158                 shmid = -1;
159                 goto done;
160         }
161
162         flags = _fcntl(fd, F_GETFD, 0);
163         if (_fcntl(fd, F_SETFD, flags & FD_CLOEXEC) == -1) {
164                 sysv_print_err("fcntl error\n");
165                 shmid = -1;
166                 goto done;
167         }
168
169         /* Receive the resource id or error. */
170         receive_message(daemon_fd, (char *)&shmid, sizeof(shmid));
171
172         if (shmid < 0) {
173                 errno = -shmid;
174                 shmid = -1;
175                 goto done;
176         }
177
178         /* Do we already have an entry for this resource? */
179         data = _hash_lookup(shmres, shmid);
180         if (data)
181                 goto done;
182
183         /* If not, add necessary data about it. */
184         data = malloc(sizeof(struct shm_data));
185         data->fd = fd;
186         data->size = size;
187         data->shmid = shmid;
188         data->type = type;
189         data->used = 0;
190         data->removed = 0;
191         data->access = 0; /* Used for sems. */
192
193         /* Insert data in hashtable using the shmid. */
194         _hash_insert(shmres, shmid, data);
195 done:
196         SYSV_MUTEX_UNLOCK(&lock_resources);
197         return (shmid);
198 }
199
200 int
201 sysvipc_shmget(key_t key, size_t size, int shmflg) {
202         return (_shmget(key, size, shmflg, SHMGET));
203 }
204
205 void *
206 sysvipc_shmat(int shmid, const void *shmaddr, int shmflg) {
207         struct shmat_msg msg;
208         void *addr = NULL;
209         int error;
210         int flags, prot;
211         size_t size;
212         struct shm_data *data;
213
214         SYSV_MUTEX_LOCK(&lock_resources);
215         if (!is_shm_started()) {
216                 errno = EINVAL;
217                 goto done;
218         }
219
220         /* Get data using shmid. */
221         data = _hash_lookup(shmres, shmid);
222         if (data == NULL) {
223                 errno = EINVAL;
224                 goto done;
225         }
226
227         size = round_page(data->size);
228
229 #ifdef VM_PROT_READ_IS_EXEC
230         prot = PROT_READ | PROT_EXECUTE;
231 #else
232         prot = PROT_READ;
233 #endif
234         if ((shmflg & SHM_RDONLY) == 0)
235                 prot |= PROT_WRITE;
236
237         flags = MAP_SHARED;
238         if (shmaddr) {
239                 if (shmflg & SHM_RND) {
240                         addr = (void *)((vm_offset_t)shmaddr & ~(SHMLBA-1));
241                 } else if (((vm_offset_t)shmaddr & (SHMLBA-1)) == 0) {
242                         addr = __DECONST(void *, shmaddr);
243                 } else {
244                         errno = EINVAL;
245                         goto done;
246                 }
247         }
248
249         msg.shmid = shmid;
250         msg.shmaddr = shmaddr;
251         msg.shmflg = shmflg;
252         msg.size = data->size; /* For undo segment. */
253
254         send_message(daemon_fd, SHMAT, (char *)&msg, sizeof(msg));
255         receive_message(daemon_fd, (char *)&error, sizeof(error));
256         if (error) {
257                 errno = error;
258                 goto done;
259         }
260
261         addr = mmap(addr, size, prot, flags, data->fd, 0);
262         if (!addr) {
263                 sysv_print_err("mmap\n");
264                 /* Detach ourselves from the segment. */
265                 send_message(daemon_fd, SHMDT, (char *)&shmid, sizeof(shmid));
266                 goto done;
267         }
268
269         /* Necessary for SEMGET, MSGGET, UNDOGET. */
270         data->internal = addr;
271
272         /* Save the mapped address for munmap call. */
273         _hash_insert(shmaddrs, (u_long)addr, data);
274 done:
275         SYSV_MUTEX_UNLOCK(&lock_resources);
276         return (addr);
277 }
278
279 /* Remove a sysv ipc resource. */
280 static
281 void shmremove(int shmid) {
282         struct shm_data *data;
283         data = _hash_remove(shmres, shmid);
284
285         //TODO nu trebuie demapat?
286         _close(data->fd);
287         free(data);
288         data = NULL;
289 }
290
291 int
292 sysvipc_shmctl(int shmid, int cmd, struct shmid_ds *buf) {
293         int size, ret;
294         struct shmctl_msg *msg;
295
296 /*      if (cmd == IPC_SET)
297                 size = sizeof(struct shmctl_msg) + sizeof(struct shmid_ds);
298         else
299                 size = sizeof(struct shmctl_msg);
300 */
301         SYSV_MUTEX_LOCK(&lock_resources);
302
303         ret = -1;
304
305         if (!is_shm_started()) {
306                 errno = EINVAL;
307                 goto done;
308         }
309
310         size = sizeof(struct shmctl_msg);
311         msg = malloc(size);
312         msg->shmid = shmid;
313         msg->cmd = cmd;
314
315         if (cmd == IPC_SET)
316                 msg->buf = *buf;
317
318         send_message(daemon_fd, SHMCTL, (char *)msg, sizeof(*msg));
319
320         receive_message(daemon_fd, (char *)&ret, sizeof(ret));
321
322         /* Get data in IPC_STAT case. */
323         if (ret == 0 && cmd == IPC_STAT)
324                 receive_message(daemon_fd, (char *)buf, sizeof(*buf));
325
326         /* Free all resources specific to a shmid in IPC_RMID case. */
327         if (ret == 0 && cmd == IPC_RMID)
328                 shmremove(shmid);
329
330         errno = ret;
331 done:
332         SYSV_MUTEX_UNLOCK(&lock_resources);
333         return (ret == 0 ? 0 : -1);
334 }
335
336 /* Functionality of shmdt with the possibility to inform or not
337  * the daemon.
338  * Inform the daemon when shmdt is called and not when an error
339  * occurs and the daemon doesn't know that the process is attaced.
340  */
341 static int
342 _shmdt(const void *shmaddr, int send_to_daemon) {
343         int ret;
344         size_t size;
345         struct shm_data *data;
346
347         ret = -1;
348
349         SYSV_MUTEX_LOCK(&lock_resources);
350         if (!is_shm_started()) {
351                 errno = EINVAL;
352                 goto done;
353         }
354
355         /* Verify if shmaddr was returned from a shmat call. */
356         data = _hash_remove(shmaddrs, (u_long)shmaddr);
357         if (data == NULL) {
358                 errno = EINVAL;
359                 goto done;
360         }
361
362         size = round_page(data->size);
363
364         ret = munmap(__DECONST(void *, shmaddr), size);
365         if (ret)
366                 goto done;
367
368         if (send_to_daemon)
369                 send_message(daemon_fd, SHMDT, (char *)&data->shmid, sizeof(int));
370
371         shmaddr = NULL;
372         free(data);
373         data = NULL;
374 done:
375         SYSV_MUTEX_UNLOCK(&lock_resources);
376         return (ret);
377 }
378
379 int
380 sysvipc_shmdt(const void *shmaddr) {
381         return (_shmdt(shmaddr, 1));
382 }
383
384 void
385 shmchild(void) {
386         int i;
387         struct entries_list *list;
388         struct hashentry *tmp, *ttmp;
389         struct shmat_msg msg;
390         struct shm_data *data;
391         int error;
392
393 /* OBS: no locking is necessary because this function is called
394  * after the child is created and at that moment only one thread
395  * exists in the process.
396  */
397         for (i=0; i<get_hash_size(MAXSIZE); i++) {
398                 list = &shmaddrs->entries[i];
399                 if (LIST_EMPTY(list))
400                         continue;
401                 LIST_FOREACH_MUTABLE(tmp, list, entry_link, ttmp) {
402                         data = (struct shm_data*)tmp->value;
403                         /* Inform daemon that we are attached. */
404
405                         if (data->type == UNDOGET) {
406                                 continue;
407                         }
408
409                         msg.shmid = data->shmid;
410                         msg.shmaddr = data->internal;
411                         msg.shmflg = 0; /* This is enough at this moment. */
412                         msg.size = data->size;
413                         /* Last field is not necessary because it is used only
414                          * for undo segments.
415                          */
416
417                         send_message(daemon_fd, SHMAT, (char *)&msg, sizeof(msg));
418                         receive_message(daemon_fd, (char *)&error, sizeof(error));
419
420                         /* If the daemon returned error munmap the region. */
421                         if (error) {
422                                 errno = error;
423                                 _shmdt(data->internal, 0);
424                                 shmremove(data->shmid);
425                                 sysv_print_err(" %d shmchild\n", error);
426                                 sleep(20);
427                         }
428                         
429                 }
430         }
431
432         /* Remove semundo structures. Those are specific only for the parent.
433          * The child must create for itself a new one.
434          */
435         data = _hash_remove(shmaddrs, (u_long)undos);
436         if (undos) {
437                 munmap(undos, round_page(data->size));
438                 undos = NULL;
439         }
440 }
441
442 /* Called each time a thread tries to access the sem/msg.
443  * It is used in order to protect data against its removal
444  * by another thread.
445  */
446 struct shm_data*
447 get_shmdata(int id, int to_remove, int shm_access) {
448         struct shm_data *data = NULL;
449
450         SYSV_MUTEX_LOCK(&lock_resources);
451         if (!is_shm_started()) {
452                 errno = EINVAL;
453                 goto done;
454         }
455
456         data = _hash_lookup(shmres, id);
457         if (!data) {
458                 errno = EINVAL;
459                 goto done;
460         }
461
462         /* If segment was removed by another thread we can't use it. */
463         if (data->removed) {
464                 sysv_print("segment already removed\n");
465                 errno = EINVAL;
466                 data = NULL;
467                 goto done;
468         }
469
470         /* Mark for removal. Inform the other threads from the
471          * same address space. */
472         if (to_remove) {
473                 sysv_print("segment is removed\n");
474                 data->removed = to_remove; /* 1 if it is removed by
475                 the current process and 2 if it was removed by
476                 another one. */
477
478                 /* No need for any rights check because this is
479                  * done by daemon if this is the process that removes
480                  * the sem/msg.
481                  * If not, there is no need for any right to clean
482                  * internal resources.
483                  */
484                 goto done2;
485         }
486
487         /* Avoid segmentation fault if the memory zone
488          * is accessed without necessary permissions
489          * (it was mapped according to them).
490          */
491         if (!(data->access & shm_access)) {
492 #if 0
493                 sysv_print("no access rights has %o and wants %o\n",
494                                 data->access, shm_access);
495                 errno = EACCES;
496                 data = NULL;
497                 goto done;
498 #endif
499         }
500
501 done2:
502         data->used++;
503 done:
504         SYSV_MUTEX_UNLOCK(&lock_resources);
505         return (data);
506 }
507
508 /* Set the shm_access type (IPC_R, IPC_W) for sem/msg. */
509 int
510 set_shmdata_access(int id, int shm_access) {
511         struct shm_data *data;
512         int ret = -1;
513
514         SYSV_MUTEX_LOCK(&lock_resources);
515         if (!is_shm_started()) {
516                 errno = EINVAL;
517                 goto done;
518         }
519
520         data = _hash_lookup(shmres, id);
521         if (!data) {
522                 errno = EINVAL;
523                 goto done;
524         }
525
526         /* If segment was removed by another thread we can't use it. */
527         if (data->removed) {
528                 errno = EINVAL;
529                 goto done;
530         }
531
532         data->access = shm_access;
533         ret = 0;
534 done:
535         SYSV_MUTEX_UNLOCK(&lock_resources);
536
537         return (ret);
538 }