f3ad5235f0424b38ac8941df4c0e0379ee3b1241
[platform/upstream/buxton.git] / src / core / main.c
1 /*
2  * This file is part of buxton.
3  *
4  * Copyright (C) 2013 Intel Corporation
5  *
6  * buxton is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1
9  * of the License, or (at your option) any later version.
10  */
11
12 /**
13  * \file core/main.c Buxton daemon
14  *
15  * This file provides the buxton daemon
16  */
17 #ifdef HAVE_CONFIG_H
18     #include "config.h"
19 #endif
20
21 #include <errno.h>
22 #include <getopt.h>
23 #include <fcntl.h>
24 #include <poll.h>
25 #include <signal.h>
26 #include <sys/signalfd.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <sys/un.h>
30 #include <systemd/sd-daemon.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <stdbool.h>
36 #include <attr/xattr.h>
37
38 #include "buxton.h"
39 #include "backend.h"
40 #include "daemon.h"
41 #include "direct.h"
42 #include "list.h"
43 #include "log.h"
44 #include "smack.h"
45 #include "util.h"
46 #include "configurator.h"
47 #include "buxtonlist.h"
48
49 #define SOCKET_TIMEOUT 5
50
51 static BuxtonDaemon self;
52
53 static void print_usage(char *name)
54 {
55         printf("%s: Usage\n\n", name);
56
57         printf("  -c, --config-file        Path to configuration file\n");
58         printf("  -h, --help               Display this help message\n");
59 }
60
61 /**
62  * Entry point into buxtond
63  * @param argc Number of arguments passed
64  * @param argv An array of string arguments
65  * @returns EXIT_SUCCESS if the operation succeeded, otherwise EXIT_FAILURE
66  */
67 int main(int argc, char *argv[])
68 {
69         int fd;
70         int smackfd = -1;
71         socklen_t addr_len;
72         struct sockaddr_un remote;
73         int descriptors;
74         int ret;
75         bool manual_start = false;
76         sigset_t mask;
77         int sigfd;
78         bool leftover_messages = false;
79         struct stat st;
80         bool help = false;
81         BuxtonList *map_list = NULL;
82         Iterator iter;
83         char *notify_key;
84
85         static struct option opts[] = {
86                 { "config-file", 1, NULL, 'c' },
87                 { "help",        0, NULL, 'h' },
88                 { NULL, 0, NULL, 0 }
89         };
90
91         while (true) {
92                 int c;
93                 int i;
94                 c = getopt_long(argc, argv, "c:h", opts, &i);
95
96                 if (c == -1) {
97                         break;
98                 }
99
100                 switch (c) {
101                 case 'c':
102                         ret = stat(optarg, &st);
103                         if (ret == -1) {
104                                 buxton_log("Invalid configuration file path\n");
105                                 exit(EXIT_FAILURE);
106                         } else {
107                                 if (st.st_mode & S_IFDIR) {
108                                         buxton_log("Configuration file given is a directory\n");
109                                         exit(EXIT_FAILURE);
110                                 }
111                         }
112
113                         buxton_add_cmd_line(CONFIG_CONF_FILE, optarg);
114                         break;
115                 case 'h':
116                         help = true;
117                         break;
118                 }
119         }
120
121         if (help) {
122                 print_usage(argv[0]);
123                 exit(EXIT_SUCCESS);
124         }
125
126         if (!buxton_cache_smack_rules()) {
127                 exit(EXIT_FAILURE);
128         }
129         smackfd = buxton_watch_smack_rules();
130         if (smackfd < 0 && errno) {
131                 exit(EXIT_FAILURE);
132         }
133
134         self.nfds_alloc = 0;
135         self.accepting_alloc = 0;
136         self.nfds = 0;
137         self.buxton.client.direct = true;
138         self.buxton.client.uid = geteuid();
139         if (!buxton_direct_open(&self.buxton)) {
140                 exit(EXIT_FAILURE);
141         }
142
143         sigemptyset(&mask);
144         ret = sigaddset(&mask, SIGINT);
145         if (ret != 0) {
146                 exit(EXIT_FAILURE);
147         }
148         ret = sigaddset(&mask, SIGTERM);
149         if (ret != 0) {
150                 exit(EXIT_FAILURE);
151         }
152         ret = sigaddset(&mask, SIGPIPE);
153         if (ret != 0) {
154                 exit(EXIT_FAILURE);
155         }
156
157         ret = sigprocmask(SIG_BLOCK, &mask, NULL);
158         if (ret == -1) {
159                 exit(EXIT_FAILURE);
160         }
161
162         sigfd = signalfd(-1, &mask, 0);
163         if (sigfd == -1) {
164                 exit(EXIT_FAILURE);
165         }
166
167         add_pollfd(&self, sigfd, POLLIN, false);
168
169         /* For client notifications */
170         self.notify_mapping = hashmap_new(string_hash_func, string_compare_func);
171         /* Store a list of connected clients */
172         LIST_HEAD_INIT(client_list_item, self.client_list);
173
174         descriptors = sd_listen_fds(0);
175         if (descriptors < 0) {
176                 buxton_log("sd_listen_fds: %m\n");
177                 exit(EXIT_FAILURE);
178         } else if (descriptors == 0) {
179                 /* Manual invocation */
180                 manual_start = true;
181                 union {
182                         struct sockaddr sa;
183                         struct sockaddr_un un;
184                 } sa;
185
186                 fd = socket(AF_UNIX, SOCK_STREAM, 0);
187                 if (fd < 0) {
188                         buxton_log("socket(): %m\n");
189                         exit(EXIT_FAILURE);
190                 }
191
192                 memzero(&sa, sizeof(sa));
193                 sa.un.sun_family = AF_UNIX;
194                 strncpy(sa.un.sun_path, buxton_socket(), sizeof(sa.un.sun_path) - 1);
195                 sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0;
196
197                 ret = unlink(sa.un.sun_path);
198                 if (ret == -1 && errno != ENOENT) {
199                         exit(EXIT_FAILURE);
200                 }
201
202                 if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
203                         buxton_log("bind(): %m\n");
204                         exit(EXIT_FAILURE);
205                 }
206
207                 chmod(sa.un.sun_path, 0666);
208
209                 if (listen(fd, SOMAXCONN) < 0) {
210                         buxton_log("listen(): %m\n");
211                         exit(EXIT_FAILURE);
212                 }
213                 add_pollfd(&self, fd, POLLIN | POLLPRI, true);
214         } else {
215                 /* systemd socket activation */
216                 for (fd = SD_LISTEN_FDS_START + 0; fd < SD_LISTEN_FDS_START + descriptors; fd++) {
217                         if (sd_is_fifo(fd, NULL)) {
218                                 add_pollfd(&self, fd, POLLIN, false);
219                                 buxton_debug("Added fd %d type FIFO\n", fd);
220                         } else if (sd_is_socket_unix(fd, SOCK_STREAM, -1, buxton_socket(), 0)) {
221                                 add_pollfd(&self, fd, POLLIN | POLLPRI, true);
222                                 buxton_debug("Added fd %d type UNIX\n", fd);
223                         } else if (sd_is_socket(fd, AF_UNSPEC, 0, -1)) {
224                                 add_pollfd(&self, fd, POLLIN | POLLPRI, true);
225                                 buxton_debug("Added fd %d type SOCKET\n", fd);
226                         }
227                 }
228         }
229
230         if (smackfd >= 0) {
231                 /* add Smack rule fd to pollfds */
232                 add_pollfd(&self, smackfd, POLLIN | POLLPRI, false);
233         }
234
235         buxton_log("%s: Started\n", argv[0]);
236
237         /* Enter loop to accept clients */
238         for (;;) {
239                 ret = poll(self.pollfds, self.nfds, leftover_messages ? 0 : -1);
240
241                 if (ret < 0) {
242                         buxton_log("poll(): %m\n");
243                         break;
244                 }
245                 if (ret == 0) {
246                         if (!leftover_messages) {
247                                 continue;
248                         }
249                 }
250
251                 leftover_messages = false;
252
253                 /* check sigfd if the daemon was signaled */
254                 if (self.pollfds[0].revents != 0) {
255                         ssize_t sinfo;
256                         struct signalfd_siginfo si;
257
258                         sinfo = read(self.pollfds[0].fd, &si, sizeof(struct signalfd_siginfo));
259                         if (sinfo != sizeof(struct signalfd_siginfo)) {
260                                 exit(EXIT_FAILURE);
261                         }
262
263                         if (si.ssi_signo == SIGINT || si.ssi_signo == SIGTERM) {
264                                 break;
265                         }
266                 }
267
268                 for (nfds_t i = 1; i < self.nfds; i++) {
269                         client_list_item *cl = NULL;
270                         char discard[256];
271
272                         if (self.pollfds[i].revents == 0) {
273                                 continue;
274                         }
275
276                         if (self.pollfds[i].fd == -1) {
277                                 /* TODO: Remove client from list  */
278                                 buxton_debug("Removing / Closing client for fd %d\n", self.pollfds[i].fd);
279                                 del_pollfd(&self, i);
280                                 continue;
281                         }
282
283                         if (smackfd >= 0) {
284                                 if (self.pollfds[i].fd == smackfd) {
285                                         if (!buxton_cache_smack_rules()) {
286                                                 exit(EXIT_FAILURE);
287                                         }
288                                         buxton_log("Reloaded Smack access rules\n");
289                                         /* discard inotify data itself */
290                                         while (read(smackfd, &discard, 256) == 256);
291                                         continue;
292                                 }
293                         }
294
295                         if (self.accepting[i] == true) {
296                                 struct timeval tv;
297                                 int fd;
298                                 int on = 1;
299
300                                 addr_len = sizeof(remote);
301
302                                 if ((fd = accept(self.pollfds[i].fd,
303                                                  (struct sockaddr *)&remote, &addr_len)) == -1) {
304                                         buxton_log("accept(): %m\n");
305                                         break;
306                                 }
307
308                                 buxton_debug("New client fd %d connected through fd %d\n", fd, self.pollfds[i].fd);
309
310                                 if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
311                                         close(fd);
312                                         break;
313                                 }
314
315                                 cl = malloc0(sizeof(client_list_item));
316                                 if (!cl) {
317                                         exit(EXIT_FAILURE);
318                                 }
319
320                                 LIST_INIT(client_list_item, item, cl);
321
322                                 cl->fd = fd;
323                                 cl->cred = (struct ucred) {0, 0, 0};
324                                 LIST_PREPEND(client_list_item, item, self.client_list, cl);
325
326                                 /* poll for data on this new client as well */
327                                 add_pollfd(&self, cl->fd, POLLIN | POLLPRI, false);
328
329                                 /* Mark our packets as high prio */
330                                 if (setsockopt(cl->fd, SOL_SOCKET, SO_PRIORITY, &on, sizeof(on)) == -1) {
331                                         buxton_log("setsockopt(SO_PRIORITY): %m\n");
332                                 }
333
334                                 /* Set socket recv timeout */
335                                 tv.tv_sec = SOCKET_TIMEOUT;
336                                 tv.tv_usec = 0;
337                                 if (setsockopt(cl->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,
338                                                sizeof(struct timeval)) == -1) {
339                                         buxton_log("setsockopt(SO_RCVTIMEO): %m\n");
340                                 }
341
342                                 /* check if this is optimal or not */
343                                 break;
344                         }
345
346                         assert(self.accepting[i] == 0);
347                         if (smackfd >= 0) {
348                                 assert(self.pollfds[i].fd != smackfd);
349                         }
350
351                         /* handle data on any connection */
352                         /* TODO: Replace with hash table lookup */
353                         LIST_FOREACH(item, cl, self.client_list)
354                                 if (self.pollfds[i].fd == cl->fd) {
355                                         break;
356                                 }
357
358                         assert(cl);
359                         if (handle_client(&self, cl, i)) {
360                                 leftover_messages = true;
361                         }
362                 }
363         }
364
365         buxton_log("%s: Closing all connections\n", argv[0]);
366
367         if (manual_start) {
368                 unlink(buxton_socket());
369         }
370         for (int i = 0; i < self.nfds; i++) {
371                 close(self.pollfds[i].fd);
372         }
373         for (client_list_item *i = self.client_list; i;) {
374                 client_list_item *j = i->item_next;
375                 free(i);
376                 i = j;
377         }
378         /* Clean up notification lists */
379         HASHMAP_FOREACH_KEY(map_list, notify_key, self.notify_mapping, iter) {
380                 hashmap_remove(self.notify_mapping, notify_key);
381                 BuxtonList *elem;
382                 BUXTON_LIST_FOREACH(map_list, elem) {
383                         BuxtonNotification *notif = (BuxtonNotification*)elem->data;
384                         if (notif->old_data) {
385                                 free_buxton_data(&(notif->old_data));
386                         }
387                 }
388                 free(notify_key);
389                 buxton_list_free_all(&map_list);
390         }
391
392         hashmap_free(self.notify_mapping);
393         buxton_direct_close(&self.buxton);
394         return EXIT_SUCCESS;
395 }
396
397 /*
398  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
399  *
400  * Local variables:
401  * c-basic-offset: 8
402  * tab-width: 8
403  * indent-tabs-mode: t
404  * End:
405  *
406  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
407  * :indentSize=8:tabSize=8:noTabs=false:
408  */