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
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
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>
43 #include "un-namespace.h"
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"
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)
55 struct hashtable *shmres = NULL;
56 struct hashtable *shmaddrs = NULL;
57 pthread_mutex_t lock_resources = PTHREAD_MUTEX_INITIALIZER;
59 /* File descriptor used to communicate with the daemon. */
61 /* Structure used to save semaphore operation with SEMUNDO flag. */
62 extern struct sem_undo *undos;
71 shmres = _hash_init(MAXSIZE);
75 shmaddrs = _hash_init(MAXSIZE);
82 _hash_destroy(shmres);
92 _hash_destroy(shmres);
93 _hash_destroy(shmaddrs);
94 SYSV_MUTEX_DESTROY(lock_resources);
99 /* Init sysv ipc resources and those used for shared memory. */
104 /* Init sysv resources. */
105 if ((ret = sysvinit()) != 0)
107 /* Init resorces used for shared memory. */
108 if ((ret = shminit()) < 0)
113 /* Check if sysv ipc resources are initialized. */
115 is_shm_started(void) {
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.
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.
135 _shmget(key_t key, size_t size, int shmflg, int type) {
136 struct shmget_msg msg;
137 struct shm_data *data;
141 SYSV_MUTEX_LOCK(&lock_resources);
142 if (shmcheck() < 0) {
143 sysv_print_err("init sysv ipc\n");
152 send_message(daemon_fd, type, (char *)&msg, sizeof(msg));
154 /* Accept a file installed by the daemon.
155 * The file is used as shared memory. */
156 fd = receive_fd(daemon_fd);
162 flags = _fcntl(fd, F_GETFD, 0);
163 if (_fcntl(fd, F_SETFD, flags & FD_CLOEXEC) == -1) {
164 sysv_print_err("fcntl error\n");
169 /* Receive the resource id or error. */
170 receive_message(daemon_fd, (char *)&shmid, sizeof(shmid));
178 /* Do we already have an entry for this resource? */
179 data = _hash_lookup(shmres, shmid);
183 /* If not, add necessary data about it. */
184 data = malloc(sizeof(struct shm_data));
191 data->access = 0; /* Used for sems. */
193 /* Insert data in hashtable using the shmid. */
194 _hash_insert(shmres, shmid, data);
196 SYSV_MUTEX_UNLOCK(&lock_resources);
201 sysvipc_shmget(key_t key, size_t size, int shmflg) {
202 return (_shmget(key, size, shmflg, SHMGET));
206 sysvipc_shmat(int shmid, const void *shmaddr, int shmflg) {
207 struct shmat_msg msg;
212 struct shm_data *data;
214 SYSV_MUTEX_LOCK(&lock_resources);
215 if (!is_shm_started()) {
220 /* Get data using shmid. */
221 data = _hash_lookup(shmres, shmid);
227 size = round_page(data->size);
229 #ifdef VM_PROT_READ_IS_EXEC
230 prot = PROT_READ | PROT_EXECUTE;
234 if ((shmflg & SHM_RDONLY) == 0)
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);
250 msg.shmaddr = shmaddr;
252 msg.size = data->size; /* For undo segment. */
254 send_message(daemon_fd, SHMAT, (char *)&msg, sizeof(msg));
255 receive_message(daemon_fd, (char *)&error, sizeof(error));
261 addr = mmap(addr, size, prot, flags, data->fd, 0);
263 sysv_print_err("mmap\n");
264 /* Detach ourselves from the segment. */
265 send_message(daemon_fd, SHMDT, (char *)&shmid, sizeof(shmid));
269 /* Necessary for SEMGET, MSGGET, UNDOGET. */
270 data->internal = addr;
272 /* Save the mapped address for munmap call. */
273 _hash_insert(shmaddrs, (u_long)addr, data);
275 SYSV_MUTEX_UNLOCK(&lock_resources);
279 /* Remove a sysv ipc resource. */
281 void shmremove(int shmid) {
282 struct shm_data *data;
283 data = _hash_remove(shmres, shmid);
285 //TODO nu trebuie demapat?
292 sysvipc_shmctl(int shmid, int cmd, struct shmid_ds *buf) {
294 struct shmctl_msg *msg;
296 /* if (cmd == IPC_SET)
297 size = sizeof(struct shmctl_msg) + sizeof(struct shmid_ds);
299 size = sizeof(struct shmctl_msg);
301 SYSV_MUTEX_LOCK(&lock_resources);
305 if (!is_shm_started()) {
310 size = sizeof(struct shmctl_msg);
318 send_message(daemon_fd, SHMCTL, (char *)msg, sizeof(*msg));
320 receive_message(daemon_fd, (char *)&ret, sizeof(ret));
322 /* Get data in IPC_STAT case. */
323 if (ret == 0 && cmd == IPC_STAT)
324 receive_message(daemon_fd, (char *)buf, sizeof(*buf));
326 /* Free all resources specific to a shmid in IPC_RMID case. */
327 if (ret == 0 && cmd == IPC_RMID)
332 SYSV_MUTEX_UNLOCK(&lock_resources);
333 return (ret == 0 ? 0 : -1);
336 /* Functionality of shmdt with the possibility to inform or not
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.
342 _shmdt(const void *shmaddr, int send_to_daemon) {
345 struct shm_data *data;
349 SYSV_MUTEX_LOCK(&lock_resources);
350 if (!is_shm_started()) {
355 /* Verify if shmaddr was returned from a shmat call. */
356 data = _hash_remove(shmaddrs, (u_long)shmaddr);
362 size = round_page(data->size);
364 ret = munmap(__DECONST(void *, shmaddr), size);
369 send_message(daemon_fd, SHMDT, (char *)&data->shmid, sizeof(int));
375 SYSV_MUTEX_UNLOCK(&lock_resources);
380 sysvipc_shmdt(const void *shmaddr) {
381 return (_shmdt(shmaddr, 1));
387 struct entries_list *list;
388 struct hashentry *tmp, *ttmp;
389 struct shmat_msg msg;
390 struct shm_data *data;
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.
397 for (i=0; i<get_hash_size(MAXSIZE); i++) {
398 list = &shmaddrs->entries[i];
399 if (LIST_EMPTY(list))
401 LIST_FOREACH_MUTABLE(tmp, list, entry_link, ttmp) {
402 data = (struct shm_data*)tmp->value;
403 /* Inform daemon that we are attached. */
405 if (data->type == UNDOGET) {
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
417 send_message(daemon_fd, SHMAT, (char *)&msg, sizeof(msg));
418 receive_message(daemon_fd, (char *)&error, sizeof(error));
420 /* If the daemon returned error munmap the region. */
423 _shmdt(data->internal, 0);
424 shmremove(data->shmid);
425 sysv_print_err(" %d shmchild\n", error);
432 /* Remove semundo structures. Those are specific only for the parent.
433 * The child must create for itself a new one.
435 data = _hash_remove(shmaddrs, (u_long)undos);
437 munmap(undos, round_page(data->size));
442 /* Called each time a thread tries to access the sem/msg.
443 * It is used in order to protect data against its removal
447 get_shmdata(int id, int to_remove, int shm_access) {
448 struct shm_data *data = NULL;
450 SYSV_MUTEX_LOCK(&lock_resources);
451 if (!is_shm_started()) {
456 data = _hash_lookup(shmres, id);
462 /* If segment was removed by another thread we can't use it. */
464 sysv_print("segment already removed\n");
470 /* Mark for removal. Inform the other threads from the
471 * same address space. */
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
478 /* No need for any rights check because this is
479 * done by daemon if this is the process that removes
481 * If not, there is no need for any right to clean
482 * internal resources.
487 /* Avoid segmentation fault if the memory zone
488 * is accessed without necessary permissions
489 * (it was mapped according to them).
491 if (!(data->access & shm_access)) {
493 sysv_print("no access rights has %o and wants %o\n",
494 data->access, shm_access);
504 SYSV_MUTEX_UNLOCK(&lock_resources);
508 /* Set the shm_access type (IPC_R, IPC_W) for sem/msg. */
510 set_shmdata_access(int id, int shm_access) {
511 struct shm_data *data;
514 SYSV_MUTEX_LOCK(&lock_resources);
515 if (!is_shm_started()) {
520 data = _hash_lookup(shmres, id);
526 /* If segment was removed by another thread we can't use it. */
532 data->access = shm_access;
535 SYSV_MUTEX_UNLOCK(&lock_resources);