xref: /dragonfly/lib/libc/sysvipc/ipc.c (revision e6d22e9b)
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 }
280