Bring in GSoC code: SysV IPC in userspace
[dragonfly.git] / usr.sbin / sysvipcd / sysvipcd.c
1 /**
2  * Copyright (c) 2013 Larisa Grigore.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  * 3. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include <sys/shm.h>
28 #include <sys/stat.h>
29
30 #include <err.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <poll.h>
38
39 #include "sysvipc_hash.h"
40 #include "sysvipc_sockets.h"
41 #include "utilsd.h"
42 #include "shmd.h"
43
44 #define MAX_CLIENTS     256
45
46 void usage(void);
47
48
49 struct pollfd poll_fds[MAX_CLIENTS];
50 struct client *clients[MAX_CLIENTS];
51 int nr_poll_fds;
52
53 struct hashtable *clientshash = NULL;
54
55 int sysvd_debug;
56 int sysvd_daemon;
57 const char *pidfile = "/var/run/sysvpicd.pid";
58
59 static int
60 remove_sysv_dir(void) 
61 {
62         /*
63          * It is not necessary to check if the dir is empty and delete all files
64          * in it. Every time a client or the daemon exists all fds are closed
65          * and all resources are deleted (the daemon calls unlink after open a
66          * file for a sysv resource.
67          */
68         return (rmdir(DIRPATH));
69 }
70
71 static int
72 create_sysv_dir(void) 
73 {
74         remove_sysv_dir();
75         return (mkdir(DIRPATH, 0600));
76 }
77
78 static int
79 daemon_init(void)
80 {
81         int error;
82         int socket_fd;
83
84         /* Create and init structures used for clients. */
85         clientshash = _hash_init(MAX_CLIENTS);
86         if (!clientshash)
87                 return (-1);
88
89         /* Create sysv resources directory. */
90         error = create_sysv_dir();
91         if (error) {
92                 sysvd_print_err("You must first remove %s dir\n",
93                                 DIRPATH);
94                 goto err;
95         }
96
97         /* Open socket used to receive connections. */
98         unlink(LISTEN_SOCKET_FILE);
99         umask(0);
100         int fd_tmp = open(LISTEN_SOCKET_FILE, O_EXCL | O_CREAT, 0666);
101         if (fd_tmp < 0) {
102                 sysvd_print_err("Could not open %s\n", LISTEN_SOCKET_FILE);
103                 goto err;
104         }
105         close(fd_tmp);
106
107         socket_fd = init_socket(LISTEN_SOCKET_FILE);
108         if (socket_fd < 0) {
109                 sysvd_print_err("Could not init %s socket\n", LISTEN_SOCKET_FILE);
110                 goto err;
111         }
112
113         poll_fds[SOCKET_FD_IDX].fd = socket_fd;
114         poll_fds[SOCKET_FD_IDX].events = POLLIN | POLLPRI;
115         poll_fds[SOCKET_FD_IDX].revents = 0;
116         nr_poll_fds++;
117
118         shminit();
119
120         return (0);
121 err:
122         free(clientshash);
123         return (-1);
124 }
125
126 static int
127 daemon_add_client(void) 
128 {
129         struct client *cl;
130         //int on = 1;
131         struct cmsgcred cred;
132         char test;
133
134         cl = malloc(sizeof(*cl));
135         if (!cl) {
136                 sysvd_print_err("malloc");
137                 return (-1);
138         }
139
140         cl->undoid = -1;
141
142         /* Segments attached to a process. It is used
143          * when the process dies.
144          */
145         LIST_INIT(&cl->ids_attached);
146
147         /* Init communication channel between daemon and client. */
148         cl->sock = handle_new_connection(poll_fds[SOCKET_FD_IDX].fd);
149
150         poll_fds[nr_poll_fds].fd = cl->sock;
151         poll_fds[nr_poll_fds].events = POLLIN;
152         poll_fds[nr_poll_fds].revents = 0;
153
154         clients[nr_poll_fds] = cl;
155         nr_poll_fds++;
156
157         if(nr_poll_fds == MAX_CLIENTS) {
158                 sysvd_print_err("No room for another client; connection refused\n");
159                 poll_fds[SOCKET_FD_IDX].events = 0;
160         }
161
162         /* Get the client pid. */
163         receive_msg_with_cred(cl->sock, &test, sizeof(test), &cred);
164         cl->pid = cred.cmcred_pid;
165
166         sysvd_print("total = %d...another one will be added\n", nr_poll_fds);
167         sysvd_print("pid = %d conected\n", cl->pid);
168
169         /* Verify if the client is already connected using the hashtable. */
170         if (_hash_lookup(clientshash, cl->pid)) {
171                 errno = EEXIST;
172                 sysvd_print_err("client already added");
173                 free(cl);
174                 return (-1);
175         }
176
177         /* Insert client in hashtable.  */
178         _hash_insert(clientshash, cl->pid, cl);
179
180         return (0);
181 }
182
183 static void
184 daemon_remove_client(int i) 
185 {
186
187         struct client *cl = clients[i];
188         sysvd_print("pid %d disconected\n", cl->pid);
189         sysvd_print("total = %d\n", nr_poll_fds);
190
191         /* Close communication channels. */
192         close(cl->sock);
193
194         /* Put last client on i position. */
195         if (i != nr_poll_fds - 1) {
196                 poll_fds[i] = poll_fds[nr_poll_fds - 1];
197                 clients[i] = clients[nr_poll_fds - 1];
198         }
199
200         semexit(cl->undoid);
201         shmexit(cl);
202
203         _hash_remove(clientshash, cl->pid);
204         nr_poll_fds--;
205         free(cl);
206         cl = NULL;
207
208         if(nr_poll_fds == MAX_CLIENTS - 1) {
209                 sysvd_print_err("Now another connexion can be handled\n");
210                 poll_fds[SOCKET_FD_IDX].events = POLLIN | POLLPRI;
211         }
212 }
213
214 static int
215 daemon_handle_msg(int i) 
216 {
217         int msg_type;
218         struct shmget_msg shmget_msg;
219         struct shmctl_msg shmctl_msg;
220         struct shmat_msg shmat_msg;
221         int shmid;
222         int error;
223         struct cmsgcred cred;
224
225         int fd_send, fd_recv;
226         fd_send = fd_recv = clients[i]->sock;
227
228         msg_type = receive_type_message(fd_recv);
229         sysvd_print("type = %d from %d\n", msg_type, clients[i]->pid);
230
231         switch(msg_type) {
232                 case CONNEXION_CLOSED:
233                         sysvd_print("connection closed\n");
234                         return (EOF);
235                 case SHMGET:
236                 case SEMGET:
237                 case MSGGET:
238                 case UNDOGET:
239                         receive_msg_with_cred(fd_recv, (char *)&shmget_msg,
240                                         sizeof(shmget_msg), &cred);
241                         shmid = handle_shmget(clients[i]->pid,
242                                         &shmget_msg, &cred);
243
244                         /* Send the shmid. */
245                         write(fd_send, (char *)&shmid,
246                                         sizeof(shmid));
247                         sysvd_print("sent %d to client %d\n",
248                                         shmid, clients[i]->pid);
249                         break;
250                 case SHMAT:
251                         receive_msg_with_cred(fd_recv, (char *)&shmat_msg,
252                                         sizeof(shmat_msg), &cred);
253                         error = handle_shmat(clients[i]->pid,
254                                         &shmat_msg, &cred);
255
256                         /* Send the error after few checks. */
257                         write(fd_send, (char *)&error,
258                                         sizeof(error));
259                         break;
260                 case SHMCTL:
261                         receive_msg_with_cred(fd_recv, (char *)&shmctl_msg,
262                                         sizeof(shmctl_msg), &cred);
263                         error = handle_shmctl(&shmctl_msg, &cred);
264
265                         /* Send the error after few checks. */
266                         write(fd_send, (char *)&error,
267                                         sizeof(error));
268                         if (error == 0 && shmctl_msg.cmd == IPC_STAT) {
269
270                                 write(fd_send, (char *)&shmctl_msg.buf,
271                                                 sizeof(struct shmid_ds));
272                         }
273                         break;
274                 case SHMDT:
275                         receive_msg_with_cred(fd_recv, (char *)&shmid,
276                                         sizeof(shmid), NULL);
277                         shmid = handle_shmdt(clients[i]->pid, shmid);
278                         break;
279                 default:
280                         break;
281         }
282         sysvd_print("end\n");
283         return (0);
284 }
285
286
287 static int
288 daemon_func(void)
289 {
290         int i;
291         //int msg;
292         int ret, r;
293
294         while(1)
295         {
296                 ret = poll(poll_fds, nr_poll_fds, INFTIM);
297                 if (ret < 0) {
298                         sysvd_print_err("poll");
299                         return (-1);
300                 }
301                 for (i=0; (i < nr_poll_fds) && ret; i++) {
302                         if (poll_fds[i].revents == 0)
303                                 continue;
304                         ret--;
305
306                         switch(i) {
307                         case SOCKET_FD_IDX:
308                                 daemon_add_client();
309                                 break;
310                         default:
311                                 r = daemon_handle_msg(i);
312                                 if (r == EOF) {
313                                         daemon_remove_client(i);
314                                         i--;
315                                 }
316                                 break;
317                         }
318                 }
319                 fflush(stdout);
320         }
321
322         return (0);
323 }
324
325 void
326 usage(void)
327 {
328         return;
329 }
330
331 int
332 main(int argc, char *argv[])
333 {
334         int c;
335         int error;
336
337         sysvd_debug = 0;
338         sysvd_daemon = 1;
339
340         while ((c = getopt(argc,argv,"df:p")) !=-1) {
341                 switch(c) {
342                 case 'd':
343                         sysvd_debug = 1;
344                         break;
345                 case 'f':
346                         sysvd_daemon = 0;
347                         break;
348                 case 'p':
349                         pidfile = optarg;
350                         break;
351                 default:
352                         usage();
353                         break;
354                 }
355         }
356
357 #ifdef SYSV_SEMS
358         sysvd_print_err("SYSV_SEMS defined (used for sysv sems; a group of semaphores is protected)"
359                         "by a rwlock and each semaphore is protected by a mutex\n");
360 #else
361         sysvd_print_err("SYSV_SEMS not defined (used for sysv sems; a group of semaphores is protected)"
362                         "by a rwlock\n");
363 #endif
364
365         sysvd_print("daemon starting\n");
366         error = daemon_init();
367         if (error)
368                 goto out;
369                 
370         if (sysvd_daemon == 1) {
371                 daemon(1,0);
372         }
373
374         daemon_func();
375
376         /* It won't reach here. */
377         sysvd_print("daemon finished\n");
378
379         //shmfree();
380         remove_sysv_dir();
381 out:
382         return (0);
383 }