| /* |
| * |
| * Connection Manager |
| * |
| * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <signal.h> |
| #include <sys/signalfd.h> |
| #include <getopt.h> |
| #include <sys/stat.h> |
| #include <net/if.h> |
| #include <netdb.h> |
| |
| #include <gdbus.h> |
| |
| #include "connman.h" |
| |
| #define DEFAULT_INPUT_REQUEST_TIMEOUT 120 * 1000 |
| #define DEFAULT_BROWSER_LAUNCH_TIMEOUT 300 * 1000 |
| |
| #define MAINFILE "main.conf" |
| #define CONFIGMAINFILE CONFIGDIR "/" MAINFILE |
| |
| static char *default_auto_connect[] = { |
| "wifi", |
| "ethernet", |
| "cellular", |
| NULL |
| }; |
| |
| static char *default_blacklist[] = { |
| "vmnet", |
| "vboxnet", |
| "virbr", |
| NULL |
| }; |
| |
| static struct { |
| connman_bool_t bg_scan; |
| char **pref_timeservers; |
| unsigned int *auto_connect; |
| unsigned int *preferred_techs; |
| char **fallback_nameservers; |
| unsigned int timeout_inputreq; |
| unsigned int timeout_browserlaunch; |
| char **blacklisted_interfaces; |
| connman_bool_t allow_hostname_updates; |
| connman_bool_t single_tech; |
| } connman_settings = { |
| .bg_scan = TRUE, |
| .pref_timeservers = NULL, |
| .auto_connect = NULL, |
| .preferred_techs = NULL, |
| .fallback_nameservers = NULL, |
| .timeout_inputreq = DEFAULT_INPUT_REQUEST_TIMEOUT, |
| .timeout_browserlaunch = DEFAULT_BROWSER_LAUNCH_TIMEOUT, |
| .blacklisted_interfaces = NULL, |
| .allow_hostname_updates = TRUE, |
| .single_tech = FALSE, |
| }; |
| |
| #define CONF_BG_SCAN "BackgroundScanning" |
| #define CONF_PREF_TIMESERVERS "FallbackTimeservers" |
| #define CONF_AUTO_CONNECT "DefaultAutoConnectTechnologies" |
| #define CONF_PREFERRED_TECHS "PreferredTechnologies" |
| #define CONF_FALLBACK_NAMESERVERS "FallbackNameservers" |
| #define CONF_TIMEOUT_INPUTREQ "InputRequestTimeout" |
| #define CONF_TIMEOUT_BROWSERLAUNCH "BrowserLaunchTimeout" |
| #define CONF_BLACKLISTED_INTERFACES "NetworkInterfaceBlacklist" |
| #define CONF_ALLOW_HOSTNAME_UPDATES "AllowHostnameUpdates" |
| #define CONF_SINGLE_TECH "SingleConnectedTechnology" |
| |
| static const char *supported_options[] = { |
| CONF_BG_SCAN, |
| CONF_PREF_TIMESERVERS, |
| CONF_AUTO_CONNECT, |
| CONF_PREFERRED_TECHS, |
| CONF_FALLBACK_NAMESERVERS, |
| CONF_TIMEOUT_INPUTREQ, |
| CONF_TIMEOUT_BROWSERLAUNCH, |
| CONF_BLACKLISTED_INTERFACES, |
| CONF_ALLOW_HOSTNAME_UPDATES, |
| CONF_SINGLE_TECH, |
| NULL |
| }; |
| |
| static GKeyFile *load_config(const char *file) |
| { |
| GError *err = NULL; |
| GKeyFile *keyfile; |
| |
| keyfile = g_key_file_new(); |
| |
| g_key_file_set_list_separator(keyfile, ','); |
| |
| if (!g_key_file_load_from_file(keyfile, file, 0, &err)) { |
| if (err->code != G_FILE_ERROR_NOENT) { |
| connman_error("Parsing %s failed: %s", file, |
| err->message); |
| } |
| |
| g_error_free(err); |
| g_key_file_free(keyfile); |
| return NULL; |
| } |
| |
| return keyfile; |
| } |
| |
| static uint *parse_service_types(char **str_list, gsize len) |
| { |
| unsigned int *type_list; |
| int i, j; |
| enum connman_service_type type; |
| |
| type_list = g_try_new0(unsigned int, len + 1); |
| if (type_list == NULL) |
| return NULL; |
| |
| i = 0; |
| j = 0; |
| while (str_list[i] != NULL) |
| { |
| type = __connman_service_string2type(str_list[i]); |
| |
| if (type != CONNMAN_SERVICE_TYPE_UNKNOWN) { |
| type_list[j] = type; |
| j += 1; |
| } |
| i += 1; |
| } |
| |
| return type_list; |
| } |
| |
| static char **parse_fallback_nameservers(char **nameservers, gsize len) |
| { |
| char **servers; |
| int i, j; |
| |
| servers = g_try_new0(char *, len + 1); |
| if (servers == NULL) |
| return NULL; |
| |
| i = 0; |
| j = 0; |
| while (nameservers[i] != NULL) { |
| if (connman_inet_check_ipaddress(nameservers[i]) > 0) { |
| servers[j] = g_strdup(nameservers[i]); |
| j += 1; |
| } |
| i += 1; |
| } |
| |
| return servers; |
| } |
| |
| static void check_config(GKeyFile *config) |
| { |
| char **keys; |
| int j; |
| |
| if (config == NULL) |
| return; |
| |
| keys = g_key_file_get_groups(config, NULL); |
| |
| for (j = 0; keys != NULL && keys[j] != NULL; j++) { |
| if (g_strcmp0(keys[j], "General") != 0) |
| connman_warn("Unknown group %s in %s", |
| keys[j], MAINFILE); |
| } |
| |
| g_strfreev(keys); |
| |
| keys = g_key_file_get_keys(config, "General", NULL, NULL); |
| |
| for (j = 0; keys != NULL && keys[j] != NULL; j++) { |
| connman_bool_t found; |
| int i; |
| |
| found = FALSE; |
| for (i = 0; supported_options[i] != NULL; i++) { |
| if (g_strcmp0(keys[j], supported_options[i]) == 0) { |
| found = TRUE; |
| break; |
| } |
| } |
| if (found == FALSE && supported_options[i] == NULL) |
| connman_warn("Unknown option %s in %s", |
| keys[j], MAINFILE); |
| } |
| |
| g_strfreev(keys); |
| } |
| |
| static void parse_config(GKeyFile *config) |
| { |
| GError *error = NULL; |
| gboolean boolean; |
| char **timeservers; |
| char **interfaces; |
| char **str_list; |
| gsize len; |
| int timeout; |
| |
| if (config == NULL) { |
| connman_settings.auto_connect = |
| parse_service_types(default_auto_connect, 3); |
| connman_settings.blacklisted_interfaces = |
| g_strdupv(default_blacklist); |
| return; |
| } |
| |
| DBG("parsing %s", MAINFILE); |
| |
| boolean = g_key_file_get_boolean(config, "General", |
| CONF_BG_SCAN, &error); |
| if (error == NULL) |
| connman_settings.bg_scan = boolean; |
| |
| g_clear_error(&error); |
| |
| timeservers = g_key_file_get_string_list(config, "General", |
| CONF_PREF_TIMESERVERS, NULL, &error); |
| if (error == NULL) |
| connman_settings.pref_timeservers = timeservers; |
| |
| g_clear_error(&error); |
| |
| str_list = g_key_file_get_string_list(config, "General", |
| CONF_AUTO_CONNECT, &len, &error); |
| |
| if (error == NULL) |
| connman_settings.auto_connect = |
| parse_service_types(str_list, len); |
| else |
| connman_settings.auto_connect = |
| parse_service_types(default_auto_connect, 3); |
| |
| g_strfreev(str_list); |
| |
| g_clear_error(&error); |
| |
| str_list = g_key_file_get_string_list(config, "General", |
| CONF_PREFERRED_TECHS, &len, &error); |
| |
| if (error == NULL) |
| connman_settings.preferred_techs = |
| parse_service_types(str_list, len); |
| |
| g_strfreev(str_list); |
| |
| g_clear_error(&error); |
| |
| str_list = g_key_file_get_string_list(config, "General", |
| CONF_FALLBACK_NAMESERVERS, &len, &error); |
| |
| if (error == NULL) |
| connman_settings.fallback_nameservers = |
| parse_fallback_nameservers(str_list, len); |
| |
| g_strfreev(str_list); |
| |
| g_clear_error(&error); |
| |
| timeout = g_key_file_get_integer(config, "General", |
| CONF_TIMEOUT_INPUTREQ, &error); |
| if (error == NULL && timeout >= 0) |
| connman_settings.timeout_inputreq = timeout * 1000; |
| |
| g_clear_error(&error); |
| |
| timeout = g_key_file_get_integer(config, "General", |
| CONF_TIMEOUT_BROWSERLAUNCH, &error); |
| if (error == NULL && timeout >= 0) |
| connman_settings.timeout_browserlaunch = timeout * 1000; |
| |
| g_clear_error(&error); |
| |
| interfaces = g_key_file_get_string_list(config, "General", |
| CONF_BLACKLISTED_INTERFACES, &len, &error); |
| |
| if (error == NULL) |
| connman_settings.blacklisted_interfaces = interfaces; |
| else |
| connman_settings.blacklisted_interfaces = |
| g_strdupv(default_blacklist); |
| |
| g_clear_error(&error); |
| |
| boolean = g_key_file_get_boolean(config, "General", |
| CONF_ALLOW_HOSTNAME_UPDATES, |
| &error); |
| if (error == NULL) |
| connman_settings.allow_hostname_updates = boolean; |
| |
| g_clear_error(&error); |
| |
| boolean = g_key_file_get_boolean(config, "General", |
| CONF_SINGLE_TECH, &error); |
| if (error == NULL) |
| connman_settings.single_tech = boolean; |
| |
| g_clear_error(&error); |
| } |
| |
| static int config_init(const char *file, connman_bool_t rlimits) |
| { |
| GKeyFile *config; |
| int err = 0; |
| |
| config = load_config(file); |
| check_config(config); |
| |
| if (rlimits) { |
| err = __connman_rlimits_init(file); |
| if (err < 0) |
| goto done; |
| } |
| |
| parse_config(config); |
| |
| done: |
| if (config != NULL) |
| g_key_file_free(config); |
| |
| return err; |
| } |
| |
| static GMainLoop *main_loop = NULL; |
| |
| static unsigned int __terminated = 0; |
| |
| static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, |
| gpointer user_data) |
| { |
| struct signalfd_siginfo si; |
| ssize_t result; |
| int fd; |
| |
| if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) |
| return FALSE; |
| |
| fd = g_io_channel_unix_get_fd(channel); |
| |
| result = read(fd, &si, sizeof(si)); |
| if (result != sizeof(si)) |
| return FALSE; |
| |
| switch (si.ssi_signo) { |
| case SIGINT: |
| case SIGTERM: |
| if (__terminated == 0) { |
| connman_info("Terminating"); |
| g_main_loop_quit(main_loop); |
| } |
| |
| __terminated = 1; |
| break; |
| } |
| |
| return TRUE; |
| } |
| |
| static guint setup_signalfd(void) |
| { |
| GIOChannel *channel; |
| guint source; |
| sigset_t mask; |
| int fd; |
| |
| sigemptyset(&mask); |
| sigaddset(&mask, SIGINT); |
| sigaddset(&mask, SIGTERM); |
| |
| if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { |
| perror("Failed to set signal mask"); |
| return 0; |
| } |
| |
| fd = signalfd(-1, &mask, 0); |
| if (fd < 0) { |
| perror("Failed to create signal descriptor"); |
| return 0; |
| } |
| |
| channel = g_io_channel_unix_new(fd); |
| |
| g_io_channel_set_close_on_unref(channel, TRUE); |
| g_io_channel_set_encoding(channel, NULL, NULL); |
| g_io_channel_set_buffered(channel, FALSE); |
| |
| source = g_io_add_watch(channel, |
| G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, |
| signal_handler, NULL); |
| |
| g_io_channel_unref(channel); |
| |
| return source; |
| } |
| |
| static void disconnect_callback(DBusConnection *conn, void *user_data) |
| { |
| connman_error("D-Bus disconnect"); |
| |
| g_main_loop_quit(main_loop); |
| } |
| |
| static gchar *option_config = NULL; |
| static gchar *option_debug = NULL; |
| static gchar *option_device = NULL; |
| static gchar *option_pidfile = NULL; |
| static gchar *option_plugin = NULL; |
| static gchar *option_nodevice = NULL; |
| static gchar *option_noplugin = NULL; |
| static gchar *option_wifi = NULL; |
| static gboolean option_detach = TRUE; |
| static gboolean option_dnsproxy = TRUE; |
| static gboolean option_backtrace = TRUE; |
| static gboolean option_version = FALSE; |
| static gboolean option_rlimits = TRUE; |
| |
| static gboolean parse_debug(const char *key, const char *value, |
| gpointer user_data, GError **error) |
| { |
| if (value) |
| option_debug = g_strdup(value); |
| else |
| option_debug = g_strdup("*"); |
| |
| return TRUE; |
| } |
| |
| static GOptionEntry options[] = { |
| { "config", 'c', 0, G_OPTION_ARG_STRING, &option_config, |
| "Load the specified configuration file " |
| "instead of " CONFIGMAINFILE, "FILE" }, |
| { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG, |
| G_OPTION_ARG_CALLBACK, parse_debug, |
| "Specify debug options to enable", "DEBUG" }, |
| { "device", 'i', 0, G_OPTION_ARG_STRING, &option_device, |
| "Specify networking device or interface", "DEV" }, |
| { "nodevice", 'I', 0, G_OPTION_ARG_STRING, &option_nodevice, |
| "Specify networking interface to ignore", "DEV" }, |
| { "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin, |
| "Specify plugins to load", "NAME,..." }, |
| { "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin, |
| "Specify plugins not to load", "NAME,..." }, |
| { "wifi", 'W', 0, G_OPTION_ARG_STRING, &option_wifi, |
| "Specify driver for WiFi/Supplicant", "NAME" }, |
| { "nodaemon", 'n', G_OPTION_FLAG_REVERSE, |
| G_OPTION_ARG_NONE, &option_detach, |
| "Don't fork daemon to background" }, |
| { "nodnsproxy", 'r', G_OPTION_FLAG_REVERSE, |
| G_OPTION_ARG_NONE, &option_dnsproxy, |
| "Don't enable DNS Proxy" }, |
| { "nobacktrace", 0, G_OPTION_FLAG_REVERSE, |
| G_OPTION_ARG_NONE, &option_backtrace, |
| "Don't print out backtrace information" }, |
| { "norlimits", 0, G_OPTION_FLAG_REVERSE, |
| G_OPTION_ARG_NONE, &option_rlimits, |
| "Don't enforce resource limits" }, |
| { "pidfile", 0, 0, G_OPTION_ARG_STRING, &option_pidfile, |
| "Path to PID file", "PIDFILE" }, |
| { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, |
| "Show version information and exit" }, |
| { NULL }, |
| }; |
| |
| const char *connman_option_get_string(const char *key) |
| { |
| if (g_strcmp0(key, "wifi") == 0) { |
| if (option_wifi == NULL) |
| return "nl80211,wext"; |
| else |
| return option_wifi; |
| } |
| |
| return NULL; |
| } |
| |
| connman_bool_t connman_setting_get_bool(const char *key) |
| { |
| if (g_str_equal(key, CONF_BG_SCAN) == TRUE) |
| return connman_settings.bg_scan; |
| |
| if (g_str_equal(key, CONF_ALLOW_HOSTNAME_UPDATES) == TRUE) |
| return connman_settings.allow_hostname_updates; |
| |
| if (g_str_equal(key, CONF_SINGLE_TECH) == TRUE) |
| return connman_settings.single_tech; |
| |
| return FALSE; |
| } |
| |
| char **connman_setting_get_string_list(const char *key) |
| { |
| if (g_str_equal(key, CONF_PREF_TIMESERVERS) == TRUE) |
| return connman_settings.pref_timeservers; |
| |
| if (g_str_equal(key, CONF_FALLBACK_NAMESERVERS) == TRUE) |
| return connman_settings.fallback_nameservers; |
| |
| if (g_str_equal(key, CONF_BLACKLISTED_INTERFACES) == TRUE) |
| return connman_settings.blacklisted_interfaces; |
| |
| return NULL; |
| } |
| |
| unsigned int *connman_setting_get_uint_list(const char *key) |
| { |
| if (g_str_equal(key, CONF_AUTO_CONNECT) == TRUE) |
| return connman_settings.auto_connect; |
| |
| if (g_str_equal(key, CONF_PREFERRED_TECHS) == TRUE) |
| return connman_settings.preferred_techs; |
| |
| return NULL; |
| } |
| |
| unsigned int connman_timeout_input_request(void) { |
| return connman_settings.timeout_inputreq; |
| } |
| |
| unsigned int connman_timeout_browser_launch(void) { |
| return connman_settings.timeout_browserlaunch; |
| } |
| |
| static void connman_daemon_init(const char *pidfile) |
| { |
| FILE *file; |
| |
| if (daemon(0, 0)) { |
| perror("Can't start daemon"); |
| exit(EXIT_FAILURE); |
| } |
| |
| if (pidfile != NULL) { |
| file = fopen(pidfile, "w"); |
| if (file != NULL) { |
| fprintf(file, "%u\n", getpid()); |
| fclose(file); |
| } |
| } |
| } |
| |
| static void connman_daemon_cleanup(const char *pidfile) |
| { |
| if (pidfile != NULL) { |
| unlink(pidfile); |
| } |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| GOptionContext *context; |
| GError *error = NULL; |
| DBusConnection *conn; |
| DBusError err; |
| guint signal; |
| |
| #ifdef NEED_THREADS |
| if (g_thread_supported() == FALSE) |
| g_thread_init(NULL); |
| #endif |
| |
| context = g_option_context_new(NULL); |
| g_option_context_add_main_entries(context, options, NULL); |
| |
| if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { |
| if (error != NULL) { |
| g_printerr("%s\n", error->message); |
| g_error_free(error); |
| } else |
| g_printerr("An unknown error occurred\n"); |
| exit(1); |
| } |
| |
| g_option_context_free(context); |
| |
| if (option_version == TRUE) { |
| printf("%s\n", VERSION); |
| exit(0); |
| } |
| |
| if (option_detach == TRUE) { |
| connman_daemon_init(option_pidfile); |
| } |
| |
| if (mkdir(STATEDIR, S_IRUSR | S_IWUSR | S_IXUSR | |
| S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) { |
| if (errno != EEXIST) |
| perror("Failed to create state directory"); |
| } |
| |
| if (mkdir(STORAGEDIR, S_IRUSR | S_IWUSR | S_IXUSR | |
| S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) { |
| if (errno != EEXIST) |
| perror("Failed to create storage directory"); |
| } |
| |
| umask(0077); |
| |
| main_loop = g_main_loop_new(NULL, FALSE); |
| |
| #ifdef NEED_THREADS |
| if (dbus_threads_init_default() == FALSE) { |
| fprintf(stderr, "Can't init usage of threads\n"); |
| exit(1); |
| } |
| #endif |
| |
| signal = setup_signalfd(); |
| |
| dbus_error_init(&err); |
| |
| conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, CONNMAN_SERVICE, &err); |
| if (conn == NULL) { |
| if (dbus_error_is_set(&err) == TRUE) { |
| fprintf(stderr, "%s\n", err.message); |
| dbus_error_free(&err); |
| } else |
| fprintf(stderr, "Can't register with system bus\n"); |
| exit(1); |
| } |
| |
| g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL); |
| |
| __connman_log_init(argv[0], option_debug, option_detach, |
| option_backtrace, "Connection Manager", VERSION); |
| |
| __connman_dbus_init(conn); |
| |
| if (option_config == NULL) |
| config_init(CONFIGMAINFILE, option_rlimits); |
| else |
| config_init(option_config, option_rlimits); |
| |
| __connman_inotify_init(); |
| __connman_technology_init(); |
| __connman_notifier_init(); |
| __connman_agent_init(); |
| __connman_service_init(); |
| __connman_provider_init(); |
| __connman_network_init(); |
| __connman_device_init(option_device, option_nodevice); |
| |
| __connman_ippool_init(); |
| __connman_iptables_init(); |
| __connman_nat_init(); |
| __connman_tethering_init(); |
| __connman_counter_init(); |
| __connman_manager_init(); |
| __connman_config_init(); |
| __connman_stats_init(); |
| __connman_clock_init(); |
| |
| __connman_resolver_init(option_dnsproxy); |
| __connman_ipconfig_init(); |
| __connman_rtnl_init(); |
| __connman_task_init(); |
| __connman_proxy_init(); |
| __connman_detect_init(); |
| __connman_session_init(); |
| __connman_timeserver_init(); |
| __connman_connection_init(); |
| |
| __connman_plugin_init(option_plugin, option_noplugin); |
| |
| __connman_rtnl_start(); |
| __connman_dhcp_init(); |
| __connman_dhcpv6_init(); |
| __connman_wpad_init(); |
| __connman_wispr_init(); |
| __connman_rfkill_init(); |
| |
| g_free(option_config); |
| g_free(option_device); |
| g_free(option_plugin); |
| g_free(option_nodevice); |
| g_free(option_noplugin); |
| |
| g_main_loop_run(main_loop); |
| |
| g_source_remove(signal); |
| |
| __connman_rfkill_cleanup(); |
| __connman_wispr_cleanup(); |
| __connman_wpad_cleanup(); |
| __connman_dhcpv6_cleanup(); |
| __connman_dhcp_cleanup(); |
| __connman_plugin_cleanup(); |
| __connman_provider_cleanup(); |
| __connman_connection_cleanup(); |
| __connman_timeserver_cleanup(); |
| __connman_session_cleanup(); |
| __connman_detect_cleanup(); |
| __connman_proxy_cleanup(); |
| __connman_task_cleanup(); |
| __connman_rtnl_cleanup(); |
| __connman_resolver_cleanup(); |
| |
| __connman_clock_cleanup(); |
| __connman_stats_cleanup(); |
| __connman_config_cleanup(); |
| __connman_manager_cleanup(); |
| __connman_counter_cleanup(); |
| __connman_tethering_cleanup(); |
| __connman_nat_cleanup(); |
| __connman_iptables_cleanup(); |
| __connman_ippool_cleanup(); |
| __connman_device_cleanup(); |
| __connman_network_cleanup(); |
| __connman_service_cleanup(); |
| __connman_agent_cleanup(); |
| __connman_ipconfig_cleanup(); |
| __connman_notifier_cleanup(); |
| __connman_technology_cleanup(); |
| __connman_inotify_cleanup(); |
| |
| __connman_dbus_cleanup(); |
| |
| __connman_log_cleanup(option_backtrace); |
| |
| dbus_connection_unref(conn); |
| |
| g_main_loop_unref(main_loop); |
| |
| if (connman_settings.pref_timeservers != NULL) |
| g_strfreev(connman_settings.pref_timeservers); |
| |
| g_free(connman_settings.auto_connect); |
| g_free(connman_settings.preferred_techs); |
| g_strfreev(connman_settings.fallback_nameservers); |
| g_strfreev(connman_settings.blacklisted_interfaces); |
| |
| connman_daemon_cleanup(option_pidfile); |
| |
| g_free(option_pidfile); |
| g_free(option_debug); |
| |
| return 0; |
| } |