Add __attribute__((__noreturn__)) to various function prototypes in usr.sbin/.
[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 #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 }