From 6345767b932f2ac1927be91d0213be251391f30b Mon Sep 17 00:00:00 2001 From: Alexandre Rostovtsev Date: Wed, 5 Sep 2012 17:44:22 -0400 Subject: Merge *-utils, rename ShellUtilsTrivial to ShellParser, fix memory leaks --- Makefile.am | 6 +- src/bus-utils.c | 109 --------- src/bus-utils.h | 11 +- src/hostnamed.c | 47 ++-- src/localed.c | 59 ++--- src/main.c | 6 +- src/shell-utils.c | 598 --------------------------------------------- src/shell-utils.h | 86 ------- src/timedated.c | 9 +- src/utils.c | 710 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/utils.h | 102 ++++++++ 11 files changed, 878 insertions(+), 865 deletions(-) delete mode 100644 src/shell-utils.c delete mode 100644 src/shell-utils.h create mode 100644 src/utils.c create mode 100644 src/utils.h diff --git a/Makefile.am b/Makefile.am index 32890fb..e12c3d7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -59,10 +59,8 @@ openrc_settingsd_SOURCES = \ src/localed.h \ src/timedated.c \ src/timedated.h \ - src/bus-utils.c \ - src/bus-utils.h \ - src/shell-utils.c \ - src/shell-utils.h \ + src/utils.c \ + src/utils.h \ src/main.c \ $(NULL) diff --git a/src/bus-utils.c b/src/bus-utils.c index f78f8f2..8db9bb4 100644 --- a/src/bus-utils.c +++ b/src/bus-utils.c @@ -22,113 +22,4 @@ #include "bus-utils.h" -struct check_polkit_data { - const gchar *unique_name; - const gchar *action_id; - gboolean user_interaction; - GAsyncReadyCallback callback; - gpointer user_data; - PolkitAuthority *authority; - PolkitSubject *subject; -}; - -void -check_polkit_data_free (struct check_polkit_data *data) -{ - if (data == NULL) - return; - - if (data->subject != NULL) - g_object_unref (data->subject); - if (data->authority != NULL) - g_object_unref (data->authority); - - g_free (data); -} - -gboolean -check_polkit_finish (GAsyncResult *res, - GError **error) -{ - GSimpleAsyncResult *simple; - - simple = G_SIMPLE_ASYNC_RESULT (res); - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - return g_simple_async_result_get_op_res_gboolean (simple); -} - -static void -check_polkit_authorization_cb (GObject *source_object, - GAsyncResult *res, - gpointer _data) -{ - struct check_polkit_data *data; - PolkitAuthorizationResult *result; - GSimpleAsyncResult *simple; - GError *err = NULL; - - data = (struct check_polkit_data *) _data; - if ((result = polkit_authority_check_authorization_finish (data->authority, res, &err)) == NULL) { - g_simple_async_report_take_gerror_in_idle (NULL, data->callback, data->user_data, err); - goto out; - } - - if (!polkit_authorization_result_get_is_authorized (result)) { - g_simple_async_report_error_in_idle (NULL, data->callback, data->user_data, POLKIT_ERROR, POLKIT_ERROR_NOT_AUTHORIZED, "Authorizing for '%s': not authorized", data->action_id); - goto out; - } - simple = g_simple_async_result_new (NULL, data->callback, data->user_data, check_polkit_async); - g_simple_async_result_set_op_res_gboolean (simple, TRUE); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - - out: - check_polkit_data_free (data); - if (result != NULL) - g_object_unref (result); -} - -static void -check_polkit_authority_cb (GObject *source_object, - GAsyncResult *res, - gpointer _data) -{ - struct check_polkit_data *data; - GError *err = NULL; - - data = (struct check_polkit_data *) _data; - if ((data->authority = polkit_authority_get_finish (res, &err)) == NULL) { - g_simple_async_report_take_gerror_in_idle (NULL, data->callback, data->user_data, err); - check_polkit_data_free (data); - return; - } - if (data->unique_name == NULL || data->action_id == NULL || - (data->subject = polkit_system_bus_name_new (data->unique_name)) == NULL) { - g_simple_async_report_error_in_idle (NULL, data->callback, data->user_data, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Authorizing for '%s': failed sanity check", data->action_id); - check_polkit_data_free (data); - return; - } - polkit_authority_check_authorization (data->authority, data->subject, data->action_id, NULL, (PolkitCheckAuthorizationFlags) data->user_interaction, NULL, check_polkit_authorization_cb, data); -} - -void -check_polkit_async (const gchar *unique_name, - const gchar *action_id, - const gboolean user_interaction, - GAsyncReadyCallback callback, - gpointer user_data) -{ - struct check_polkit_data *data; - - data = g_new0 (struct check_polkit_data, 1); - data->unique_name = unique_name; - data->action_id = action_id; - data->user_interaction = user_interaction; - data->callback = callback; - data->user_data = user_data; - - polkit_authority_get_async (NULL, check_polkit_authority_cb, data); -} diff --git a/src/bus-utils.h b/src/bus-utils.h index 4a57bb5..282a63e 100644 --- a/src/bus-utils.h +++ b/src/bus-utils.h @@ -21,15 +21,6 @@ #include -void -check_polkit_async (const gchar *unique_name, - const gchar *action_id, - const gboolean user_interaction, - GAsyncReadyCallback callback, - gpointer user_data); - -gboolean -check_polkit_finish (GAsyncResult *res, - GError **error); + #endif diff --git a/src/hostnamed.c b/src/hostnamed.c index f0992bd..1fb14c0 100644 --- a/src/hostnamed.c +++ b/src/hostnamed.c @@ -28,8 +28,7 @@ #include "hostnamed.h" #include "hostname1-generated.h" -#include "bus-utils.h" -#include "shell-utils.h" +#include "utils.h" #include "config.h" @@ -66,10 +65,12 @@ hostname_is_valid (const gchar *name) name, G_REGEX_MULTILINE, 0); } -static void +static gchar * guess_icon_name () { /* TODO: detect virtualization by reading rc_sys */ + gchar *filebuf = NULL; + gchar *ret = NULL; #if defined(__i386__) || defined(__x86_64__) /* @@ -80,32 +81,34 @@ guess_icon_name () http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf */ - gchar *contents; - if (g_file_get_contents ("/sys/class/dmi/id/chassis_type", &contents, NULL, NULL)) { - switch (g_ascii_strtoull (contents, NULL, 10)) { + if (g_file_get_contents ("/sys/class/dmi/id/chassis_type", &filebuf, NULL, NULL)) { + switch (g_ascii_strtoull (filebuf, NULL, 10)) { case 0x3: case 0x4: case 0x5: case 0x6: case 0x7: - icon_name = g_strdup ("computer-desktop"); - return; + ret = g_strdup ("computer-desktop"); + goto out; case 0x9: case 0xA: case 0xE: - icon_name = g_strdup ("computer-laptop"); - return; + ret = g_strdup ("computer-laptop"); + goto out; case 0x11: case 0x17: case 0x1C: case 0x1D: - icon_name = g_strdup ("computer-server"); - return; + ret = g_strdup ("computer-server"); + goto out; } } #endif - icon_name = g_strdup ("computer"); + ret = g_strdup ("computer"); + out: + g_free (filebuf); + return ret; } static void @@ -198,7 +201,7 @@ on_handle_set_static_hostname_authorized_cb (GObject *source_object, data->name = g_strdup ("localhost"); } - if (!shell_utils_trivial_set_and_save (static_hostname_file, &err, "hostname", "HOSTNAME", data->name, NULL)) { + if (!shell_parser_set_and_save (static_hostname_file, &err, "hostname", "HOSTNAME", data->name, NULL)) { g_dbus_method_invocation_return_gerror (data->invocation, err); G_UNLOCK (static_hostname); goto out; @@ -257,7 +260,7 @@ on_handle_set_pretty_hostname_authorized_cb (GObject *source_object, if (data->name == NULL) data->name = g_strdup (""); - if (!shell_utils_trivial_set_and_save (machine_info_file, &err, "PRETTY_HOSTNAME", NULL, data->name, NULL)) { + if (!shell_parser_set_and_save (machine_info_file, &err, "PRETTY_HOSTNAME", NULL, data->name, NULL)) { g_dbus_method_invocation_return_gerror (data->invocation, err); G_UNLOCK (machine_info); goto out; @@ -316,7 +319,7 @@ on_handle_set_icon_name_authorized_cb (GObject *source_object, if (data->name == NULL) data->name = g_strdup (""); - if (!shell_utils_trivial_set_and_save (machine_info_file, &err, "ICON_NAME", NULL, data->name, NULL)) { + if (!shell_parser_set_and_save (machine_info_file, &err, "ICON_NAME", NULL, data->name, NULL)) { g_dbus_method_invocation_return_gerror (data->invocation, err); G_UNLOCK (machine_info); goto out; @@ -425,13 +428,13 @@ hostnamed_init (gboolean _read_only) static_hostname_file = g_file_new_for_path (SYSCONFDIR "/conf.d/hostname"); machine_info_file = g_file_new_for_path (SYSCONFDIR "/machine-info"); - static_hostname = shell_utils_source_var (static_hostname_file, "${hostname-${HOSTNAME-localhost}}", &err); + static_hostname = shell_source_var (static_hostname_file, "${hostname-${HOSTNAME-localhost}}", &err); if (err != NULL) { g_debug ("%s", err->message); g_error_free (err); err = NULL; } - pretty_hostname = shell_utils_source_var (machine_info_file, "${PRETTY_HOSTNAME}", &err); + pretty_hostname = shell_source_var (machine_info_file, "${PRETTY_HOSTNAME}", &err); if (pretty_hostname == NULL) pretty_hostname = g_strdup (""); if (err != NULL) { @@ -439,7 +442,7 @@ hostnamed_init (gboolean _read_only) g_error_free (err); err = NULL; } - icon_name = shell_utils_source_var (machine_info_file, "${ICON_NAME}", &err); + icon_name = shell_source_var (machine_info_file, "${ICON_NAME}", &err); if (icon_name == NULL) icon_name = g_strdup (""); if (err != NULL) { @@ -448,8 +451,10 @@ hostnamed_init (gboolean _read_only) err = NULL; } - if (icon_name == NULL || strlen (icon_name) == 0) - guess_icon_name (); + if (icon_name == NULL || *icon_name == 0) { + g_free (icon_name); + icon_name = guess_icon_name (); + } read_only = _read_only; diff --git a/src/localed.c b/src/localed.c index 2e8e44e..0cb4404 100644 --- a/src/localed.c +++ b/src/localed.c @@ -26,8 +26,7 @@ #include "localed.h" #include "locale1-generated.h" -#include "bus-utils.h" -#include "shell-utils.h" +#include "utils.h" #include "config.h" @@ -215,16 +214,17 @@ kbd_model_map_load (GError **error) for (line = filebuf; *line != 0; line = newline + 1) { struct kbd_model_map_entry *entry = NULL; GMatchInfo *match_info = NULL; + gboolean m = FALSE; if ((newline = strstr (line, "\n")) != NULL) *newline = 0; else newline = line + strlen (line) - 1; - if (g_regex_match (kbd_model_map_line_comment_re, line, 0, &match_info)) { - g_match_info_free (match_info); + m = g_regex_match (kbd_model_map_line_comment_re, line, 0, &match_info); + _g_match_info_clear (&match_info); + if (m) continue; - } if (!g_regex_match (kbd_model_map_line_re, line, 0, &match_info)) { g_propagate_error (error, @@ -253,7 +253,7 @@ kbd_model_map_load (GError **error) entry->x11_options[0] = 0; ret = g_list_prepend (ret, entry); - g_match_info_free (match_info); + _g_match_info_clear (&match_info); } out: if (ret != NULL) @@ -452,6 +452,7 @@ xorg_confd_parser_new (GFile *xorg_confd_file, struct xorg_confd_line_entry *entry = NULL; GMatchInfo *match_info = NULL; gboolean matched = FALSE; + gboolean m = FALSE; if ((newline = strstr (line, "\n")) != NULL) *newline = 0; @@ -463,48 +464,48 @@ xorg_confd_parser_new (GFile *xorg_confd_file, if (g_regex_match (xorg_confd_line_comment_re, line, 0, &match_info)) { g_debug ("Parsed line '%s' as comment", line); entry->type = XORG_CONFD_LINE_TYPE_COMMENT; - } else if (g_regex_match (xorg_confd_line_section_input_class_re, line, 0, &match_info)) { + } else if (_g_match_info_clear (&match_info) && g_regex_match (xorg_confd_line_section_input_class_re, line, 0, &match_info)) { g_debug ("Parsed line '%s' as InputClass section", line); if (in_section) goto no_match; in_section = TRUE; entry->type = XORG_CONFD_LINE_TYPE_SECTION_INPUT_CLASS; - } else if (g_regex_match (xorg_confd_line_section_re, line, 0, &match_info)) { + } else if (_g_match_info_clear (&match_info) && g_regex_match (xorg_confd_line_section_re, line, 0, &match_info)) { g_debug ("Parsed line '%s' as non-InputClass section", line); if (in_section) goto no_match; in_section = TRUE; entry->type = XORG_CONFD_LINE_TYPE_SECTION_OTHER; - } else if (g_regex_match (xorg_confd_line_end_section_re, line, 0, &match_info)) { + } else if (_g_match_info_clear (&match_info) && g_regex_match (xorg_confd_line_end_section_re, line, 0, &match_info)) { g_debug ("Parsed line '%s' as end of section", line); if (!in_section) goto no_match; entry->type = XORG_CONFD_LINE_TYPE_END_SECTION; - } else if (g_regex_match (xorg_confd_line_match_is_keyboard_re, line, 0, &match_info)) { + } else if (_g_match_info_clear (&match_info) && g_regex_match (xorg_confd_line_match_is_keyboard_re, line, 0, &match_info)) { g_debug ("Parsed line '%s' as MatchIsKeyboard declaration", line); if (!in_section) goto no_match; entry->type = XORG_CONFD_LINE_TYPE_MATCH_IS_KEYBOARD; in_xkb_section = TRUE; - } else if (g_regex_match (xorg_confd_line_xkb_layout_re, line, 0, &match_info)) { + } else if (_g_match_info_clear (&match_info) && g_regex_match (xorg_confd_line_xkb_layout_re, line, 0, &match_info)) { g_debug ("Parsed line '%s' as XkbLayout option", line); if (!in_section) goto no_match; entry->type = XORG_CONFD_LINE_TYPE_XKB_LAYOUT; entry->value = g_match_info_fetch (match_info, 2); - } else if (g_regex_match (xorg_confd_line_xkb_model_re, line, 0, &match_info)) { + } else if (_g_match_info_clear (&match_info) && g_regex_match (xorg_confd_line_xkb_model_re, line, 0, &match_info)) { g_debug ("Parsed line '%s' as XkbModel option", line); if (!in_section) goto no_match; entry->type = XORG_CONFD_LINE_TYPE_XKB_MODEL; entry->value = g_match_info_fetch (match_info, 2); - } else if (g_regex_match (xorg_confd_line_xkb_variant_re, line, 0, &match_info)) { + } else if (_g_match_info_clear (&match_info) && g_regex_match (xorg_confd_line_xkb_variant_re, line, 0, &match_info)) { g_debug ("Parsed line '%s' as XkbVariant option", line); if (!in_section) goto no_match; entry->type = XORG_CONFD_LINE_TYPE_XKB_VARIANT; entry->value = g_match_info_fetch (match_info, 2); - } else if (g_regex_match (xorg_confd_line_xkb_options_re, line, 0, &match_info)) { + } else if (_g_match_info_clear (&match_info) && g_regex_match (xorg_confd_line_xkb_options_re, line, 0, &match_info)) { g_debug ("Parsed line '%s' as XkbOptions option", line); if (!in_section) goto no_match; @@ -515,7 +516,7 @@ xorg_confd_parser_new (GFile *xorg_confd_file, if (entry->type == XORG_CONFD_LINE_TYPE_UNKNOWN) g_debug ("Parsing line '%s' as unknown", line); - g_match_info_free (match_info); + _g_match_info_clear (&match_info); parser->line_list = g_list_prepend (parser->line_list, entry); if (in_section) { if (entry->type == XORG_CONFD_LINE_TYPE_SECTION_INPUT_CLASS) @@ -534,7 +535,7 @@ xorg_confd_parser_new (GFile *xorg_confd_file, no_match: /* Nothing matched... */ g_free (entry); - g_match_info_free (match_info); + _g_match_info_clear (&match_info); goto parse_fail; } @@ -787,7 +788,7 @@ on_handle_set_locale_authorized_cb (GObject *source_object, GError *err = NULL; struct invoked_locale *data; gchar **loc, **var, **val, **locale_values = NULL; - ShellUtilsTrivial *locale_file_parsed = NULL; + ShellParser *locale_file_parsed = NULL; data = (struct invoked_locale *) user_data; if (!check_polkit_finish (res, &err)) { @@ -825,16 +826,16 @@ on_handle_set_locale_authorized_cb (GObject *source_object, } } - if ((locale_file_parsed = shell_utils_trivial_new (locale_file, &err)) == NULL) { + if ((locale_file_parsed = shell_parser_new (locale_file, &err)) == NULL) { g_dbus_method_invocation_return_gerror (data->invocation, err); G_UNLOCK (locale); goto out; } - if (shell_utils_trivial_is_empty (locale_file_parsed)) { + if (shell_parser_is_empty (locale_file_parsed)) { /* Simply write the new env file */ - shell_utils_trivial_free (locale_file_parsed); - if ((locale_file_parsed = shell_utils_trivial_new_from_string (locale_file, "# Configuration file for eselect\n# This file has been automatically generated\n", &err)) == NULL) { + shell_parser_free (locale_file_parsed); + if ((locale_file_parsed = shell_parser_new_from_string (locale_file, "# Configuration file for eselect\n# This file has been automatically generated\n", &err)) == NULL) { g_dbus_method_invocation_return_gerror (data->invocation, err); G_UNLOCK (locale); goto out; @@ -843,12 +844,12 @@ on_handle_set_locale_authorized_cb (GObject *source_object, for (val = locale_values, var = locale_variables; *var != NULL; val++, var++) { if (*val == NULL) - shell_utils_trivial_clear_variable (locale_file_parsed, *var); + shell_parser_clear_variable (locale_file_parsed, *var); else - shell_utils_trivial_set_variable (locale_file_parsed, *var, *val, TRUE); + shell_parser_set_variable (locale_file_parsed, *var, *val, TRUE); } - if (!shell_utils_trivial_save (locale_file_parsed, &err)) { + if (!shell_parser_save (locale_file_parsed, &err)) { g_dbus_method_invocation_return_gerror (data->invocation, err); G_UNLOCK (locale); goto out; @@ -869,7 +870,7 @@ on_handle_set_locale_authorized_cb (GObject *source_object, G_UNLOCK (locale); out: - shell_utils_trivial_free (locale_file_parsed); + shell_parser_free (locale_file_parsed); g_strfreev (locale_values); invoked_locale_free (data); if (err != NULL) @@ -954,7 +955,7 @@ on_handle_set_vconsole_keyboard_authorized_cb (GObject *source_object, } /* We do not set vconsole_keymap_toggle because there is no good equivalent for it in OpenRC */ - if (!shell_utils_trivial_set_and_save (keymaps_file, &err, "keymap", NULL, data->vconsole_keymap, NULL)) { + if (!shell_parser_set_and_save (keymaps_file, &err, "keymap", NULL, data->vconsole_keymap, NULL)) { g_dbus_method_invocation_return_gerror (data->invocation, err); goto unlock; } @@ -1145,7 +1146,7 @@ on_handle_set_x11_keyboard_authorized_cb (GObject *source_object, g_printerr ("Failed to find conversion entry for x11 layout '%s' in '%s'\n", data->x11_layout, filename); g_free (filename); } else { - if (!shell_utils_trivial_set_and_save (keymaps_file, &err, "keymap", NULL, best_entry->vconsole_keymap, NULL)) { + if (!shell_parser_set_and_save (keymaps_file, &err, "keymap", NULL, best_entry->vconsole_keymap, NULL)) { g_dbus_method_invocation_return_gerror (data->invocation, err); goto unlock; } @@ -1273,7 +1274,7 @@ localed_init (gboolean _read_only) x11_systemd_file = g_file_new_for_path (SYSCONFDIR "/X11/xorg.conf.d/00-keyboard.conf"); locale = g_new0 (gchar *, g_strv_length (locale_variables) + 1); - locale_values = shell_utils_trivial_source_var_list (locale_file, (const gchar * const *)locale_variables, &err); + locale_values = shell_parser_source_var_list (locale_file, (const gchar * const *)locale_variables, &err); if (locale_values != NULL) { gchar **variable, **value, **loc; loc = locale; @@ -1291,7 +1292,7 @@ localed_init (gboolean _read_only) g_clear_error (&err); } - vconsole_keymap = shell_utils_source_var (keymaps_file, "${keymap}", &err); + vconsole_keymap = shell_source_var (keymaps_file, "${keymap}", &err); if (vconsole_keymap == NULL) vconsole_keymap = g_strdup (""); if (err != NULL) { diff --git a/src/main.c b/src/main.c index b7d6324..54eb407 100644 --- a/src/main.c +++ b/src/main.c @@ -25,7 +25,7 @@ #include "hostnamed.h" #include "localed.h" #include "timedated.h" -#include "shell-utils.h" +#include "utils.h" #include "config.h" @@ -76,7 +76,7 @@ main (gint argc, gchar *argv[]) } else g_log_set_default_handler (log_handler, NULL); - shell_utils_init (); + utils_init (); hostnamed_init (read_only); localed_init (read_only); timedated_init (read_only, ntp_preferred_service); @@ -87,7 +87,7 @@ main (gint argc, gchar *argv[]) timedated_destroy (); localed_destroy (); hostnamed_destroy (); - shell_utils_destroy (); + utils_destroy (); g_free (ntp_preferred_service); return 0; } diff --git a/src/shell-utils.c b/src/shell-utils.c deleted file mode 100644 index f5c9b20..0000000 --- a/src/shell-utils.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - Copyright 2012 Alexandre Rostovtsev - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - 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 -*/ - -#include -#include - -#include -#include - -#include "shell-utils.h" - -#include "config.h" - -GRegex *indent_regex = NULL; -GRegex *comment_regex = NULL; -GRegex *separator_regex = NULL; -GRegex *var_equals_regex = NULL; -GRegex *single_quoted_regex = NULL; -GRegex *double_quoted_regex = NULL; -GRegex *unquoted_regex = NULL; - -enum ShellEntryType { - SHELL_ENTRY_TYPE_INDENT, - SHELL_ENTRY_TYPE_COMMENT, - SHELL_ENTRY_TYPE_SEPARATOR, - SHELL_ENTRY_TYPE_ASSIGNMENT, -}; - -struct ShellEntry { - enum ShellEntryType type; - gchar *string; - gchar *variable; /* only relevant for assignments */ - gchar *unquoted_value; /* only relevant for assignments */ -}; - -gchar * -shell_utils_source_var (GFile *file, - const gchar *variable, - GError **error) -{ - gchar *argv[4] = { "sh", "-c", NULL, NULL }; - gchar *filename = NULL, *quoted_filename = NULL; - gchar *output = NULL; - GFileInfo *info; - const GFileAttributeInfo *attribute_info; - - filename = g_file_get_path (file); - if ((info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_ACCESS_CAN_READ, G_FILE_QUERY_INFO_NONE, NULL, error)) == NULL) { - g_prefix_error (error, "Unable to source '%s': ", filename); - goto out; - } - - if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR && - g_file_info_get_file_type (info) != G_FILE_TYPE_SYMBOLIC_LINK) { - g_propagate_error (error, - g_error_new (G_FILE_ERROR, G_FILE_ERROR_FAILED, - "Unable to source '%s': not a regular file", filename)); - goto out; - } - - if (!g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) { - g_propagate_error (error, - g_error_new (G_FILE_ERROR, G_FILE_ERROR_ACCES, - "Unable to source '%s': permission denied", filename)); - goto out; - } - - quoted_filename = g_shell_quote (filename); - argv[2] = g_strdup_printf (". %s; echo -n %s", quoted_filename, variable); - - if (!g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &output, NULL, NULL, error)) { - g_prefix_error (error, "Unable to source '%s': ", filename); - } - - out: - g_free (filename); - g_free (quoted_filename); - if (info != NULL) - g_object_unref (info); - if (argv[2] != NULL) - g_free (argv[2]); - return output; -} - -static void -shell_entry_free (struct ShellEntry *entry) -{ - if (entry == NULL) - return; - - g_free (entry->string); - g_free (entry->variable); - g_free (entry->unquoted_value); - g_free (entry); -} - -void -shell_utils_trivial_free (ShellUtilsTrivial *trivial) -{ - if (trivial == NULL) - return; - - if (trivial->file != NULL) - g_object_unref (trivial->file); - if (trivial->filename != NULL) - g_free (trivial->filename); - if (trivial->entry_list != NULL) - g_list_free_full (trivial->entry_list, (GDestroyNotify)shell_entry_free); - g_free (trivial); -} - -ShellUtilsTrivial * -shell_utils_trivial_new (GFile *file, - GError **error) -{ - gchar *filebuf = NULL; - GError *local_err = NULL; - - if (file == NULL) - return NULL; - - if (!g_file_load_contents (file, NULL, &filebuf, NULL, NULL, &local_err)) { - if (local_err != NULL) { - /* Inability to parse or open is a failure; file not existing at all is *not* a failure */ - if (local_err->code == G_IO_ERROR_NOT_FOUND) { - ShellUtilsTrivial *ret = NULL; - - g_error_free (local_err); - ret = g_new0 (ShellUtilsTrivial, 1); - g_object_ref (file); - ret->file = file; - ret->filename = g_file_get_path (file); - return ret; - } else { - gchar *filename; - filename = g_file_get_path (file); - g_propagate_prefixed_error (error, local_err, "Unable to read '%s':", filename); - g_free (filename); - } - } - return NULL; - } - return shell_utils_trivial_new_from_string (file, filebuf, error); -} - -ShellUtilsTrivial * -shell_utils_trivial_new_from_string (GFile *file, - gchar *filebuf, - GError **error) -{ - ShellUtilsTrivial *ret = NULL; - GError *local_err = NULL; - gchar *s; - - if (file == NULL || filebuf == NULL) - return NULL; - - ret = g_new0 (ShellUtilsTrivial, 1); - g_object_ref (file); - ret->file = file; - ret->filename = g_file_get_path (file); - - gboolean want_separator = FALSE; /* Do we expect the next entry to be a separator or comment? */ - s = filebuf; - while (*s != 0) { - g_debug ("Scanning string: ``%s''", s); - gboolean matched = FALSE; - GMatchInfo *match_info = NULL; - struct ShellEntry *entry = NULL; - - matched = g_regex_match (comment_regex, s, 0, &match_info); - if (matched) { - entry = g_new0 (struct ShellEntry, 1); - entry->type = SHELL_ENTRY_TYPE_COMMENT; - entry->string = g_match_info_fetch (match_info, 0); - ret->entry_list = g_list_prepend (ret->entry_list, entry); - s += strlen (entry->string); - g_debug ("Scanned comment: ``%s''", entry->string); - g_match_info_free (match_info); - match_info = NULL; - want_separator = FALSE; - continue; - } - g_match_info_free (match_info); - match_info = NULL; - - matched = g_regex_match (separator_regex, s, 0, &match_info); - if (matched) { - entry = g_new0 (struct ShellEntry, 1); - entry->type = SHELL_ENTRY_TYPE_SEPARATOR; - entry->string = g_match_info_fetch (match_info, 0); - ret->entry_list = g_list_prepend (ret->entry_list, entry); - s += strlen (entry->string); - g_debug ("Scanned separator: ``%s''", entry->string); - g_match_info_free (match_info); - match_info = NULL; - want_separator = FALSE; - continue; - } - g_match_info_free (match_info); - match_info = NULL; - - matched = g_regex_match (indent_regex, s, 0, &match_info); - if (matched) { - entry = g_new0 (struct ShellEntry, 1); - entry->type = SHELL_ENTRY_TYPE_INDENT; - entry->string = g_match_info_fetch (match_info, 0); - ret->entry_list = g_list_prepend (ret->entry_list, entry); - s += strlen (entry->string); - g_debug ("Scanned indent: ``%s''", entry->string); - g_match_info_free (match_info); - match_info = NULL; - continue; - } - g_match_info_free (match_info); - match_info = NULL; - - matched = g_regex_match (var_equals_regex, s, 0, &match_info); - if (matched) { - gchar *raw_value = NULL, *temp1 = NULL, *temp2 = NULL; - /* If we expect a separator and get an assignment instead, fail */ - if (want_separator) - goto no_match; - - entry = g_new0 (struct ShellEntry, 1); - entry->type = SHELL_ENTRY_TYPE_ASSIGNMENT; - entry->string = g_match_info_fetch (match_info, 0); - entry->variable = g_match_info_fetch (match_info, 1); - s += strlen (entry->string); - g_debug ("Scanned variable: ``%s''", entry->string); - g_match_info_free (match_info); - match_info = NULL; - want_separator = TRUE; - - while (*s != 0) { - g_debug ("Scanning string for values: ``%s''", s); - gboolean matched2 = FALSE; - - matched2 = g_regex_match (single_quoted_regex, s, 0, &match_info); - if (matched2) - goto append_value; - - g_match_info_free (match_info); - match_info = NULL; - - matched2 = g_regex_match (double_quoted_regex, s, 0, &match_info); - if (matched2) - goto append_value; - - g_match_info_free (match_info); - match_info = NULL; - - matched2 = g_regex_match (unquoted_regex, s, 0, &match_info); - if (matched2) - goto append_value; - - g_match_info_free (match_info); - match_info = NULL; - - break; -append_value: - - if (raw_value == NULL) { - raw_value = g_match_info_fetch (match_info, 0); - s += strlen (raw_value); - g_debug ("Scanned value: ``%s''", raw_value); - } else { - temp1 = raw_value; - temp2 = g_match_info_fetch (match_info, 0); - raw_value = g_strconcat (temp1, temp2, NULL); - s += strlen (temp2); - g_debug ("Scanned value: ``%s''", temp2); - g_free (temp1); - g_free (temp2); - } - g_match_info_free (match_info); - match_info = NULL; - } - - if (raw_value != NULL) { - entry->unquoted_value = g_shell_unquote (raw_value, &local_err); - g_debug ("Unquoted value: ``%s''", entry->unquoted_value); - temp1 = entry->string; - temp2 = raw_value; - entry->string = g_strconcat (temp1, temp2, NULL); - g_free (temp1); - g_free (temp2); - ret->entry_list = g_list_prepend (ret->entry_list, entry); - if (local_err != NULL) - goto no_match; - } - continue; - } - -no_match: - /* Nothing matches, parsing has failed! */ - g_match_info_free (match_info); - match_info = NULL; - if (local_err != NULL) - g_propagate_prefixed_error (error, local_err, "Unable to parse '%s':", ret->filename); - else - g_propagate_error (error, - g_error_new (G_FILE_ERROR, G_FILE_ERROR_FAILED, - "Unable to parse '%s'", ret->filename)); - shell_utils_trivial_free (ret); - return NULL; - } - - ret->entry_list = g_list_reverse (ret->entry_list); - return ret; -} - -gboolean -shell_utils_trivial_is_empty (ShellUtilsTrivial *trivial) -{ - if (trivial == NULL || trivial->entry_list == NULL) - return TRUE; - return FALSE; -} - -gboolean -shell_utils_trivial_set_variable (ShellUtilsTrivial *trivial, - const gchar *variable, - const gchar *value, - gboolean add_if_unset) -{ - GList *curr = NULL; - struct ShellEntry *found_entry = NULL; - gchar *quoted_value = NULL; - gboolean ret = FALSE; - - g_assert (trivial != NULL); - g_assert (variable != NULL); - - for (curr = trivial->entry_list; curr != NULL; curr = curr->next) { - struct ShellEntry *entry; - - entry = (struct ShellEntry *)(curr->data); - if (entry->type == SHELL_ENTRY_TYPE_ASSIGNMENT && g_strcmp0 (variable, entry->variable) == 0) - found_entry = entry; - } - - quoted_value = g_shell_quote (value); - - if (found_entry != NULL) { - g_free (found_entry->string); - found_entry->string = g_strdup_printf ("%s=%s", variable, quoted_value); - ret = TRUE; - } else { - if (add_if_unset) { - found_entry = g_new0 (struct ShellEntry, 1); - found_entry->type = SHELL_ENTRY_TYPE_ASSIGNMENT; - found_entry->variable = g_strdup (variable); - found_entry->string = g_strdup_printf ("%s=%s", variable, quoted_value); - trivial->entry_list = g_list_append (trivial->entry_list, found_entry); - ret = TRUE; - } - } - - g_free (quoted_value); - return ret; -} - -void -shell_utils_trivial_clear_variable (ShellUtilsTrivial *trivial, - const gchar *variable) -{ - GList *curr = NULL; - gboolean ret = FALSE; - - g_assert (trivial != NULL); - g_assert (variable != NULL); - - for (curr = trivial->entry_list; curr != NULL; ) { - struct ShellEntry *entry; - - entry = (struct ShellEntry *)(curr->data); - if (entry->type == SHELL_ENTRY_TYPE_ASSIGNMENT && g_strcmp0 (variable, entry->variable) == 0) { - GList *prev, *next; - - prev = curr->prev; - next = curr->next; - curr->prev = NULL; - curr->next = NULL; - g_list_free_full (curr, (GDestroyNotify)shell_entry_free); - if (prev != NULL) - prev->next = next; - if (next != NULL) - next->prev = prev; - curr = next; - } else - curr = curr->next; - } -} - -gboolean -shell_utils_trivial_save (ShellUtilsTrivial *trivial, - GError **error) -{ - gboolean ret = FALSE; - GList *curr = NULL; - GFileOutputStream *os; - - g_assert (trivial != NULL && trivial->file != NULL && trivial->filename != NULL); - if ((os = g_file_replace (trivial->file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error)) == NULL) { - g_prefix_error (error, "Unable to save '%s': ", trivial->filename); - goto out; - } - - for (curr = trivial->entry_list; curr != NULL; curr = curr->next) { - struct ShellEntry *entry; - gsize written; - - entry = (struct ShellEntry *)(curr->data); - if (!g_output_stream_write_all (G_OUTPUT_STREAM (os), entry->string, strlen (entry->string), &written, NULL, error)) { - g_prefix_error (error, "Unable to save '%s': ", trivial->filename); - goto out; - } - } - - if (!g_output_stream_close (G_OUTPUT_STREAM (os), NULL, error)) { - g_prefix_error (error, "Unable to save '%s': ", trivial->filename); - g_output_stream_close (G_OUTPUT_STREAM (os), NULL, NULL); - goto out; - } - ret = TRUE; - - out: - if (os) - g_object_unref (os); - return ret; -} - -gboolean -shell_utils_trivial_set_and_save (GFile *file, - GError **error, - const gchar *first_var_name, - const gchar *first_alt_var_name, - const gchar *first_value, - ...) -{ - va_list ap; - ShellUtilsTrivial *trivial; - gboolean ret = FALSE; - const gchar *var_name, *alt_var_name, *value; - - va_start (ap, first_value); - if ((trivial = shell_utils_trivial_new (file, error)) == NULL) - goto out; - - var_name = first_var_name; - alt_var_name = first_alt_var_name; - value = first_value; - do { - if (alt_var_name == NULL) { - if (!shell_utils_trivial_set_variable (trivial, var_name, value, TRUE)) { - g_propagate_error (error, - g_error_new (G_FILE_ERROR, G_FILE_ERROR_FAILED, - "Unable to set %s in '%s'", var_name, trivial->filename)); - goto out; - } - } else { - if (!shell_utils_trivial_set_variable (trivial, var_name, value, FALSE) && - !shell_utils_trivial_set_variable (trivial, alt_var_name, value, FALSE) && - !shell_utils_trivial_set_variable (trivial, var_name, value, TRUE)) { - g_propagate_error (error, - g_error_new (G_FILE_ERROR, G_FILE_ERROR_FAILED, - "Unable to set %s or %s in '%s'", var_name, alt_var_name, trivial->filename)); - goto out; - } - } - } while ((var_name = va_arg (ap, const gchar*)) != NULL ? - alt_var_name = va_arg (ap, const gchar*), value = va_arg (ap, const gchar*), 1 : 0); - - if (!shell_utils_trivial_save (trivial, error)) - goto out; - - ret = TRUE; - - out: - va_end (ap); - if (trivial != NULL) - shell_utils_trivial_free (trivial); - return ret; -} - -gchar ** -shell_utils_trivial_source_var_list (GFile *file, - const gchar * const *var_names, - GError **error) -{ - ShellUtilsTrivial *trivial; - gchar **ret = NULL, **value; - const gchar* const* var_name; - - if (var_names == NULL) - return NULL; - - if ((trivial = shell_utils_trivial_new (file, error)) == NULL) - return NULL; - - ret = g_new0 (gchar *, g_strv_length ((gchar **)var_names) + 1); - for (var_name = var_names, value = ret; *var_name != NULL; var_name++, value++) { - GList *curr; - for (curr = trivial->entry_list; curr != NULL; curr = curr->next) { - struct ShellEntry *entry; - - entry = (struct ShellEntry *)(curr->data); - if (entry->type == SHELL_ENTRY_TYPE_ASSIGNMENT && g_strcmp0 (*var_name, entry->variable) == 0) - *value = g_strdup (entry->unquoted_value); - } - } - shell_utils_trivial_free (trivial); - return ret; -} - -void -shell_utils_destroy (void) -{ - if (indent_regex != NULL) { - g_regex_unref (indent_regex); - indent_regex = NULL; - } - if (comment_regex != NULL) { - g_regex_unref (comment_regex); - comment_regex = NULL; - } - if (separator_regex != NULL) { - g_regex_unref (separator_regex); - separator_regex = NULL; - } - if (var_equals_regex != NULL) { - g_regex_unref (var_equals_regex); - var_equals_regex = NULL; - } - if (single_quoted_regex != NULL) { - g_regex_unref (single_quoted_regex); - single_quoted_regex = NULL; - } - if (double_quoted_regex != NULL) { - g_regex_unref (double_quoted_regex); - double_quoted_regex = NULL; - } - if (unquoted_regex != NULL) { - g_regex_unref (unquoted_regex); - unquoted_regex = NULL; - } -} - -void -shell_utils_init (void) -{ - if (indent_regex == NULL) { - indent_regex = g_regex_new ("^[ \\t]+", G_REGEX_ANCHORED, 0, NULL); - g_assert (indent_regex != NULL); - } - if (comment_regex == NULL) { - comment_regex = g_regex_new ("^#[^\\n]*\\n", G_REGEX_ANCHORED|G_REGEX_MULTILINE, 0, NULL); - g_assert (comment_regex != NULL); - } - if (separator_regex == NULL) { - separator_regex = g_regex_new ("^[ \\t;\\n\\r]*[;\\n][ \\t;\\n\\r]*", G_REGEX_ANCHORED|G_REGEX_MULTILINE, 0, NULL); - g_assert (separator_regex != NULL); - } - if (var_equals_regex == NULL) { - var_equals_regex = g_regex_new ("^([a-zA-Z_][a-zA-Z0-9_]*)(?:(?:\\\\\\n)*)=(?:(?:\\\\\\n)*)", G_REGEX_ANCHORED|G_REGEX_MULTILINE, 0, NULL); - g_assert (var_equals_regex != NULL); - } - if (single_quoted_regex == NULL) { - single_quoted_regex = g_regex_new ("^'[^']*'", G_REGEX_ANCHORED|G_REGEX_MULTILINE, 0, NULL); - g_assert (single_quoted_regex != NULL); - } - /* We do not want to allow $(...) or `...` constructs in double-quoted - * strings because they might have side effects, but ${...} is OK */ - if (double_quoted_regex == NULL) { - double_quoted_regex = g_regex_new ("^\"(?:[^\"`\\$]|\\\\[\"`\\$]|\\$\\{)*\"", G_REGEX_ANCHORED|G_REGEX_MULTILINE, 0, NULL); - g_assert (double_quoted_regex != NULL); - } - if (unquoted_regex == NULL) { - unquoted_regex = g_regex_new ("^(?:[^\\s\"'`\\$\\|&<>;]|\\\\[\\s\"'`\\$\\|&<>;]|\\$\\{)+", G_REGEX_ANCHORED|G_REGEX_MULTILINE, 0, NULL); - g_assert (unquoted_regex != NULL); - } -} diff --git a/src/shell-utils.h b/src/shell-utils.h deleted file mode 100644 index 629d017..0000000 --- a/src/shell-utils.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - Copyright 2012 Alexandre Rostovtsev - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - 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 -*/ - -#ifndef _SHELL_UTILS_H_ -#define _SHELL_UTILS_H_ - -#include -#include - -typedef struct _ShellUtilsTrivial ShellUtilsTrivial; - -struct _ShellUtilsTrivial -{ - GFile *file; - gchar *filename; - GList *entry_list; -}; - -gchar * -shell_utils_source_var (GFile *file, - const gchar *variable, - GError **error); - -void -shell_utils_init (void); - -void -shell_utils_destroy (void); - -ShellUtilsTrivial * -shell_utils_trivial_new (GFile *file, - GError **error); - -ShellUtilsTrivial * -shell_utils_trivial_new_from_string (GFile *file, - gchar *filebuf, - GError **error); - -void -shell_utils_trivial_free (ShellUtilsTrivial *trivial); - -gboolean -shell_utils_trivial_is_empty (ShellUtilsTrivial *trivial); - -gboolean -shell_utils_trivial_set_variable (ShellUtilsTrivial *trivial, - const gchar *variable, - const gchar *value, - gboolean add_if_unset); - -void -shell_utils_trivial_clear_variable (ShellUtilsTrivial *trivial, - const gchar *variable); - -gboolean -shell_utils_trivial_save (ShellUtilsTrivial *trivial, - GError **error); - -gboolean -shell_utils_trivial_set_and_save (GFile *file, - GError **error, - const gchar *first_var_name, - const gchar *first_alt_var_name, - const gchar *first_value, - ...); - -gchar ** -shell_utils_trivial_source_var_list (GFile *file, - const gchar * const *var_names, - GError **error); -#endif diff --git a/src/timedated.c b/src/timedated.c index b8a41fc..d6588f4 100644 --- a/src/timedated.c +++ b/src/timedated.c @@ -30,8 +30,7 @@ #include "copypaste/hwclock.h" #include "timedated.h" #include "timedate1-generated.h" -#include "bus-utils.h" -#include "shell-utils.h" +#include "utils.h" #include "config.h" @@ -61,7 +60,7 @@ get_local_rtc (GError **error) gchar *clock = NULL; gboolean ret = FALSE; - clock = shell_utils_source_var (hwclock_file, "${clock}", error); + clock = shell_source_var (hwclock_file, "${clock}", error); if (!g_strcmp0 (clock, "local")) ret = TRUE; return ret; @@ -468,9 +467,9 @@ on_handle_set_local_rtc_authorized_cb (GObject *source_object, } G_LOCK (clock); - clock = shell_utils_source_var (hwclock_file, "${clock}", NULL); + clock = shell_source_var (hwclock_file, "${clock}", NULL); if (clock != NULL || data->local_rtc) - if (!shell_utils_trivial_set_and_save (hwclock_file, &err, "clock", NULL, clock_types[data->local_rtc], NULL)) { + if (!shell_parser_set_and_save (hwclock_file, &err, "clock", NULL, clock_types[data->local_rtc], NULL)) { g_dbus_method_invocation_return_gerror (data->invocation, err); goto unlock; } diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..d21f9f4 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,710 @@ +/* + Copyright 2012 Alexandre Rostovtsev + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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 +*/ + +#include +#include + +#include +#include +#include + +#include "utils.h" + +#include "config.h" + +GRegex *indent_regex = NULL; +GRegex *comment_regex = NULL; +GRegex *separator_regex = NULL; +GRegex *var_equals_regex = NULL; +GRegex *single_quoted_regex = NULL; +GRegex *double_quoted_regex = NULL; +GRegex *unquoted_regex = NULL; + +/* Always returns TRUE */ +gboolean +_g_match_info_clear (GMatchInfo **match_info) +{ + if (match_info == NULL || *match_info == NULL) + return TRUE; + g_match_info_free (*match_info); + *match_info = NULL; + return TRUE; +} + +struct check_polkit_data { + const gchar *unique_name; + const gchar *action_id; + gboolean user_interaction; + GAsyncReadyCallback callback; + gpointer user_data; + + PolkitAuthority *authority; + PolkitSubject *subject; +}; + +void +check_polkit_data_free (struct check_polkit_data *data) +{ + if (data == NULL) + return; + + if (data->subject != NULL) + g_object_unref (data->subject); + if (data->authority != NULL) + g_object_unref (data->authority); + + g_free (data); +} + +gboolean +check_polkit_finish (GAsyncResult *res, + GError **error) +{ + GSimpleAsyncResult *simple; + + simple = G_SIMPLE_ASYNC_RESULT (res); + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + return g_simple_async_result_get_op_res_gboolean (simple); +} + +static void +check_polkit_authorization_cb (GObject *source_object, + GAsyncResult *res, + gpointer _data) +{ + struct check_polkit_data *data; + PolkitAuthorizationResult *result; + GSimpleAsyncResult *simple; + GError *err = NULL; + + data = (struct check_polkit_data *) _data; + if ((result = polkit_authority_check_authorization_finish (data->authority, res, &err)) == NULL) { + g_simple_async_report_take_gerror_in_idle (NULL, data->callback, data->user_data, err); + goto out; + } + + if (!polkit_authorization_result_get_is_authorized (result)) { + g_simple_async_report_error_in_idle (NULL, data->callback, data->user_data, POLKIT_ERROR, POLKIT_ERROR_NOT_AUTHORIZED, "Authorizing for '%s': not authorized", data->action_id); + goto out; + } + simple = g_simple_async_result_new (NULL, data->callback, data->user_data, check_polkit_async); + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + + out: + check_polkit_data_free (data); + if (result != NULL) + g_object_unref (result); +} + +static void +check_polkit_authority_cb (GObject *source_object, + GAsyncResult *res, + gpointer _data) +{ + struct check_polkit_data *data; + GError *err = NULL; + + data = (struct check_polkit_data *) _data; + if ((data->authority = polkit_authority_get_finish (res, &err)) == NULL) { + g_simple_async_report_take_gerror_in_idle (NULL, data->callback, data->user_data, err); + check_polkit_data_free (data); + return; + } + if (data->unique_name == NULL || data->action_id == NULL || + (data->subject = polkit_system_bus_name_new (data->unique_name)) == NULL) { + g_simple_async_report_error_in_idle (NULL, data->callback, data->user_data, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Authorizing for '%s': failed sanity check", data->action_id); + check_polkit_data_free (data); + return; + } + polkit_authority_check_authorization (data->authority, data->subject, data->action_id, NULL, (PolkitCheckAuthorizationFlags) data->user_interaction, NULL, check_polkit_authorization_cb, data); +} + +void +check_polkit_async (const gchar *unique_name, + const gchar *action_id, + const gboolean user_interaction, + GAsyncReadyCallback callback, + gpointer user_data) +{ + struct check_polkit_data *data; + + data = g_new0 (struct check_polkit_data, 1); + data->unique_name = unique_name; + data->action_id = action_id; + data->user_interaction = user_interaction; + data->callback = callback; + data->user_data = user_data; + + polkit_authority_get_async (NULL, check_polkit_authority_cb, data); +} + +enum ShellEntryType { + SHELL_ENTRY_TYPE_INDENT, + SHELL_ENTRY_TYPE_COMMENT, + SHELL_ENTRY_TYPE_SEPARATOR, + SHELL_ENTRY_TYPE_ASSIGNMENT, +}; + +struct ShellEntry { + enum ShellEntryType type; + gchar *string; + gchar *variable; /* only relevant for assignments */ + gchar *unquoted_value; /* only relevant for assignments */ +}; + +gchar * +shell_source_var (GFile *file, + const gchar *variable, + GError **error) +{ + gchar *argv[4] = { "sh", "-c", NULL, NULL }; + gchar *filename = NULL, *quoted_filename = NULL; + gchar *output = NULL; + GFileInfo *info; + const GFileAttributeInfo *attribute_info; + + filename = g_file_get_path (file); + if ((info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_ACCESS_CAN_READ, G_FILE_QUERY_INFO_NONE, NULL, error)) == NULL) { + g_prefix_error (error, "Unable to source '%s': ", filename); + goto out; + } + + if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR && + g_file_info_get_file_type (info) != G_FILE_TYPE_SYMBOLIC_LINK) { + g_propagate_error (error, + g_error_new (G_FILE_ERROR, G_FILE_ERROR_FAILED, + "Unable to source '%s': not a regular file", filename)); + goto out; + } + + if (!g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) { + g_propagate_error (error, + g_error_new (G_FILE_ERROR, G_FILE_ERROR_ACCES, + "Unable to source '%s': permission denied", filename)); + goto out; + } + + quoted_filename = g_shell_quote (filename); + argv[2] = g_strdup_printf (". %s; echo -n %s", quoted_filename, variable); + + if (!g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &output, NULL, NULL, error)) { + g_prefix_error (error, "Unable to source '%s': ", filename); + } + + out: + g_free (filename); + g_free (quoted_filename); + if (info != NULL) + g_object_unref (info); + if (argv[2] != NULL) + g_free (argv[2]); + return output; +} + +static void +shell_entry_free (struct ShellEntry *entry) +{ + if (entry == NULL) + return; + + g_free (entry->string); + g_free (entry->variable); + g_free (entry->unquoted_value); + g_free (entry); +} + +void +shell_parser_free (ShellParser *parser) +{ + if (parser == NULL) + return; + + if (parser->file != NULL) + g_object_unref (parser->file); + if (parser->filename != NULL) + g_free (parser->filename); + if (parser->entry_list != NULL) + g_list_free_full (parser->entry_list, (GDestroyNotify)shell_entry_free); + g_free (parser); +} + +ShellParser * +shell_parser_new (GFile *file, + GError **error) +{ + gchar *filebuf = NULL; + GError *local_err = NULL; + ShellParser *ret = NULL; + + if (file == NULL) + return NULL; + + if (!g_file_load_contents (file, NULL, &filebuf, NULL, NULL, &local_err)) { + if (local_err != NULL) { + /* Inability to parse or open is a failure; file not existing at all is *not* a failure */ + if (local_err->code == G_IO_ERROR_NOT_FOUND) { + g_error_free (local_err); + ret = g_new0 (ShellParser, 1); + g_object_ref (file); + ret->file = file; + ret->filename = g_file_get_path (file); + return ret; + } else { + gchar *filename; + filename = g_file_get_path (file); + g_propagate_prefixed_error (error, local_err, "Unable to read '%s':", filename); + g_free (filename); + } + } + return NULL; + } + ret = shell_parser_new_from_string (file, filebuf, error); + g_free (filebuf); + return ret; +} + +ShellParser * +shell_parser_new_from_string (GFile *file, + gchar *filebuf, + GError **error) +{ + ShellParser *ret = NULL; + GError *local_err = NULL; + gchar *s; + + if (file == NULL || filebuf == NULL) + return NULL; + + ret = g_new0 (ShellParser, 1); + g_object_ref (file); + ret->file = file; + ret->filename = g_file_get_path (file); + + gboolean want_separator = FALSE; /* Do we expect the next entry to be a separator or comment? */ + s = filebuf; + while (*s != 0) { + g_debug ("Scanning string: ``%s''", s); + gboolean matched = FALSE; + GMatchInfo *match_info = NULL; + struct ShellEntry *entry = NULL; + + matched = g_regex_match (comment_regex, s, 0, &match_info); + if (matched) { + entry = g_new0 (struct ShellEntry, 1); + entry->type = SHELL_ENTRY_TYPE_COMMENT; + entry->string = g_match_info_fetch (match_info, 0); + ret->entry_list = g_list_prepend (ret->entry_list, entry); + s += strlen (entry->string); + g_debug ("Scanned comment: ``%s''", entry->string); + _g_match_info_clear (&match_info); + want_separator = FALSE; + continue; + } + _g_match_info_clear (&match_info); + + matched = g_regex_match (separator_regex, s, 0, &match_info); + if (matched) { + entry = g_new0 (struct ShellEntry, 1); + entry->type = SHELL_ENTRY_TYPE_SEPARATOR; + entry->string = g_match_info_fetch (match_info, 0); + ret->entry_list = g_list_prepend (ret->entry_list, entry); + s += strlen (entry->string); + g_debug ("Scanned separator: ``%s''", entry->string); + _g_match_info_clear (&match_info); + want_separator = FALSE; + continue; + } + _g_match_info_clear (&match_info); + + matched = g_regex_match (indent_regex, s, 0, &match_info); + if (matched) { + entry = g_new0 (struct ShellEntry, 1); + entry->type = SHELL_ENTRY_TYPE_INDENT; + entry->string = g_match_info_fetch (match_info, 0); + ret->entry_list = g_list_prepend (ret->entry_list, entry); + s += strlen (entry->string); + g_debug ("Scanned indent: ``%s''", entry->string); + _g_match_info_clear (&match_info); + continue; + } + _g_match_info_clear (&match_info); + + matched = g_regex_match (var_equals_regex, s, 0, &match_info); + if (matched) { + gchar *raw_value = NULL, *temp1 = NULL, *temp2 = NULL; + /* If we expect a separator and get an assignment instead, fail */ + if (want_separator) + goto no_match; + + entry = g_new0 (struct ShellEntry, 1); + entry->type = SHELL_ENTRY_TYPE_ASSIGNMENT; + entry->string = g_match_info_fetch (match_info, 0); + entry->variable = g_match_info_fetch (match_info, 1); + s += strlen (entry->string); + g_debug ("Scanned variable: ``%s''", entry->string); + _g_match_info_clear (&match_info); + want_separator = TRUE; + + while (*s != 0) { + g_debug ("Scanning string for values: ``%s''", s); + gboolean matched2 = FALSE; + + matched2 = g_regex_match (single_quoted_regex, s, 0, &match_info); + if (matched2) + goto append_value; + + _g_match_info_clear (&match_info); + + matched2 = g_regex_match (double_quoted_regex, s, 0, &match_info); + if (matched2) + goto append_value; + + _g_match_info_clear (&match_info); + + matched2 = g_regex_match (unquoted_regex, s, 0, &match_info); + if (matched2) + goto append_value; + + _g_match_info_clear (&match_info); + + break; + + append_value: + if (raw_value == NULL) { + raw_value = g_match_info_fetch (match_info, 0); + s += strlen (raw_value); + g_debug ("Scanned value: ``%s''", raw_value); + } else { + temp1 = raw_value; + temp2 = g_match_info_fetch (match_info, 0); + raw_value = g_strconcat (temp1, temp2, NULL); + s += strlen (temp2); + g_debug ("Scanned value: ``%s''", temp2); + g_free (temp1); + g_free (temp2); + } + _g_match_info_clear (&match_info); + } + + if (raw_value != NULL) { + entry->unquoted_value = g_shell_unquote (raw_value, &local_err); + g_debug ("Unquoted value: ``%s''", entry->unquoted_value); + temp1 = entry->string; + temp2 = raw_value; + entry->string = g_strconcat (temp1, temp2, NULL); + g_free (temp1); + g_free (temp2); + ret->entry_list = g_list_prepend (ret->entry_list, entry); + if (local_err != NULL) + goto no_match; + } + continue; + } + + no_match: + /* Nothing matches, parsing has failed! */ + _g_match_info_clear (&match_info); + if (local_err != NULL) + g_propagate_prefixed_error (error, local_err, "Unable to parse '%s':", ret->filename); + else + g_propagate_error (error, + g_error_new (G_FILE_ERROR, G_FILE_ERROR_FAILED, + "Unable to parse '%s'", ret->filename)); + shell_parser_free (ret); + return NULL; + } + + ret->entry_list = g_list_reverse (ret->entry_list); + return ret; +} + +gboolean +shell_parser_is_empty (ShellParser *parser) +{ + if (parser == NULL || parser->entry_list == NULL) + return TRUE; + return FALSE; +} + +gboolean +shell_parser_set_variable (ShellParser *parser, + const gchar *variable, + const gchar *value, + gboolean add_if_unset) +{ + GList *curr = NULL; + struct ShellEntry *found_entry = NULL; + gchar *quoted_value = NULL; + gboolean ret = FALSE; + + g_assert (parser != NULL); + g_assert (variable != NULL); + + for (curr = parser->entry_list; curr != NULL; curr = curr->next) { + struct ShellEntry *entry; + + entry = (struct ShellEntry *)(curr->data); + if (entry->type == SHELL_ENTRY_TYPE_ASSIGNMENT && g_strcmp0 (variable, entry->variable) == 0) + found_entry = entry; + } + + quoted_value = g_shell_quote (value); + + if (found_entry != NULL) { + g_free (found_entry->string); + found_entry->string = g_strdup_printf ("%s=%s", variable, quoted_value); + ret = TRUE; + } else { + if (add_if_unset) { + found_entry = g_new0 (struct ShellEntry, 1); + found_entry->type = SHELL_ENTRY_TYPE_ASSIGNMENT; + found_entry->variable = g_strdup (variable); + found_entry->string = g_strdup_printf ("%s=%s", variable, quoted_value); + parser->entry_list = g_list_append (parser->entry_list, found_entry); + ret = TRUE; + } + } + + g_free (quoted_value); + return ret; +} + +void +shell_parser_clear_variable (ShellParser *parser, + const gchar *variable) +{ + GList *curr = NULL; + gboolean ret = FALSE; + + g_assert (parser != NULL); + g_assert (variable != NULL); + + for (curr = parser->entry_list; curr != NULL; ) { + struct ShellEntry *entry; + + entry = (struct ShellEntry *)(curr->data); + if (entry->type == SHELL_ENTRY_TYPE_ASSIGNMENT && g_strcmp0 (variable, entry->variable) == 0) { + GList *prev, *next; + + prev = curr->prev; + next = curr->next; + curr->prev = NULL; + curr->next = NULL; + g_list_free_full (curr, (GDestroyNotify)shell_entry_free); + if (prev != NULL) + prev->next = next; + if (next != NULL) + next->prev = prev; + curr = next; + } else + curr = curr->next; + } +} + +gboolean +shell_parser_save (ShellParser *parser, + GError **error) +{ + gboolean ret = FALSE; + GList *curr = NULL; + GFileOutputStream *os; + + g_assert (parser != NULL && parser->file != NULL && parser->filename != NULL); + if ((os = g_file_replace (parser->file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error)) == NULL) { + g_prefix_error (error, "Unable to save '%s': ", parser->filename); + goto out; + } + + for (curr = parser->entry_list; curr != NULL; curr = curr->next) { + struct ShellEntry *entry; + gsize written; + + entry = (struct ShellEntry *)(curr->data); + if (!g_output_stream_write_all (G_OUTPUT_STREAM (os), entry->string, strlen (entry->string), &written, NULL, error)) { + g_prefix_error (error, "Unable to save '%s': ", parser->filename); + goto out; + } + } + + if (!g_output_stream_close (G_OUTPUT_STREAM (os), NULL, error)) { + g_prefix_error (error, "Unable to save '%s': ", parser->filename); + g_output_stream_close (G_OUTPUT_STREAM (os), NULL, NULL); + goto out; + } + ret = TRUE; + + out: + if (os) + g_object_unref (os); + return ret; +} + +gboolean +shell_parser_set_and_save (GFile *file, + GError **error, + const gchar *first_var_name, + const gchar *first_alt_var_name, + const gchar *first_value, + ...) +{ + va_list ap; + ShellParser *parser; + gboolean ret = FALSE; + const gchar *var_name, *alt_var_name, *value; + + va_start (ap, first_value); + if ((parser = shell_parser_new (file, error)) == NULL) + goto out; + + var_name = first_var_name; + alt_var_name = first_alt_var_name; + value = first_value; + do { + if (alt_var_name == NULL) { + if (!shell_parser_set_variable (parser, var_name, value, TRUE)) { + g_propagate_error (error, + g_error_new (G_FILE_ERROR, G_FILE_ERROR_FAILED, + "Unable to set %s in '%s'", var_name, parser->filename)); + goto out; + } + } else { + if (!shell_parser_set_variable (parser, var_name, value, FALSE) && + !shell_parser_set_variable (parser, alt_var_name, value, FALSE) && + !shell_parser_set_variable (parser, var_name, value, TRUE)) { + g_propagate_error (error, + g_error_new (G_FILE_ERROR, G_FILE_ERROR_FAILED, + "Unable to set %s or %s in '%s'", var_name, alt_var_name, parser->filename)); + goto out; + } + } + } while ((var_name = va_arg (ap, const gchar*)) != NULL ? + alt_var_name = va_arg (ap, const gchar*), value = va_arg (ap, const gchar*), 1 : 0); + + if (!shell_parser_save (parser, error)) + goto out; + + ret = TRUE; + + out: + va_end (ap); + if (parser != NULL) + shell_parser_free (parser); + return ret; +} + +gchar ** +shell_parser_source_var_list (GFile *file, + const gchar * const *var_names, + GError **error) +{ + ShellParser *parser; + gchar **ret = NULL, **value; + const gchar* const* var_name; + + if (var_names == NULL) + return NULL; + + if ((parser = shell_parser_new (file, error)) == NULL) + return NULL; + + ret = g_new0 (gchar *, g_strv_length ((gchar **)var_names) + 1); + for (var_name = var_names, value = ret; *var_name != NULL; var_name++, value++) { + GList *curr; + for (curr = parser->entry_list; curr != NULL; curr = curr->next) { + struct ShellEntry *entry; + + entry = (struct ShellEntry *)(curr->data); + if (entry->type == SHELL_ENTRY_TYPE_ASSIGNMENT && g_strcmp0 (*var_name, entry->variable) == 0) + *value = g_strdup (entry->unquoted_value); + } + } + shell_parser_free (parser); + return ret; +} + +void +utils_destroy (void) +{ + if (indent_regex != NULL) { + g_regex_unref (indent_regex); + indent_regex = NULL; + } + if (comment_regex != NULL) { + g_regex_unref (comment_regex); + comment_regex = NULL; + } + if (separator_regex != NULL) { + g_regex_unref (separator_regex); + separator_regex = NULL; + } + if (var_equals_regex != NULL) { + g_regex_unref (var_equals_regex); + var_equals_regex = NULL; + } + if (single_quoted_regex != NULL) { + g_regex_unref (single_quoted_regex); + single_quoted_regex = NULL; + } + if (double_quoted_regex != NULL) { + g_regex_unref (double_quoted_regex); + double_quoted_regex = NULL; + } + if (unquoted_regex != NULL) { + g_regex_unref (unquoted_regex); + unquoted_regex = NULL; + } +} + +void +utils_init (void) +{ + if (indent_regex == NULL) { + indent_regex = g_regex_new ("^[ \\t]+", G_REGEX_ANCHORED, 0, NULL); + g_assert (indent_regex != NULL); + } + if (comment_regex == NULL) { + comment_regex = g_regex_new ("^#[^\\n]*\\n", G_REGEX_ANCHORED|G_REGEX_MULTILINE, 0, NULL); + g_assert (comment_regex != NULL); + } + if (separator_regex == NULL) { + separator_regex = g_regex_new ("^[ \\t;\\n\\r]*[;\\n][ \\t;\\n\\r]*", G_REGEX_ANCHORED|G_REGEX_MULTILINE, 0, NULL); + g_assert (separator_regex != NULL); + } + if (var_equals_regex == NULL) { + var_equals_regex = g_regex_new ("^([a-zA-Z_][a-zA-Z0-9_]*)(?:(?:\\\\\\n)*)=(?:(?:\\\\\\n)*)", G_REGEX_ANCHORED|G_REGEX_MULTILINE, 0, NULL); + g_assert (var_equals_regex != NULL); + } + if (single_quoted_regex == NULL) { + single_quoted_regex = g_regex_new ("^'[^']*'", G_REGEX_ANCHORED|G_REGEX_MULTILINE, 0, NULL); + g_assert (single_quoted_regex != NULL); + } + /* We do not want to allow $(...) or `...` constructs in double-quoted + * strings because they might have side effects, but ${...} is OK */ + if (double_quoted_regex == NULL) { + double_quoted_regex = g_regex_new ("^\"(?:[^\"`\\$]|\\\\[\"`\\$]|\\$\\{)*\"", G_REGEX_ANCHORED|G_REGEX_MULTILINE, 0, NULL); + g_assert (double_quoted_regex != NULL); + } + if (unquoted_regex == NULL) { + unquoted_regex = g_regex_new ("^(?:[^\\s\"'`\\$\\|&<>;]|\\\\[\\s\"'`\\$\\|&<>;]|\\$\\{)+", G_REGEX_ANCHORED|G_REGEX_MULTILINE, 0, NULL); + g_assert (unquoted_regex != NULL); + } +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..bf03d3e --- /dev/null +++ b/src/utils.h @@ -0,0 +1,102 @@ +/* + Copyright 2012 Alexandre Rostovtsev + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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 +*/ + +#ifndef _SHELL_UTILS_H_ +#define _SHELL_UTILS_H_ + +#include +#include + +typedef struct _ShellParser ShellParser; + +struct _ShellParser +{ + GFile *file; + gchar *filename; + GList *entry_list; +}; + +/* Always return TRUE */ +gboolean +_g_match_info_clear (GMatchInfo **match_info); + +void +check_polkit_async (const gchar *unique_name, + const gchar *action_id, + const gboolean user_interaction, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean +check_polkit_finish (GAsyncResult *res, + GError **error); + +gchar * +shell_source_var (GFile *file, + const gchar *variable, + GError **error); + +ShellParser * +shell_parser_new (GFile *file, + GError **error); + +ShellParser * +shell_parser_new_from_string (GFile *file, + gchar *filebuf, + GError **error); + +void +shell_parser_free (ShellParser *parser); + +gboolean +shell_parser_is_empty (ShellParser *parser); + +gboolean +shell_parser_set_variable (ShellParser *parser, + const gchar *variable, + const gchar *value, + gboolean add_if_unset); + +void +shell_parser_clear_variable (ShellParser *parser, + const gchar *variable); + +gboolean +shell_parser_save (ShellParser *parser, + GError **error); + +gboolean +shell_parser_set_and_save (GFile *file, + GError **error, + const gchar *first_var_name, + const gchar *first_alt_var_name, + const gchar *first_value, + ...); + +gchar ** +shell_parser_source_var_list (GFile *file, + const gchar * const *var_names, + GError **error); + +void +utils_init (void); + +void +utils_destroy (void); + +#endif -- cgit v1.2.3-65-gdbad