nrelease - fix/improve livecd
[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 {
64         _write(fd, &type, sizeof(type));
65         return (send_msg_with_cred(fd, data, size));
66 }
67
68 /* Receive the type of the message that will follow. */
69 int
70 receive_type_message(int fd)
71 {
72         int type;
73         int r = _read(fd, &type, sizeof(type));
74         return (r == 0 ? 0 : type);
75 }
76
77 /* Receive data. */
78 int
79 receive_message(int fd, char *data, int size)
80 {
81         _read(fd, data, size);
82         return (0);
83 }
84
85 int
86 is_sysvinit(void)
87 {
88         return (daemon_fd == -1 ? 0:1);
89 }
90
91 static int
92 register_to_daemon(void)
93 {
94         int flags;
95         char test = 't';
96
97         daemon_fd = connect_to_daemon(LISTEN_SOCKET_FILE);
98
99         flags = _fcntl(daemon_fd, F_GETFD, 0);
100         if (_fcntl(daemon_fd, F_SETFD, flags & FD_CLOEXEC) == -1) {
101                 sysv_print_err("fcntl error\n");
102                 return (-1);
103         }
104
105         /* Send a message such that daemon can obtain process credentials.*/
106         send_msg_with_cred(daemon_fd, &test, sizeof(test));
107
108         sysv_print("register to daemon: sock fd = %d\n", daemon_fd);
109
110         return (0);
111 }
112
113 /* Used in fork case, to avoid deadlocks.
114  * The fork caller acquires all locks before fork and release them
115  * after because the child will have only a thread. If one lock is
116  * taken by another thread than, in the child process, nobody will
117  * release it.
118  */
119 static void
120 acquire_locks(void)
121 {
122         struct entries_list *list;
123         struct hashentry *tmp;
124         struct shm_data *data;
125         struct semid_pool *semaptr;
126         int i;
127
128         SYSV_MUTEX_LOCK(&lock_undo);
129         SYSV_MUTEX_LOCK(&lock_resources);
130         //pthread_rwlock_wrlock(&rwlock_addrs);
131
132         for (i=0; i<get_hash_size(MAXSIZE); i++) {
133                 list = &shmaddrs->entries[i];
134                 if (LIST_EMPTY(list))
135                         continue;
136                 LIST_FOREACH(tmp, list, entry_link) {
137                         data = (struct shm_data*)tmp->value;
138                         if (data->type == SEMGET) {
139                                 semaptr = (struct semid_pool *)data->internal;
140 #ifdef SYSV_RWLOCK
141 #ifdef SYSV_SEMS
142                                 /* There is no need to acquire the mutexes from
143                                  * each semaphore in the group. It is enough
144                                  * to acquire the group lock in write mode.
145                                  */
146 #endif
147                                 sysv_rwlock_wrlock(&semaptr->rwlock);
148 #else
149                                 sysv_mutex_lock(&semaptr->mutex);
150 #endif
151                         }
152                 }
153         }
154 }
155
156 /* Function called by parent after fork to release locks
157  * acquired before fork.
158  */
159 static void
160 parent_release_locks(void)
161 {
162         struct entries_list *list;
163         struct hashentry *tmp;
164         struct shm_data *data;
165         struct semid_pool *semaptr;
166         int i;
167
168         SYSV_MUTEX_UNLOCK(&lock_undo);
169         SYSV_MUTEX_UNLOCK(&lock_resources);
170         //pthread_rwlock_unlock(&rwlock_addrs);
171
172         for (i=0; i<get_hash_size(MAXSIZE); i++) {
173                 list = &shmaddrs->entries[i];
174                 if (LIST_EMPTY(list))
175                         continue;
176                 LIST_FOREACH(tmp, list, entry_link) {
177                         data = (struct shm_data*)tmp->value;
178                         if (data->type == SEMGET) {
179                                 semaptr = (struct semid_pool *)data->internal;
180 #ifdef SYSV_RWLOCK
181                                 sysv_rwlock_unlock(&semaptr->rwlock);
182 #else
183                                 sysv_mutex_unlock(&semaptr->mutex);
184 #endif
185                         }
186                 }
187         }
188 }
189
190 /* Function called by child after fork to release locks
191  * acquired before fork by the parent.
192  * Only locks specific to the address space are released.
193  * Those created in the shared memory are released by the
194  * parent.
195  */
196 static void
197 child_release_locks(void)
198 {
199         SYSV_MUTEX_UNLOCK(&lock_undo);
200         SYSV_MUTEX_UNLOCK(&lock_resources);
201         //pthread_rwlock_unlock(&rwlock_addrs);
202 }
203
204 static void
205 prepare_parent_atfork(void)
206 {
207         /* Function called only if the process has
208          * sysv ipc structures initialized.
209          */
210         if (!is_sysvinit())
211                 return;
212
213         /* Acquire all locks to be sure that neither one is
214          * held by another thread.
215          */
216         acquire_locks();
217 }
218
219 static void
220 parent_atfork(void)
221 {
222         if (!is_sysvinit())
223                 return;
224
225         /* Release locks acquired before fork. */
226         parent_release_locks();
227 }
228
229 static void
230 child_atfork(void)
231 {
232         if (!is_sysvinit())
233                 return;
234
235         /* Release locks acquired before fork. */
236         child_release_locks();
237         /* Close the file descriptor used by parent. */
238         _close(daemon_fd);
239
240         /* Register it to daemon too. */
241         if (register_to_daemon() < 0) {
242                 sysv_print_err("register to daemon error\n");
243                 exit(-1);
244         }
245
246         /* Inform the daemon about each shared memory segment used. */
247         shmchild();
248 }
249
250 /* The function is called only once, when the process uses for
251  * the first time sysv ipc resources.
252  */
253 int
254 sysvinit(void)
255 {
256         if (is_sysvinit()) {
257                 return (IPC_INITIALIZED);
258         }
259
260         if (register_to_daemon() < 0)
261                 return (-1);
262
263         /* Add handlers for parent and child when fork is called. */
264         if (_pthread_atfork(prepare_parent_atfork, parent_atfork,
265                                 child_atfork) < 0) {
266                 sysv_print_err("pthread_atfork error\n");
267                 return (-1);
268         }
269         return 0;
270 }
271
272 int
273 sysvexit(void)
274 {
275         if (!is_sysvinit())
276                 return (-1);
277
278         return (0);
279 }