diff --git a/rpm/usb-moded.spec b/rpm/usb-moded.spec index 3b77af5..396b14e 100644 --- a/rpm/usb-moded.spec +++ b/rpm/usb-moded.spec @@ -353,6 +353,8 @@ install -m 644 -D config/diag/* %{buildroot}/%{_sysconfdir}/usb-moded/diag/ install -m 644 -D config/run/* %{buildroot}/%{_sysconfdir}/usb-moded/run/ install -m 644 -D config/run-diag/* %{buildroot}/%{_sysconfdir}/usb-moded/run-diag/ install -m 644 -D config/mass-storage-jolla.ini %{buildroot}/%{_sysconfdir}/usb-moded/ +install -d %{buildroot}/%{_sharedstatedir}/usb-moded + touch %{buildroot}/%{_sysconfdir}/modprobe.d/g_ether.conf touch %{buildroot}/%{_sysconfdir}/udhcpd.conf @@ -386,11 +388,14 @@ systemctl daemon-reload || : %config(noreplace) %{_sysconfdir}/modprobe.d/usb_moded.conf %ghost %config(noreplace) %{_sysconfdir}/modprobe.d/g_ether.conf %ghost %{_sysconfdir}/udhcpd.conf +%ghost %{_sysconfdir}/usb-moded/usb-moded.ini %{_sbindir}/usb_moded %{_sbindir}/usb_moded_util /lib/systemd/system/%{name}.service /lib/systemd/system/basic.target.wants/%{name}.service %config %{_sysconfdir}/tmpfiles.d/usb-moded.conf +%dir %{_sharedstatedir}/usb-moded +%ghost %{_sharedstatedir}/usb-moded/usb-moded.ini %files devel %defattr(-,root,root,-) diff --git a/src/usb_moded-config-private.h b/src/usb_moded-config-private.h index 89b33d3..1afeba1 100644 --- a/src/usb_moded-config-private.h +++ b/src/usb_moded-config-private.h @@ -36,8 +36,19 @@ # include "usb_moded-config.h" +# include # include +/* ========================================================================= * + * Constants + * ========================================================================= */ + +# define USB_MODED_STATIC_CONFIG_DIR "/etc/usb-moded" +# define USB_MODED_STATIC_CONFIG_FILE USB_MODED_STATIC_CONFIG_DIR"/usb-moded.ini" + +# define USB_MODED_DYNAMIC_CONFIG_DIR "/var/lib/usb-moded" +# define USB_MODED_DYNAMIC_CONFIG_FILE USB_MODED_DYNAMIC_CONFIG_DIR"/usb-moded.ini" + /* ========================================================================= * * Prototypes * ========================================================================= */ @@ -64,7 +75,7 @@ set_config_result_t config_set_mode_whitelist (const char *whitelist); set_config_result_t config_set_mode_in_whitelist (const char *mode, int allowed); set_config_result_t config_set_network_setting (const char *config, const char *setting); char *config_get_network_setting (const char *config); -int config_merge_conf_file (void); +bool config_init (void); char *config_get_android_manufacturer(void); char *config_get_android_vendor_id (void); char *config_get_android_product (void); diff --git a/src/usb_moded-config.c b/src/usb_moded-config.c index eceabef..1c0c8dc 100644 --- a/src/usb_moded-config.c +++ b/src/usb_moded-config.c @@ -51,6 +51,7 @@ #include #include #include +#include /* ========================================================================= * * Prototypes @@ -74,12 +75,11 @@ static char *config_get_network_interface (void); static char *config_get_network_gateway (void); static char *config_get_network_netmask (void); static char *config_get_network_nat_interface(void); -static void config_create_conf_file (void); +static void config_setup_default_values (GKeyFile *settingsfile); static int config_get_conf_int (const gchar *entry, const gchar *key); static char *config_get_conf_string (const gchar *entry, const gchar *key); static char *config_get_kcmdline_string (const char *entry); char *config_get_mode_setting (void); -int config_value_changed (GKeyFile *settingsfile, const char *entry, const char *key, const char *new_value); set_config_result_t config_set_config_setting (const char *entry, const char *key, const char *value); set_config_result_t config_set_mode_setting (const char *mode); static char *config_make_modes_string (const char *key, const char *mode_name, int include); @@ -91,10 +91,18 @@ set_config_result_t config_set_network_setting (const char *config, const char *config_get_network_setting (const char *config); static void config_merge_key (GKeyFile *dest, GKeyFile *srce, const char *grp, const char *key); static void config_merge_group (GKeyFile *dest, GKeyFile *srce, const char *grp); -static void config_merge_file (GKeyFile *dest, GKeyFile *srce); +static void config_merge_data (GKeyFile *dest, GKeyFile *srce); +static void config_purge_data (GKeyFile *dest, GKeyFile *srce); +static void config_purge_empty_groups (GKeyFile *dest); static int config_glob_error_cb (const char *path, int err); -static GKeyFile *config_read_ini_files (void); -int config_merge_conf_file (void); +static bool config_merge_from_file (GKeyFile *ini, const char *path); +static void config_load_static_config (GKeyFile *ini); +static bool config_load_legacy_config (GKeyFile *ini); +static void config_remove_legacy_config (void); +static void config_load_dynamic_config (GKeyFile *ini); +static void config_save_dynamic_config (GKeyFile *ini); +bool config_init (void); +static GKeyFile *config_get_settings (void); char *config_get_android_manufacturer (void); char *config_get_android_vendor_id (void); char *config_get_android_product (void); @@ -129,7 +137,6 @@ char *config_find_mounts(void) { LOG_REGISTER_CONTEXT; - char *ret = NULL; ret = config_get_conf_string(FS_MOUNT_ENTRY, FS_MOUNT_KEY); @@ -145,7 +152,6 @@ int config_find_sync(void) { LOG_REGISTER_CONTEXT; - return config_get_conf_int(FS_SYNC_ENTRY, FS_SYNC_KEY); } @@ -253,111 +259,37 @@ static char * config_get_network_nat_interface(void) return config_get_conf_string(NETWORK_ENTRY, NETWORK_NAT_INTERFACE_KEY); } -/* create basic conffile with sensible defaults */ -static void config_create_conf_file(void) +static void config_setup_default_values(GKeyFile *settingsfile) { LOG_REGISTER_CONTEXT; - GKeyFile *settingsfile; - gchar *keyfile; - int dir = 1; - struct stat dir_stat; - - /* since this function can also be called when the dir exists we only create - * it if it is missing */ - if(stat(CONFIG_FILE_DIR, &dir_stat)) - { - dir = mkdir(CONFIG_FILE_DIR, 0755); - if(dir < 0) - { - log_warning("Could not create confdir, continuing without configuration!\n"); - /* no point in trying to generate the config file if the dir cannot be created */ - return; - } - } - - settingsfile = g_key_file_new(); - g_key_file_set_string(settingsfile, MODE_SETTING_ENTRY, MODE_SETTING_KEY, MODE_DEVELOPER ); - keyfile = g_key_file_to_data (settingsfile, NULL, NULL); - if(g_file_set_contents(FS_MOUNT_CONFIG_FILE, keyfile, -1, NULL) == 0) - log_debug("Conffile creation failed. Continuing without configuration!\n"); - free(keyfile); - g_key_file_free(settingsfile); } static int config_get_conf_int(const gchar *entry, const gchar *key) { LOG_REGISTER_CONTEXT; - GKeyFile *settingsfile; - gboolean test = FALSE; - gchar **keys, **origkeys; - int ret = 0; - - settingsfile = g_key_file_new(); - test = g_key_file_load_from_file(settingsfile, FS_MOUNT_CONFIG_FILE, G_KEY_FILE_NONE, NULL); - if(!test) - { - log_debug("no conffile, Creating\n"); - config_create_conf_file(); - } - keys = g_key_file_get_keys (settingsfile, entry, NULL, NULL); - if(keys == NULL) - return ret; - origkeys = keys; - while (*keys != NULL) - { - if(!strcmp(*keys, key)) - { - ret = g_key_file_get_integer(settingsfile, entry, *keys, NULL); - log_debug("%s key value = %d\n", key, ret); - } - keys++; - } - g_strfreev(origkeys); - g_key_file_free(settingsfile); - return ret; - + // TODO: use cached values instead of reloading every time? + GKeyFile *ini = config_get_settings(); + // Note: zero value is returned if key does not exist + gint val = g_key_file_get_integer(ini, entry, key, 0); + g_key_file_free(ini); + log_debug("key [%s] %s value is: %d\n", entry, key, val); + return val; } static char * config_get_conf_string(const gchar *entry, const gchar *key) { LOG_REGISTER_CONTEXT; - GKeyFile *settingsfile; - gboolean test = FALSE; - gchar **keys, **origkeys, *tmp_char = NULL; - settingsfile = g_key_file_new(); - test = g_key_file_load_from_file(settingsfile, FS_MOUNT_CONFIG_FILE, G_KEY_FILE_NONE, NULL); - if(!test) - { - log_debug("No conffile. Creating\n"); - config_create_conf_file(); - /* should succeed now */ - g_key_file_load_from_file(settingsfile, FS_MOUNT_CONFIG_FILE, G_KEY_FILE_NONE, NULL); - } - keys = g_key_file_get_keys (settingsfile, entry, NULL, NULL); - if(keys == NULL) - goto end; - origkeys = keys; - while (*keys != NULL) - { - if(!strcmp(*keys, key)) - { - tmp_char = g_key_file_get_string(settingsfile, entry, *keys, NULL); - if(tmp_char) - { - log_debug("key %s value = %s\n", key, tmp_char); - } - } - keys++; - } - g_strfreev(origkeys); -end: - g_key_file_free(settingsfile); - return tmp_char; - + // TODO: use cached values instead of reloading every time? + GKeyFile *ini = config_get_settings(); + // Note: null value is returned if key does not exist + gchar *val = g_key_file_get_string(ini, entry, key, 0); + g_key_file_free(ini); + log_debug("key [%s] %s value is: %s\n", entry, key, val ?: ""); + return val; } static char * config_get_kcmdline_string(const char *entry) @@ -457,58 +389,41 @@ char * config_get_mode_setting(void) EXIT: return mode; } -/* - * @param settingsfile: already opened settingsfile we want to read an entry from - * @param entry: entry we want to read - * @param key: key value of the entry we want to read - * @new_value: potentially new value we want to compare against - * - * @return: 0 when the old value is the same as the new one, 1 otherwise - */ -int config_value_changed(GKeyFile *settingsfile, const char *entry, const char *key, const char *new_value) -{ - LOG_REGISTER_CONTEXT; - - char *old_value = g_key_file_get_string(settingsfile, entry, key, NULL); - int changed = (g_strcmp0(old_value, new_value) != 0); - g_free(old_value); - return changed; -} set_config_result_t config_set_config_setting(const char *entry, const char *key, const char *value) { LOG_REGISTER_CONTEXT; - GKeyFile *settingsfile; - gboolean test = FALSE; - set_config_result_t ret = SET_CONFIG_ERROR; - gchar *keyfile; + set_config_result_t ret = SET_CONFIG_UNCHANGED; + + GKeyFile *static_ini = g_key_file_new(); + GKeyFile *active_ini = g_key_file_new(); - settingsfile = g_key_file_new(); - test = g_key_file_load_from_file(settingsfile, FS_MOUNT_CONFIG_FILE, G_KEY_FILE_NONE, NULL); - if(test) - { - if(!config_value_changed(settingsfile, entry, key, value)) - { - g_key_file_free(settingsfile); - return SET_CONFIG_UNCHANGED; - } - } - else - { - log_debug("No conffile. Creating.\n"); - config_create_conf_file(); - } + gchar *prev = 0; + + /* Load static configuration */ + config_load_static_config(static_ini); + + /* Merge static and dynamic settings */ + config_setup_default_values(active_ini); + config_merge_data(active_ini, static_ini); + config_load_dynamic_config(active_ini); - g_key_file_set_string(settingsfile, entry, key, value); - keyfile = g_key_file_to_data (settingsfile, NULL, NULL); - /* free the settingsfile before writing things out to be sure - * the contents will be correctly written to file afterwards. - * Just a precaution. */ - g_key_file_free(settingsfile); - if (g_file_set_contents(FS_MOUNT_CONFIG_FILE, keyfile, -1, NULL)) + prev = g_key_file_get_string(active_ini, entry, key, 0); + if( g_strcmp0(prev, value) ) { + g_key_file_set_string(active_ini, entry, key, value); ret = SET_CONFIG_UPDATED; - g_free(keyfile); + } + + /* Filter out dynamic data that matches static values */ + config_purge_data(active_ini, static_ini); + + /* Update data on filesystem if changed */ + config_save_dynamic_config(active_ini); + + g_free(prev); + g_key_file_free(active_ini); + g_key_file_free(static_ini); return ret; } @@ -688,50 +603,16 @@ set_config_result_t config_set_network_setting(const char *config, const char *s { LOG_REGISTER_CONTEXT; - GKeyFile *settingsfile; - gboolean test = FALSE; - gchar *keyfile; - if(!strcmp(config, NETWORK_IP_KEY) || !strcmp(config, NETWORK_GATEWAY_KEY)) if(config_validate_ip(setting) != 0) return SET_CONFIG_ERROR; - settingsfile = g_key_file_new(); - test = g_key_file_load_from_file(settingsfile, FS_MOUNT_CONFIG_FILE, G_KEY_FILE_NONE, NULL); - if(!strcmp(config, NETWORK_IP_KEY) || !strcmp(config, NETWORK_INTERFACE_KEY) || !strcmp(config, NETWORK_GATEWAY_KEY)) { - set_config_result_t ret = SET_CONFIG_ERROR; - if (test) - { - if(!config_value_changed(settingsfile, NETWORK_ENTRY, config, setting)) - { - g_key_file_free(settingsfile); - return SET_CONFIG_UNCHANGED; - } - } - else - { - log_debug("No conffile. Creating.\n"); - config_create_conf_file(); - } - - g_key_file_set_string(settingsfile, NETWORK_ENTRY, config, setting); - keyfile = g_key_file_to_data (settingsfile, NULL, NULL); - /* free the settingsfile before writing things out to be sure - * the contents will be correctly written to file afterwards. - * Just a precaution. */ - g_key_file_free(settingsfile); - if (g_file_set_contents(FS_MOUNT_CONFIG_FILE, keyfile, -1, NULL)) - ret = SET_CONFIG_UPDATED; - free(keyfile); - return ret; - } - else - { - g_key_file_free(settingsfile); - return SET_CONFIG_ERROR; + return config_set_config_setting(NETWORK_ENTRY, config, setting); } + + return SET_CONFIG_ERROR; } char * config_get_network_setting(const char *config) @@ -835,7 +716,7 @@ static void config_merge_group(GKeyFile *dest, GKeyFile *srce, * @param dest keyfile to modify * @param srce keyfile to merge from */ -static void config_merge_file(GKeyFile *dest, GKeyFile *srce) +static void config_merge_data(GKeyFile *dest, GKeyFile *srce) { LOG_REGISTER_CONTEXT; @@ -848,6 +729,53 @@ static void config_merge_file(GKeyFile *dest, GKeyFile *srce) } } +static void config_purge_data(GKeyFile *dest, GKeyFile *srce) +{ + LOG_REGISTER_CONTEXT; + + gsize groups = 0; + gchar **group = g_key_file_get_groups(srce, &groups); + for( gsize g = 0; g < groups; ++g ) { + gsize keys = 0; + gchar **key = g_key_file_get_keys(srce, group[g], &keys, 0); + for( gsize k = 0; k < keys; ++k ) { + gchar *cur_val = g_key_file_get_value(dest, group[g], key[k], 0); + if( !cur_val ) + continue; + + gchar *def_val = g_key_file_get_value(srce, group[g], key[k], 0); + + if( !g_strcmp0(cur_val, def_val) ) { + log_debug("purge redundant: [%s] %s = %s", + group[g], key[k], cur_val); + g_key_file_remove_key(dest, group[g], key[k], 0); + } + g_free(def_val); + g_free(cur_val); + } + g_strfreev(key); + } + g_strfreev(group); +} + +static void config_purge_empty_groups(GKeyFile *dest) +{ + LOG_REGISTER_CONTEXT; + + gsize groups = 0; + gchar **group = g_key_file_get_groups(dest, &groups); + for( gsize g = 0; g < groups; ++g ) { + gsize keys = 0; + gchar **key = g_key_file_get_keys(dest, group[g], &keys, 0); + if( keys == 0 ) { + log_debug("purge redundant group: [%s]", group[g]); + g_key_file_remove_group(dest, group[g], 0); + } + g_strfreev(key); + } + g_strfreev(group); +} + /** * Callback function for logging errors within glob() * @@ -864,93 +792,160 @@ static int config_glob_error_cb(const char *path, int err) return 0; } -/** - * Read *.ini files on CONFIG_FILE_DIR in the order of [0-9][A-Z][a-z] - * - * @return the in memory value-pair file. - */ -static GKeyFile *config_read_ini_files(void) +static bool config_merge_from_file(GKeyFile *ini, const char *path) { LOG_REGISTER_CONTEXT; - static const char pattern[] = CONFIG_FILE_DIR"/*.ini"; + bool ack = false; + GError *err = 0; + GKeyFile *tmp = g_key_file_new(); - GKeyFile *ini = g_key_file_new(); - glob_t gb; + if( !g_key_file_load_from_file(tmp, path, 0, &err) ) { + log_debug("%s: can't load: %s", path, err->message); + } else { + log_debug("processing %s ...", path); + config_merge_data(ini, tmp); + ack = true; + } + g_clear_error(&err); + g_key_file_free(tmp); + return ack; +} - memset(&gb, 0, sizeof gb); +static void config_load_static_config(GKeyFile *ini) +{ + LOG_REGISTER_CONTEXT; + + static const char pattern[] = USB_MODED_STATIC_CONFIG_DIR"/*.ini"; + + glob_t gb = {}; - if( glob(pattern, 0, config_glob_error_cb, &gb) != 0 ) { + if( glob(pattern, 0, config_glob_error_cb, &gb) != 0 ) log_debug("no configuration ini-files found"); - g_key_file_free(ini); - ini = NULL; - goto exit; - } for( size_t i = 0; i < gb.gl_pathc; ++i ) { const char *path = gb.gl_pathv[i]; - GError *err = 0; - GKeyFile *tmp = g_key_file_new(); - - if( !g_key_file_load_from_file(tmp, path, 0, &err) ) { - log_debug("%s: can't load: %s", path, err->message); - } else { - log_debug("processing %s ...", path); - config_merge_file(ini, tmp); + if( strcmp(path, USB_MODED_STATIC_CONFIG_FILE) ) + config_merge_from_file(ini, path); + } + + globfree(&gb); +} + +static bool config_load_legacy_config(GKeyFile *ini) +{ + LOG_REGISTER_CONTEXT; + + bool ack = false; + if( access(USB_MODED_STATIC_CONFIG_FILE, F_OK) != -1 ) + ack = config_merge_from_file(ini, USB_MODED_STATIC_CONFIG_FILE); + return ack; +} + +static void config_remove_legacy_config(void) +{ + LOG_REGISTER_CONTEXT; + + if( unlink(USB_MODED_STATIC_CONFIG_FILE) == -1 && errno != ENOENT ) { + log_warning("%s: can't remove stale config file: %m", + USB_MODED_STATIC_CONFIG_FILE); + } +} + +static void config_load_dynamic_config(GKeyFile *ini) +{ + LOG_REGISTER_CONTEXT; + + config_merge_from_file(ini, USB_MODED_DYNAMIC_CONFIG_FILE); +} + +static void config_save_dynamic_config(GKeyFile *ini) +{ + LOG_REGISTER_CONTEXT; + + gchar *current_dta = 0; + gchar *previous_dta = 0; + + config_purge_empty_groups(ini); + current_dta = g_key_file_to_data(ini, 0, 0); + + g_file_get_contents(USB_MODED_DYNAMIC_CONFIG_FILE, &previous_dta, 0, 0); + if( g_strcmp0(previous_dta, current_dta) ) { + GError *err = 0; + if( mkdir(USB_MODED_DYNAMIC_CONFIG_DIR, 0755) == -1 && errno != EEXIST ) { + log_err("%s: can't create dir: %m", USB_MODED_DYNAMIC_CONFIG_DIR); + } + else if( !g_file_set_contents(USB_MODED_DYNAMIC_CONFIG_FILE, + current_dta, -1, &err) ) { + log_err("%s: can't save: %s", USB_MODED_DYNAMIC_CONFIG_FILE, + err->message); + } + else { + log_debug("%s: updated", USB_MODED_DYNAMIC_CONFIG_FILE); + + /* The legacy file is not needed anymore */ + config_remove_legacy_config(); } g_clear_error(&err); - g_key_file_free(tmp); } -exit: - globfree(&gb); - return ini; + + g_free(current_dta); + g_free(previous_dta); } /** - * Read the *.ini files and create/overwrite FS_MOUNT_CONFIG_FILE with + * Read the *.ini files and create/overwrite USB_MODED_STATIC_CONFIG_FILE with * the merged data. * * @return 0 on failure */ -int config_merge_conf_file(void) +bool config_init(void) { LOG_REGISTER_CONTEXT; - GString *keyfile_string = NULL; - GKeyFile *settingsfile,*tempfile; - int ret = 0; + bool ack = true; - settingsfile = config_read_ini_files(); - if (!settingsfile) - { - log_debug("No configuration. Creating defaults."); - config_create_conf_file(); - /* There was no configuration so no info to be merged */ - return ret; - } + GKeyFile *legacy_ini = g_key_file_new(); + GKeyFile *static_ini = g_key_file_new(); + GKeyFile *active_ini = g_key_file_new(); - tempfile = g_key_file_new(); - if (g_key_file_load_from_file(tempfile, FS_MOUNT_CONFIG_FILE, - G_KEY_FILE_NONE,NULL)) { - if (!g_strcmp0(g_key_file_to_data(settingsfile, NULL, NULL), - g_key_file_to_data(tempfile, NULL, NULL))) - goto out; - } + /* Setup built-in defaults */ + config_setup_default_values(active_ini); + + /* Load static configuration */ + config_load_static_config(static_ini); - log_debug("Merging configuration"); - keyfile_string = g_string_new(NULL); - keyfile_string = g_string_append(keyfile_string, - g_key_file_to_data(settingsfile, - NULL, NULL)); - if (keyfile_string) { - ret = !g_file_set_contents(FS_MOUNT_CONFIG_FILE, - keyfile_string->str,-1, NULL); - g_string_free(keyfile_string, TRUE); + /* Handle legacy settings */ + if( config_load_legacy_config(legacy_ini) ) { + config_purge_data(legacy_ini, static_ini); + config_merge_data(active_ini, legacy_ini); } -out: - g_key_file_free(tempfile); - g_key_file_free(settingsfile); - return ret; + + /* Load dynamic settings */ + config_load_dynamic_config(active_ini); + + /* Filter out dynamic data that matches static values */ + config_purge_data(active_ini, static_ini); + + /* Update data on filesystem if changed */ + config_save_dynamic_config(active_ini); + + g_key_file_free(active_ini); + g_key_file_free(static_ini); + g_key_file_free(legacy_ini); + + return ack; +} + +static GKeyFile *config_get_settings(void) +{ + LOG_REGISTER_CONTEXT; + + GKeyFile *ini = g_key_file_new(); + config_setup_default_values(ini); + config_load_static_config(ini); + config_load_dynamic_config(ini); + return ini; } char * config_get_android_manufacturer(void) diff --git a/src/usb_moded-config.h b/src/usb_moded-config.h index 73e95cf..73eb181 100644 --- a/src/usb_moded-config.h +++ b/src/usb_moded-config.h @@ -34,9 +34,6 @@ * Constants * ========================================================================= */ -# define CONFIG_FILE_DIR "/etc/usb-moded" -# define FS_MOUNT_CONFIG_FILE CONFIG_FILE_DIR"/usb-moded.ini" - # define MODE_SETTING_ENTRY "usbmode" # define MODE_SETTING_KEY "mode" # define FS_MOUNT_DEFAULT "/dev/mmcblk0p1" diff --git a/src/usb_moded.c b/src/usb_moded.c index 725a689..9eb6604 100644 --- a/src/usb_moded.c +++ b/src/usb_moded.c @@ -539,7 +539,7 @@ static bool usbmoded_init(void) modesetting_init(); /* check config, merge or create if outdated */ - if( config_merge_conf_file() != 0 ) { + if( !config_init() ) { log_crit("Cannot create or find a valid configuration"); goto EXIT; }