xref: /dragonfly/usr.sbin/sysvipcd/sysvipcd.c (revision 1310e0bb)
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 #include <sysexits.h>
39 #include <libutil.h>
40 
41 #include "sysvipc_hash.h"
42 #include "sysvipc_sockets.h"
43 #include "utilsd.h"
44 #include "shmd.h"
45 
46 #define MAX_CLIENTS	256
47 
48 void usage(void) __dead2;
49 
50 
51 struct pollfd poll_fds[MAX_CLIENTS];
52 struct client *clients[MAX_CLIENTS];
53 int nr_poll_fds;
54 
55 struct hashtable *clientshash = NULL;
56 
57 int sysvd_debug;
58 int sysvd_daemon;
59 
60 static int
61 remove_sysv_dir(void)
62 {
63 	/*
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.
68 	 */
69 	return (rmdir(DIRPATH));
70 }
71 
72 static int
73 create_sysv_dir(void)
74 {
75 	remove_sysv_dir();
76 	return (mkdir(DIRPATH, 0600));
77 }
78 
79 static int
80 daemon_init(void)
81 {
82 	int error;
83 	int socket_fd;
84 
85 	/* Create and init structures used for clients. */
86 	clientshash = _hash_init(MAX_CLIENTS);
87 	if (!clientshash)
88 		return (-1);
89 
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;
96 	}
97 
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;
105 	}
106 	close(fd_tmp);
107 
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;
112 	}
113 
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++;
118 
119 	shminit();
120 
121 	return (0);
122 err:
123 	free(clientshash);
124 	return (-1);
125 }
126 
127 static int
128 daemon_add_client(void)
129 {
130 	struct client *cl;
131 	//int on = 1;
132 	struct cmsgcred cred;
133 	char test;
134 
135 	cl = malloc(sizeof(*cl));
136 	if (!cl) {
137 		sysvd_print_err("malloc");
138 		return (-1);
139 	}
140 
141 	cl->undoid = -1;
142 
143 	/* Segments attached to a process. It is used
144 	 * when the process dies.
145 	 */
146 	LIST_INIT(&cl->ids_attached);
147 
148 	/* Init communication channel between daemon and client. */
149 	cl->sock = handle_new_connection(poll_fds[SOCKET_FD_IDX].fd);
150 
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;
154 
155 	clients[nr_poll_fds] = cl;
156 	nr_poll_fds++;
157 
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;
161 	}
162 
163 	/* Get the client pid. */
164 	receive_msg_with_cred(cl->sock, &test, sizeof(test), &cred);
165 	cl->pid = cred.cmcred_pid;
166 
167 	sysvd_print("total = %d...another one will be added\n", nr_poll_fds);
168 	sysvd_print("pid = %d connected\n", cl->pid);
169 
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);
176 	}
177 
178 	/* Insert client in hashtable.  */
179 	_hash_insert(clientshash, cl->pid, cl);
180 
181 	return (0);
182 }
183 
184 static void
185 daemon_remove_client(int i)
186 {
187 
188 	struct client *cl = clients[i];
189 	sysvd_print("pid %d disconected\n", cl->pid);
190 	sysvd_print("total = %d\n", nr_poll_fds);
191 
192 	/* Close communication channels. */
193 	close(cl->sock);
194 
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];
199 	}
200 
201 	semexit(cl->undoid);
202 	shmexit(cl);
203 
204 	_hash_remove(clientshash, cl->pid);
205 	nr_poll_fds--;
206 	free(cl);
207 	cl = NULL;
208 
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;
212 	}
213 }
214 
215 static int
216 daemon_handle_msg(int i)
217 {
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;
225 
226 	int fd_send, fd_recv;
227 	fd_send = fd_recv = clients[i]->sock;
228 
229 	msg_type = receive_type_message(fd_recv);
230 	sysvd_print("type = %d from %d\n", msg_type, clients[i]->pid);
231 
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);
244 
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);
256 
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);
265 
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) {
270 
271 				write(fd_send, (char *)&shmctl_msg.buf,
272 						sizeof(struct shmid_ds));
273 			}
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;
282 	}
283 	sysvd_print("end\n");
284 	return (0);
285 }
286 
287 
288 static int
289 daemon_func(void)
290 {
291 	int i;
292 	//int msg;
293 	int ret, r;
294 
295 	while(1)
296 	{
297 		ret = poll(poll_fds, nr_poll_fds, INFTIM);
298 		if (ret < 0) {
299 			sysvd_print_err("poll");
300 			return (-1);
301 		}
302 		for (i=0; (i < nr_poll_fds) && ret; i++) {
303 			if (poll_fds[i].revents == 0)
304 				continue;
305 			ret--;
306 
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--;
316 				}
317 				break;
318 			}
319 		}
320 		fflush(stdout);
321 	}
322 
323 	return (0);
324 }
325 
326 void
327 usage(void)
328 {
329 	fprintf(stderr, "sysvipcd [-df] [-p pidfile]\n");
330 	exit(EX_USAGE);
331 }
332 
333 int
334 main(int argc, char *argv[])
335 {
336 	int c;
337 	int error;
338 	char *pidfilename = NULL;
339 	struct pidfh *pfh = NULL;
340 
341 	sysvd_debug = 0;
342 	sysvd_daemon = 1;
343 
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;
359 		}
360 	}
361 
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
371 
372 	sysvd_print("daemon starting\n");
373 	error = daemon_init();
374 	if (error)
375 		goto out;
376 
377 	if (sysvd_daemon == 1) {
378 		pfh = pidfile_open(pidfilename, 600, NULL);
379 		daemon(1,0);
380 		pidfile_write(pfh);
381 	}
382 
383 	daemon_func();
384 
385 	/* It won't reach here. */
386 	sysvd_print("daemon finished\n");
387 
388 	//shmfree();
389 	remove_sysv_dir();
390 out:
391 	return (0);
392 }
393