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