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