dhcpcd: 02-dump is no longer supplied
[dragonfly.git] / lib / libc / sysvipc / shm.c
blob00c8f6f0eee024ade5f94071eac58496cb84b29b
1 /*
2 * Copyright (c) 2013 Larisa Grigore <larisagrigore@gmail.com>.
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.
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.
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 <stdint.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <err.h>
42 #include <pthread.h>
43 #include <unistd.h>
44 #include "un-namespace.h"
46 #include "sysvipc_lock.h"
47 #include "sysvipc_ipc.h"
48 #include "sysvipc_sockets.h"
49 #include "sysvipc_shm.h"
50 #include "sysvipc_hash.h"
52 #define SYSV_MUTEX_LOCK(x) if (__isthreaded) _pthread_mutex_lock(x)
53 #define SYSV_MUTEX_UNLOCK(x) if (__isthreaded) _pthread_mutex_unlock(x)
54 #define SYSV_MUTEX_DESTROY(x) if (__isthreaded) _pthread_mutex_destroy(x)
56 struct hashtable *shmres = NULL;
57 struct hashtable *shmaddrs = NULL;
58 pthread_mutex_t lock_resources = PTHREAD_MUTEX_INITIALIZER;
60 /* File descriptor used to communicate with the daemon. */
61 extern int daemon_fd;
62 /* Structure used to save semaphore operation with SEMUNDO flag. */
63 extern struct sem_undo *undos;
65 static int
66 shminit(void)
68 if (shmres) {
69 errno = EPERM;
70 return (-1);
73 shmres = _hash_init(MAXSIZE);
74 if (!shmres)
75 goto out_resources;
77 shmaddrs = _hash_init(MAXSIZE);
78 if (!shmaddrs)
79 goto out_addrs;
81 return 0;
83 out_addrs:
84 _hash_destroy(shmres);
85 out_resources:
86 return -1;
89 /*static int
90 shmexit(void)
92 if (!shmres)
93 return -EPERM;
95 _hash_destroy(shmres);
96 _hash_destroy(shmaddrs);
97 SYSV_MUTEX_DESTROY(lock_resources);
99 return 0;
102 /* Init sysv ipc resources and those used for shared memory. */
103 static int
104 shmcheck(void)
106 int ret;
108 /* Init sysv resources. */
109 if ((ret = sysvinit()) != 0)
110 return (ret);
111 /* Init resorces used for shared memory. */
112 if ((ret = shminit()) < 0)
113 return (ret);
114 return (0);
117 /* Check if sysv ipc resources are initialized. */
118 static int
119 is_shm_started(void)
121 if (!is_sysvinit())
122 return (0);
123 if (!shmres)
124 return (0);
125 return (1);
128 /* OBS: It is used only a rwlock for both hashtables and
129 * socket. I've made that choice because is I considered to
130 * be much expensive to acquire/release more than one especially
131 * as the daemon is not multithreading.
134 /* This function has another parameter apart from shmget.
135 * The parameter has information about the type of sysv
136 * ipc resource (shm, sem, msg, undo).
137 * The undo segment is used for sem ops with UNDO flag set.
140 _shmget(key_t key, size_t size, int shmflg, int type)
142 struct shmget_msg msg;
143 struct shm_data *data;
144 int shmid, fd;
145 int flags;
147 SYSV_MUTEX_LOCK(&lock_resources);
148 if (shmcheck() < 0) {
149 sysv_print_err("init sysv ipc\n");
150 goto done;
153 msg.key = key;
154 msg.size = size;
155 msg.shmflg = shmflg;
156 msg.type = type;
158 send_message(daemon_fd, type, (char *)&msg, sizeof(msg));
160 /* Accept a file installed by the daemon.
161 * The file is used as shared memory. */
162 fd = receive_fd(daemon_fd);
163 if (fd < 0) {
164 shmid = -1;
165 goto done;
168 flags = _fcntl(fd, F_GETFD, 0);
169 if (_fcntl(fd, F_SETFD, flags & FD_CLOEXEC) == -1) {
170 sysv_print_err("fcntl error\n");
171 shmid = -1;
172 goto done;
175 /* Receive the resource id or error. */
176 receive_message(daemon_fd, (char *)&shmid, sizeof(shmid));
178 if (shmid < 0) {
179 errno = -shmid;
180 shmid = -1;
181 goto done;
184 /* Do we already have an entry for this resource? */
185 data = _hash_lookup(shmres, shmid);
186 if (data)
187 goto done;
189 /* If not, add necessary data about it. */
190 data = malloc(sizeof(struct shm_data));
191 data->fd = fd;
192 data->size = size;
193 data->shmid = shmid;
194 data->type = type;
195 data->used = 0;
196 data->removed = 0;
197 data->access = 0; /* Used for sems. */
199 /* Insert data in hashtable using the shmid. */
200 _hash_insert(shmres, shmid, data);
201 done:
202 SYSV_MUTEX_UNLOCK(&lock_resources);
203 return (shmid);
207 sysvipc_shmget(key_t key, size_t size, int shmflg)
209 return (_shmget(key, size, shmflg, SHMGET));
212 void *
213 sysvipc_shmat(int shmid, const void *shmaddr, int shmflg)
215 struct shmat_msg msg;
216 void *addr = NULL;
217 int error;
218 int flags, prot;
219 size_t size;
220 struct shm_data *data;
222 SYSV_MUTEX_LOCK(&lock_resources);
223 if (!is_shm_started()) {
224 errno = EINVAL;
225 goto done;
228 /* Get data using shmid. */
229 data = _hash_lookup(shmres, shmid);
230 if (data == NULL) {
231 errno = EINVAL;
232 goto done;
235 size = round_page(data->size);
237 #ifdef VM_PROT_READ_IS_EXEC
238 prot = PROT_READ | PROT_EXECUTE;
239 #else
240 prot = PROT_READ;
241 #endif
242 if ((shmflg & SHM_RDONLY) == 0)
243 prot |= PROT_WRITE;
245 flags = MAP_SHARED;
246 if (shmaddr) {
247 if (shmflg & SHM_RND) {
248 addr = (void *)(rounddown2((uintptr_t)shmaddr, SHMLBA));
249 } else if (((uintptr_t)shmaddr & (SHMLBA-1)) == 0) {
250 addr = __DECONST(void *, shmaddr);
251 } else {
252 errno = EINVAL;
253 goto done;
257 msg.shmid = shmid;
258 msg.shmaddr = shmaddr;
259 msg.shmflg = shmflg;
260 msg.size = data->size; /* For undo segment. */
262 send_message(daemon_fd, SHMAT, (char *)&msg, sizeof(msg));
263 receive_message(daemon_fd, (char *)&error, sizeof(error));
264 if (error) {
265 errno = error;
266 goto done;
269 addr = mmap(addr, size, prot, flags, data->fd, 0);
270 if (!addr) {
271 sysv_print_err("mmap\n");
272 /* Detach ourselves from the segment. */
273 send_message(daemon_fd, SHMDT, (char *)&shmid, sizeof(shmid));
274 goto done;
277 /* Necessary for SEMGET, MSGGET, UNDOGET. */
278 data->internal = addr;
280 /* Save the mapped address for munmap call. */
281 _hash_insert(shmaddrs, (u_long)addr, data);
282 done:
283 SYSV_MUTEX_UNLOCK(&lock_resources);
284 return (addr);
287 /* Remove a sysv ipc resource. */
288 static
289 void shmremove(int shmid)
291 struct shm_data *data;
292 data = _hash_remove(shmres, shmid);
294 //TODO nu trebuie demapat?
295 _close(data->fd);
296 free(data);
297 data = NULL;
301 sysvipc_shmctl(int shmid, int cmd, struct shmid_ds *buf)
303 int size, ret;
304 struct shmctl_msg *msg;
306 /* if (cmd == IPC_SET)
307 size = sizeof(struct shmctl_msg) + sizeof(struct shmid_ds);
308 else
309 size = sizeof(struct shmctl_msg);
311 SYSV_MUTEX_LOCK(&lock_resources);
313 ret = -1;
315 if (!is_shm_started()) {
316 errno = EINVAL;
317 goto done;
320 size = sizeof(struct shmctl_msg);
321 msg = malloc(size);
322 msg->shmid = shmid;
323 msg->cmd = cmd;
325 if (cmd == IPC_SET)
326 msg->buf = *buf;
328 send_message(daemon_fd, SHMCTL, (char *)msg, sizeof(*msg));
330 receive_message(daemon_fd, (char *)&ret, sizeof(ret));
332 /* Get data in IPC_STAT case. */
333 if (ret == 0 && cmd == IPC_STAT)
334 receive_message(daemon_fd, (char *)buf, sizeof(*buf));
336 /* Free all resources specific to a shmid in IPC_RMID case. */
337 if (ret == 0 && cmd == IPC_RMID)
338 shmremove(shmid);
340 errno = ret;
341 done:
342 SYSV_MUTEX_UNLOCK(&lock_resources);
343 return (ret == 0 ? 0 : -1);
346 /* Functionality of shmdt with the possibility to inform or not
347 * the daemon.
348 * Inform the daemon when shmdt is called and not when an error
349 * occurs and the daemon doesn't know that the process is attaced.
351 static int
352 _shmdt(const void *shmaddr, int send_to_daemon)
354 int ret;
355 size_t size;
356 struct shm_data *data;
358 ret = -1;
360 SYSV_MUTEX_LOCK(&lock_resources);
361 if (!is_shm_started()) {
362 errno = EINVAL;
363 goto done;
366 /* Verify if shmaddr was returned from a shmat call. */
367 data = _hash_remove(shmaddrs, (u_long)shmaddr);
368 if (data == NULL) {
369 errno = EINVAL;
370 goto done;
373 size = round_page(data->size);
375 ret = munmap(__DECONST(void *, shmaddr), size);
376 if (ret)
377 goto done;
379 if (send_to_daemon)
380 send_message(daemon_fd, SHMDT, (char *)&data->shmid, sizeof(int));
382 shmaddr = NULL;
383 free(data);
384 data = NULL;
385 done:
386 SYSV_MUTEX_UNLOCK(&lock_resources);
387 return (ret);
391 sysvipc_shmdt(const void *shmaddr)
393 return (_shmdt(shmaddr, 1));
396 void
397 shmchild(void)
399 int i;
400 struct entries_list *list;
401 struct hashentry *tmp, *ttmp;
402 struct shmat_msg msg;
403 struct shm_data *data;
404 int error;
406 /* OBS: no locking is necessary because this function is called
407 * after the child is created and at that moment only one thread
408 * exists in the process.
410 for (i=0; i<get_hash_size(MAXSIZE); i++) {
411 list = &shmaddrs->entries[i];
412 if (LIST_EMPTY(list))
413 continue;
414 LIST_FOREACH_MUTABLE(tmp, list, entry_link, ttmp) {
415 data = (struct shm_data*)tmp->value;
416 /* Inform daemon that we are attached. */
418 if (data->type == UNDOGET) {
419 continue;
422 msg.shmid = data->shmid;
423 msg.shmaddr = data->internal;
424 msg.shmflg = 0; /* This is enough at this moment. */
425 msg.size = data->size;
426 /* Last field is not necessary because it is used only
427 * for undo segments.
430 send_message(daemon_fd, SHMAT, (char *)&msg, sizeof(msg));
431 receive_message(daemon_fd, (char *)&error, sizeof(error));
433 /* If the daemon returned error munmap the region. */
434 if (error) {
435 errno = error;
436 _shmdt(data->internal, 0);
437 shmremove(data->shmid);
438 sysv_print_err(" %d shmchild\n", error);
439 sleep(20);
445 /* Remove semundo structures. Those are specific only for the parent.
446 * The child must create for itself a new one.
448 data = _hash_remove(shmaddrs, (u_long)undos);
449 if (undos) {
450 munmap(undos, round_page(data->size));
451 undos = NULL;
455 /* Called each time a thread tries to access the sem/msg.
456 * It is used in order to protect data against its removal
457 * by another thread.
459 struct shm_data *
460 get_shmdata(int id, int to_remove, int shm_access)
462 struct shm_data *data = NULL;
464 SYSV_MUTEX_LOCK(&lock_resources);
465 if (!is_shm_started()) {
466 errno = EINVAL;
467 goto done;
470 data = _hash_lookup(shmres, id);
471 if (!data) {
472 errno = EINVAL;
473 goto done;
476 /* If segment was removed by another thread we can't use it. */
477 if (data->removed) {
478 sysv_print("segment already removed\n");
479 errno = EINVAL;
480 data = NULL;
481 goto done;
484 /* Mark for removal. Inform the other threads from the
485 * same address space. */
486 if (to_remove) {
487 sysv_print("segment is removed\n");
488 data->removed = to_remove; /* 1 if it is removed by
489 the current process and 2 if it was removed by
490 another one. */
492 /* No need for any rights check because this is
493 * done by daemon if this is the process that removes
494 * the sem/msg.
495 * If not, there is no need for any right to clean
496 * internal resources.
498 goto done2;
501 /* Avoid segmentation fault if the memory zone
502 * is accessed without necessary permissions
503 * (it was mapped according to them).
505 if (!(data->access & shm_access)) {
506 #if 0
507 sysv_print("no access rights has %o and wants %o\n",
508 data->access, shm_access);
509 errno = EACCES;
510 data = NULL;
511 goto done;
512 #endif
515 done2:
516 data->used++;
517 done:
518 SYSV_MUTEX_UNLOCK(&lock_resources);
519 return (data);
522 /* Set the shm_access type (IPC_R, IPC_W) for sem/msg. */
524 set_shmdata_access(int id, int shm_access)
526 struct shm_data *data;
527 int ret = -1;
529 SYSV_MUTEX_LOCK(&lock_resources);
530 if (!is_shm_started()) {
531 errno = EINVAL;
532 goto done;
535 data = _hash_lookup(shmres, id);
536 if (!data) {
537 errno = EINVAL;
538 goto done;
541 /* If segment was removed by another thread we can't use it. */
542 if (data->removed) {
543 errno = EINVAL;
544 goto done;
547 data->access = shm_access;
548 ret = 0;
549 done:
550 SYSV_MUTEX_UNLOCK(&lock_resources);
552 return (ret);