kernel - Fix vmspace termination race (2)
[dragonfly.git] / usr.sbin / sysvipcd / sysvipcd.c
blob19620d2263b48090762fefab29a9a13eb7fab350
1 /**
2 * Copyright (c) 2013 Larisa Grigore. All rights reserved.
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.
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.
27 #include <sys/shm.h>
28 #include <sys/stat.h>
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 #include <sysexits.h>
39 #include <libutil.h>
41 #include "sysvipc_hash.h"
42 #include "sysvipc_sockets.h"
43 #include "utilsd.h"
44 #include "shmd.h"
46 #define MAX_CLIENTS 256
48 void usage(void);
51 struct pollfd poll_fds[MAX_CLIENTS];
52 struct client *clients[MAX_CLIENTS];
53 int nr_poll_fds;
55 struct hashtable *clientshash = NULL;
57 int sysvd_debug;
58 int sysvd_daemon;
60 static int
61 remove_sysv_dir(void)
64 * It is not necessary to check if the dir is empty and delete all files
65 * in it. Every time a client or the daemon exists all fds are closed
66 * and all resources are deleted (the daemon calls unlink after open a
67 * file for a sysv resource.
69 return (rmdir(DIRPATH));
72 static int
73 create_sysv_dir(void)
75 remove_sysv_dir();
76 return (mkdir(DIRPATH, 0600));
79 static int
80 daemon_init(void)
82 int error;
83 int socket_fd;
85 /* Create and init structures used for clients. */
86 clientshash = _hash_init(MAX_CLIENTS);
87 if (!clientshash)
88 return (-1);
90 /* Create sysv resources directory. */
91 error = create_sysv_dir();
92 if (error) {
93 sysvd_print_err("You must first remove %s dir\n",
94 DIRPATH);
95 goto err;
98 /* Open socket used to receive connections. */
99 unlink(LISTEN_SOCKET_FILE);
100 umask(0);
101 int fd_tmp = open(LISTEN_SOCKET_FILE, O_EXCL | O_CREAT, 0666);
102 if (fd_tmp < 0) {
103 sysvd_print_err("Could not open %s\n", LISTEN_SOCKET_FILE);
104 goto err;
106 close(fd_tmp);
108 socket_fd = init_socket(LISTEN_SOCKET_FILE);
109 if (socket_fd < 0) {
110 sysvd_print_err("Could not init %s socket\n", LISTEN_SOCKET_FILE);
111 goto err;
114 poll_fds[SOCKET_FD_IDX].fd = socket_fd;
115 poll_fds[SOCKET_FD_IDX].events = POLLIN | POLLPRI;
116 poll_fds[SOCKET_FD_IDX].revents = 0;
117 nr_poll_fds++;
119 shminit();
121 return (0);
122 err:
123 free(clientshash);
124 return (-1);
127 static int
128 daemon_add_client(void)
130 struct client *cl;
131 //int on = 1;
132 struct cmsgcred cred;
133 char test;
135 cl = malloc(sizeof(*cl));
136 if (!cl) {
137 sysvd_print_err("malloc");
138 return (-1);
141 cl->undoid = -1;
143 /* Segments attached to a process. It is used
144 * when the process dies.
146 LIST_INIT(&cl->ids_attached);
148 /* Init communication channel between daemon and client. */
149 cl->sock = handle_new_connection(poll_fds[SOCKET_FD_IDX].fd);
151 poll_fds[nr_poll_fds].fd = cl->sock;
152 poll_fds[nr_poll_fds].events = POLLIN;
153 poll_fds[nr_poll_fds].revents = 0;
155 clients[nr_poll_fds] = cl;
156 nr_poll_fds++;
158 if(nr_poll_fds == MAX_CLIENTS) {
159 sysvd_print_err("No room for another client; connection refused\n");
160 poll_fds[SOCKET_FD_IDX].events = 0;
163 /* Get the client pid. */
164 receive_msg_with_cred(cl->sock, &test, sizeof(test), &cred);
165 cl->pid = cred.cmcred_pid;
167 sysvd_print("total = %d...another one will be added\n", nr_poll_fds);
168 sysvd_print("pid = %d connected\n", cl->pid);
170 /* Verify if the client is already connected using the hashtable. */
171 if (_hash_lookup(clientshash, cl->pid)) {
172 errno = EEXIST;
173 sysvd_print_err("client already added");
174 free(cl);
175 return (-1);
178 /* Insert client in hashtable. */
179 _hash_insert(clientshash, cl->pid, cl);
181 return (0);
184 static void
185 daemon_remove_client(int i)
188 struct client *cl = clients[i];
189 sysvd_print("pid %d disconected\n", cl->pid);
190 sysvd_print("total = %d\n", nr_poll_fds);
192 /* Close communication channels. */
193 close(cl->sock);
195 /* Put last client on i position. */
196 if (i != nr_poll_fds - 1) {
197 poll_fds[i] = poll_fds[nr_poll_fds - 1];
198 clients[i] = clients[nr_poll_fds - 1];
201 semexit(cl->undoid);
202 shmexit(cl);
204 _hash_remove(clientshash, cl->pid);
205 nr_poll_fds--;
206 free(cl);
207 cl = NULL;
209 if(nr_poll_fds == MAX_CLIENTS - 1) {
210 sysvd_print_err("Now another connexion can be handled\n");
211 poll_fds[SOCKET_FD_IDX].events = POLLIN | POLLPRI;
215 static int
216 daemon_handle_msg(int i)
218 int msg_type;
219 struct shmget_msg shmget_msg;
220 struct shmctl_msg shmctl_msg;
221 struct shmat_msg shmat_msg;
222 int shmid;
223 int error;
224 struct cmsgcred cred;
226 int fd_send, fd_recv;
227 fd_send = fd_recv = clients[i]->sock;
229 msg_type = receive_type_message(fd_recv);
230 sysvd_print("type = %d from %d\n", msg_type, clients[i]->pid);
232 switch(msg_type) {
233 case CONNEXION_CLOSED:
234 sysvd_print("connection closed\n");
235 return (EOF);
236 case SHMGET:
237 case SEMGET:
238 case MSGGET:
239 case UNDOGET:
240 receive_msg_with_cred(fd_recv, (char *)&shmget_msg,
241 sizeof(shmget_msg), &cred);
242 shmid = handle_shmget(clients[i]->pid,
243 &shmget_msg, &cred);
245 /* Send the shmid. */
246 write(fd_send, (char *)&shmid,
247 sizeof(shmid));
248 sysvd_print("sent %d to client %d\n",
249 shmid, clients[i]->pid);
250 break;
251 case SHMAT:
252 receive_msg_with_cred(fd_recv, (char *)&shmat_msg,
253 sizeof(shmat_msg), &cred);
254 error = handle_shmat(clients[i]->pid,
255 &shmat_msg, &cred);
257 /* Send the error after few checks. */
258 write(fd_send, (char *)&error,
259 sizeof(error));
260 break;
261 case SHMCTL:
262 receive_msg_with_cred(fd_recv, (char *)&shmctl_msg,
263 sizeof(shmctl_msg), &cred);
264 error = handle_shmctl(&shmctl_msg, &cred);
266 /* Send the error after few checks. */
267 write(fd_send, (char *)&error,
268 sizeof(error));
269 if (error == 0 && shmctl_msg.cmd == IPC_STAT) {
271 write(fd_send, (char *)&shmctl_msg.buf,
272 sizeof(struct shmid_ds));
274 break;
275 case SHMDT:
276 receive_msg_with_cred(fd_recv, (char *)&shmid,
277 sizeof(shmid), NULL);
278 shmid = handle_shmdt(clients[i]->pid, shmid);
279 break;
280 default:
281 break;
283 sysvd_print("end\n");
284 return (0);
288 static int
289 daemon_func(void)
291 int i;
292 //int msg;
293 int ret, r;
295 while(1)
297 ret = poll(poll_fds, nr_poll_fds, INFTIM);
298 if (ret < 0) {
299 sysvd_print_err("poll");
300 return (-1);
302 for (i=0; (i < nr_poll_fds) && ret; i++) {
303 if (poll_fds[i].revents == 0)
304 continue;
305 ret--;
307 switch(i) {
308 case SOCKET_FD_IDX:
309 daemon_add_client();
310 break;
311 default:
312 r = daemon_handle_msg(i);
313 if (r == EOF) {
314 daemon_remove_client(i);
315 i--;
317 break;
320 fflush(stdout);
323 return (0);
326 void
327 usage(void)
329 fprintf(stderr, "sysvipcd [-df] [-p pidfile]\n");
330 exit(EX_USAGE);
334 main(int argc, char *argv[])
336 int c;
337 int error;
338 char *pidfilename = NULL;
339 struct pidfh *pfh = NULL;
341 sysvd_debug = 0;
342 sysvd_daemon = 1;
344 while ((c = getopt(argc,argv,"dfp:")) !=-1) {
345 switch(c) {
346 case 'd':
347 sysvd_debug = 1;
348 sysvd_daemon = 0;
349 break;
350 case 'f':
351 sysvd_daemon = 0;
352 break;
353 case 'p':
354 pidfilename = optarg;
355 break;
356 default:
357 usage();
358 break;
362 #ifdef SYSV_SEMS
363 sysvd_print("SYSV_SEMS defined (used for sysv sems); "
364 "a group of semaphores is protected)\n"
365 "by a rwlock and each semaphore is protected by a mutex\n");
366 #else
367 sysvd_print("SYSV_SEMS not defined (used for sysv sems); "
368 "a group of semaphores is protected)\n"
369 "by a rwlock\n");
370 #endif
372 sysvd_print("daemon starting\n");
373 error = daemon_init();
374 if (error)
375 goto out;
377 if (sysvd_daemon == 1) {
378 pfh = pidfile_open(pidfilename, 600, NULL);
379 daemon(1,0);
380 pidfile_write(pfh);
383 daemon_func();
385 /* It won't reach here. */
386 sysvd_print("daemon finished\n");
388 //shmfree();
389 remove_sysv_dir();
390 out:
391 return (0);