Merge branch 'vendor/TRE'
[dragonfly.git] / lib / libc / sysvipc / ipc.c
1 /**
2  * Copyright (c) 2013 Larisa Grigore<larisagrigore@gmail.com>.
3  * All rights reserved.
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. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "namespace.h"
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/ioctl.h>
33 #include <pthread.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include "un-namespace.h"
38
39 #include <stdio.h>
40
41 #include "sysvipc_ipc.h"
42 #include "sysvipc_sockets.h"
43 #include "sysvipc_sem.h"
44 #include "sysvipc_shm.h"
45 #include "sysvipc_hash.h"
46 #include "sysvipc_lock.h"
47 #include "sysvipc_lock_generic.h"
48
49 #define SYSV_MUTEX_LOCK(x)              if (__isthreaded) _pthread_mutex_lock(x)
50 #define SYSV_MUTEX_UNLOCK(x)    if (__isthreaded) _pthread_mutex_unlock(x)
51 #define SYSV_MUTEX_DESTROY(x)   if (__isthreaded) _pthread_mutex_destroy(x)
52
53 int daemon_fd = -1;
54
55 extern pthread_mutex_t lock_resources;
56 //extern pthread_rwlock_t rwlock_addrs;
57 extern pthread_mutex_t lock_undo;
58 extern struct hashtable *shmaddrs;
59
60 /* Send the type of the message followed by data. */
61 int
62 send_message(int fd, int type, char *data, int size) {
63         _write(fd, &type, sizeof(type));
64         return (send_msg_with_cred(fd, data, size));
65 }
66
67 /* Receive the type of the message that will follow. */
68 int
69 receive_type_message(int fd) {
70         int type;
71         int r = _read(fd, &type, sizeof(type));
72         return (r == 0 ? 0 : type);
73 }
74
75 /* Receive data. */
76 int
77 receive_message(int fd, char *data, int size) {
78         _read(fd, data, size);
79         return (0);
80 }
81
82 int
83 is_sysvinit(void) {
84         return (daemon_fd == -1 ? 0:1);
85 }
86
87 static int
88 register_to_daemon(void) {
89         int flags;
90         char test = 't';
91
92         daemon_fd = connect_to_daemon(LISTEN_SOCKET_FILE);
93
94         flags = _fcntl(daemon_fd, F_GETFD, 0);
95         if (_fcntl(daemon_fd, F_SETFD, flags & FD_CLOEXEC) == -1) {
96                 sysv_print_err("fcntl error\n");
97                 return (-1);
98         }
99
100         /* Send a message such that daemon can obtain process credentials.*/
101         send_msg_with_cred(daemon_fd, &test, sizeof(test));
102
103         sysv_print("register to daemon: sock fd = %d\n", daemon_fd);
104
105         return (0);
106 }
107
108 /* Used in fork case, to avoid deadlocks.
109  * The fork caller acquires all locks before fork and release them
110  * after because the child will have only a thread. If one lock is
111  * taken by another thread than, in the child process, nobody will
112  * release it.
113  */
114 static void
115 acquire_locks(void) {
116         struct entries_list *list;
117         struct hashentry *tmp;
118         struct shm_data *data;
119         struct semid_pool *semaptr;
120         int i;
121
122         SYSV_MUTEX_LOCK(&lock_undo);
123         SYSV_MUTEX_LOCK(&lock_resources);
124         //pthread_rwlock_wrlock(&rwlock_addrs);
125
126         for (i=0; i<get_hash_size(MAXSIZE); i++) {
127                 list = &shmaddrs->entries[i];
128                 if (LIST_EMPTY(list))
129                         continue;
130                 LIST_FOREACH(tmp, list, entry_link) {
131                         data = (struct shm_data*)tmp->value;
132                         if (data->type == SEMGET) {
133                                 semaptr = (struct semid_pool *)data->internal;
134 #ifdef SYSV_RWLOCK
135 #ifdef SYSV_SEMS
136                                 /* There is no need to acquire the mutexes from
137                                  * each semaphore in the group. It is enough
138                                  * to acquire the group lock in write mode.
139                                  */
140 #endif
141                                 sysv_rwlock_wrlock(&semaptr->rwlock);
142 #else
143                                 sysv_mutex_lock(&semaptr->mutex);
144 #endif
145                         }
146                 }
147         }
148 }
149
150 /* Function called by parent after fork to release locks
151  * acquired before fork.
152  */
153 static void
154 parent_release_locks(void) {
155         struct entries_list *list;
156         struct hashentry *tmp;
157         struct shm_data *data;
158         struct semid_pool *semaptr;
159         int i;
160
161         SYSV_MUTEX_UNLOCK(&lock_undo);
162         SYSV_MUTEX_UNLOCK(&lock_resources);
163         //pthread_rwlock_unlock(&rwlock_addrs);
164
165         for (i=0; i<get_hash_size(MAXSIZE); i++) {
166                 list = &shmaddrs->entries[i];
167                 if (LIST_EMPTY(list))
168                         continue;
169                 LIST_FOREACH(tmp, list, entry_link) {
170                         data = (struct shm_data*)tmp->value;
171                         if (data->type == SEMGET) {
172                                 semaptr = (struct semid_pool *)data->internal;
173 #ifdef SYSV_RWLOCK
174                                 sysv_rwlock_unlock(&semaptr->rwlock);
175 #else
176                                 sysv_mutex_unlock(&semaptr->mutex);
177 #endif
178                         }
179                 }
180         }
181 }
182
183 /* Function called by child after fork to release locks
184  * acquired before fork by the parent.
185  * Only locks specific to the address space are released.
186  * Those created in the shared memory are released by the
187  * parent.
188  */
189 static void
190 child_release_locks(void) {
191         SYSV_MUTEX_UNLOCK(&lock_undo);
192         SYSV_MUTEX_UNLOCK(&lock_resources);
193         //pthread_rwlock_unlock(&rwlock_addrs);
194 }
195
196 static void
197 prepare_parent_atfork(void) {
198         /* Function called only if the process has
199          * sysv ipc structures initialized.
200          */
201         if (!is_sysvinit())
202                 return;
203
204         /* Acquire all locks to be sure that neither one is
205          * held by another thread.
206          */
207         acquire_locks();
208 }
209
210 static void
211 parent_atfork(void) {
212         if (!is_sysvinit())
213                 return;
214
215         /* Release locks acquired before fork. */
216         parent_release_locks();
217 }
218
219 static void
220 child_atfork(void) {
221         if (!is_sysvinit())
222                 return;
223
224         /* Release locks acquired before fork. */
225         child_release_locks();
226         /* Close the file descriptor used by parent. */
227         _close(daemon_fd);
228
229         /* Register it to daemon too. */
230         if (register_to_daemon() < 0) {
231                 sysv_print_err("register to daemon error\n");
232                 exit(-1);
233         }
234
235         /* Inform the daemon about each shared memory segment used. */
236         shmchild();
237 }
238
239 /* The function is called only once, when the process uses for
240  * the first time sysv ipc resources.
241  */
242 int
243 sysvinit(void) {
244
245         if (is_sysvinit()) {
246                 return (IPC_INITIALIZED);
247         }
248
249         if (register_to_daemon() < 0)
250                 return (-1);
251
252         /* Add handlers for parent and child when fork is called. */
253         if (_pthread_atfork(prepare_parent_atfork, parent_atfork,
254                                 child_atfork) < 0) {
255                 sysv_print_err("pthread_atfork error\n");
256                 return (-1);
257         }
258         return 0;
259 }
260
261 int
262 sysvexit(void) {
263         if (!is_sysvinit())
264                 return (-1);
265
266         return (0);
267 }