xref: /dragonfly/lib/libc/sysvipc/ipc.c (revision ff86f40163b90743b832c47e55fc6ca83aa45121)
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
send_message(int fd,int type,char * data,int size)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
receive_type_message(int fd)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
receive_message(int fd,char * data,int size)79 receive_message(int fd, char *data, int size)
80 {
81           _read(fd, data, size);
82           return (0);
83 }
84 
85 int
is_sysvinit(void)86 is_sysvinit(void)
87 {
88           return (daemon_fd == -1 ? 0:1);
89 }
90 
91 static int
register_to_daemon(void)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
acquire_locks(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
parent_release_locks(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
child_release_locks(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
prepare_parent_atfork(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
parent_atfork(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
child_atfork(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
sysvinit(void)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
sysvexit(void)273 sysvexit(void)
274 {
275           if (!is_sysvinit())
276                     return (-1);
277 
278           return (0);
279 }
280