ee88aacdbbf3323d0a5c03acff91d9699dcdb311
[platform/upstream/connman.git] / vpn / main.c
1 /*
2  *
3  *  ConnMan VPN daemon
4  *
5  *  Copyright (C) 2012-2013  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <signal.h>
32 #include <sys/signalfd.h>
33 #include <getopt.h>
34 #include <sys/stat.h>
35 #include <net/if.h>
36 #include <netdb.h>
37
38 #include <gdbus.h>
39
40 #include "../src/connman.h"
41 #include "vpn.h"
42
43 #include "connman/vpn-dbus.h"
44
45 #define CONFIGMAINFILE CONFIGDIR "/connman-vpn.conf"
46
47 #define DEFAULT_INPUT_REQUEST_TIMEOUT 300 * 1000
48
49 static GMainLoop *main_loop = NULL;
50
51 static unsigned int __terminated = 0;
52
53 static struct {
54         unsigned int timeout_inputreq;
55 } connman_vpn_settings  = {
56         .timeout_inputreq = DEFAULT_INPUT_REQUEST_TIMEOUT,
57 };
58
59 static GKeyFile *load_config(const char *file)
60 {
61         GError *err = NULL;
62         GKeyFile *keyfile;
63
64         keyfile = g_key_file_new();
65
66         g_key_file_set_list_separator(keyfile, ',');
67
68         if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
69                 if (err->code != G_FILE_ERROR_NOENT) {
70                         connman_error("Parsing %s failed: %s", file,
71                                                                 err->message);
72                 }
73
74                 g_error_free(err);
75                 g_key_file_free(keyfile);
76                 return NULL;
77         }
78
79         return keyfile;
80 }
81
82 static void parse_config(GKeyFile *config, const char *file)
83 {
84         GError *error = NULL;
85         int timeout;
86
87         if (!config)
88                 return;
89
90         DBG("parsing %s", file);
91
92         timeout = g_key_file_get_integer(config, "General",
93                         "InputRequestTimeout", &error);
94         if (!error && timeout >= 0)
95                 connman_vpn_settings.timeout_inputreq = timeout * 1000;
96
97         g_clear_error(&error);
98 }
99
100 static int config_init(const char *file)
101 {
102         GKeyFile *config;
103
104         config = load_config(file);
105         parse_config(config, file);
106         if (config)
107                 g_key_file_free(config);
108
109         return 0;
110 }
111
112 static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
113                                                         gpointer user_data)
114 {
115         struct signalfd_siginfo si;
116         ssize_t result;
117         int fd;
118
119         if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
120                 return FALSE;
121
122         fd = g_io_channel_unix_get_fd(channel);
123
124         result = read(fd, &si, sizeof(si));
125         if (result != sizeof(si))
126                 return FALSE;
127
128         switch (si.ssi_signo) {
129         case SIGINT:
130         case SIGTERM:
131                 if (__terminated == 0) {
132                         connman_info("Terminating");
133                         g_main_loop_quit(main_loop);
134                 }
135
136                 __terminated = 1;
137                 break;
138         }
139
140         return TRUE;
141 }
142
143 static guint setup_signalfd(void)
144 {
145         GIOChannel *channel;
146         guint source;
147         sigset_t mask;
148         int fd;
149
150         sigemptyset(&mask);
151         sigaddset(&mask, SIGINT);
152         sigaddset(&mask, SIGTERM);
153
154         if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
155                 perror("Failed to set signal mask");
156                 return 0;
157         }
158
159         fd = signalfd(-1, &mask, 0);
160         if (fd < 0) {
161                 perror("Failed to create signal descriptor");
162                 return 0;
163         }
164
165         channel = g_io_channel_unix_new(fd);
166
167         g_io_channel_set_close_on_unref(channel, TRUE);
168         g_io_channel_set_encoding(channel, NULL, NULL);
169         g_io_channel_set_buffered(channel, FALSE);
170
171         source = g_io_add_watch(channel,
172                                 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
173                                 signal_handler, NULL);
174
175         g_io_channel_unref(channel);
176
177         return source;
178 }
179
180 static void disconnect_callback(DBusConnection *conn, void *user_data)
181 {
182         connman_error("D-Bus disconnect");
183
184         g_main_loop_quit(main_loop);
185 }
186
187 static gchar *option_config = NULL;
188 static gchar *option_debug = NULL;
189 static gchar *option_plugin = NULL;
190 static gchar *option_noplugin = NULL;
191 static bool option_detach = true;
192 static bool option_version = false;
193 static bool option_routes = false;
194
195 static bool parse_debug(const char *key, const char *value,
196                                         gpointer user_data, GError **error)
197 {
198         if (value)
199                 option_debug = g_strdup(value);
200         else
201                 option_debug = g_strdup("*");
202
203         return true;
204 }
205
206 static GOptionEntry options[] = {
207         { "config", 'c', 0, G_OPTION_ARG_STRING, &option_config,
208                                 "Load the specified configuration file "
209                                 "instead of " CONFIGMAINFILE, "FILE" },
210         { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG,
211                                 G_OPTION_ARG_CALLBACK, parse_debug,
212                                 "Specify debug options to enable", "DEBUG" },
213         { "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin,
214                                 "Specify plugins to load", "NAME,..." },
215         { "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin,
216                                 "Specify plugins not to load", "NAME,..." },
217         { "nodaemon", 'n', G_OPTION_FLAG_REVERSE,
218                                 G_OPTION_ARG_NONE, &option_detach,
219                                 "Don't fork daemon to background" },
220         { "routes", 'r', 0, G_OPTION_ARG_NONE, &option_routes,
221                                 "Create/delete VPN routes" },
222         { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
223                                 "Show version information and exit" },
224         { NULL },
225 };
226
227 /*
228  * This function will be called from generic src/agent.c code so we have
229  * to use connman_ prefix instead of vpn_ one.
230  */
231 unsigned int connman_timeout_input_request(void)
232 {
233         return connman_vpn_settings.timeout_inputreq;
234 }
235
236 int main(int argc, char *argv[])
237 {
238         GOptionContext *context;
239         GError *error = NULL;
240         DBusConnection *conn;
241         DBusError err;
242         guint signal;
243
244         context = g_option_context_new(NULL);
245         g_option_context_add_main_entries(context, options, NULL);
246
247         if (!g_option_context_parse(context, &argc, &argv, &error)) {
248                 if (error) {
249                         g_printerr("%s\n", error->message);
250                         g_error_free(error);
251                 } else
252                         g_printerr("An unknown error occurred\n");
253                 exit(1);
254         }
255
256         g_option_context_free(context);
257
258         if (option_version) {
259                 printf("%s\n", VERSION);
260                 exit(0);
261         }
262
263         if (option_detach) {
264                 if (daemon(0, 0)) {
265                         perror("Can't start daemon");
266                         exit(1);
267                 }
268         }
269
270         if (mkdir(VPN_STATEDIR, S_IRUSR | S_IWUSR | S_IXUSR |
271                                 S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) {
272                 if (errno != EEXIST)
273                         perror("Failed to create state directory");
274         }
275
276         /*
277          * At some point the VPN stuff is migrated into VPN_STORAGEDIR
278          * and this mkdir() call can be removed.
279          */
280         if (mkdir(STORAGEDIR, S_IRUSR | S_IWUSR | S_IXUSR |
281                                 S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) {
282                 if (errno != EEXIST)
283                         perror("Failed to create storage directory");
284         }
285
286         if (mkdir(VPN_STORAGEDIR, S_IRUSR | S_IWUSR | S_IXUSR |
287                                 S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) {
288                 if (errno != EEXIST)
289                         perror("Failed to create VPN storage directory");
290         }
291
292         umask(0077);
293
294         main_loop = g_main_loop_new(NULL, FALSE);
295
296         signal = setup_signalfd();
297
298         dbus_error_init(&err);
299
300         conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, VPN_SERVICE, &err);
301         if (!conn) {
302                 if (dbus_error_is_set(&err)) {
303                         fprintf(stderr, "%s\n", err.message);
304                         dbus_error_free(&err);
305                 } else
306                         fprintf(stderr, "Can't register with system bus\n");
307                 exit(1);
308         }
309
310         g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL);
311
312         __connman_log_init(argv[0], option_debug, option_detach, false,
313                         "Connection Manager VPN daemon", VERSION);
314         __connman_dbus_init(conn);
315
316         if (!option_config)
317                 config_init(CONFIGMAINFILE);
318         else
319                 config_init(option_config);
320
321         __connman_inotify_init();
322         __connman_agent_init();
323         __vpn_provider_init(option_routes);
324         __vpn_manager_init();
325         __vpn_ipconfig_init();
326         __vpn_rtnl_init();
327         __connman_task_init();
328         __connman_plugin_init(option_plugin, option_noplugin);
329         __vpn_config_init();
330
331         __vpn_rtnl_start();
332
333         g_free(option_plugin);
334         g_free(option_noplugin);
335
336         g_main_loop_run(main_loop);
337
338         g_source_remove(signal);
339
340         __vpn_config_cleanup();
341         __connman_plugin_cleanup();
342         __connman_task_cleanup();
343         __vpn_rtnl_cleanup();
344         __vpn_ipconfig_cleanup();
345         __vpn_manager_cleanup();
346         __vpn_provider_cleanup();
347         __connman_agent_cleanup();
348         __connman_inotify_cleanup();
349         __connman_dbus_cleanup();
350         __connman_log_cleanup(false);
351
352         dbus_connection_unref(conn);
353
354         g_main_loop_unref(main_loop);
355
356         g_free(option_debug);
357
358         return 0;
359 }