diff --git a/Makefile-libostree-defines.am b/Makefile-libostree-defines.am index 77748a41..06035157 100644 --- a/Makefile-libostree-defines.am +++ b/Makefile-libostree-defines.am @@ -45,6 +45,7 @@ libostree_public_headers = \ src/libostree/ostree-repo-finder-config.h \ src/libostree/ostree-repo-finder-mount.h \ src/libostree/ostree-repo-finder-override.h \ + src/libostree/ostree-kernel-args.h \ $(NULL) # This one is generated via configure.ac, and the gtk-doc diff --git a/Makefile-libostree.am b/Makefile-libostree.am index dbc9ebb8..0ea54cf8 100644 --- a/Makefile-libostree.am +++ b/Makefile-libostree.am @@ -21,8 +21,6 @@ include Makefile-libostree-defines.am -noinst_LTLIBRARIES += libostree-kernel-args.la - if ENABLE_RUST bupsplitpath = @abs_top_builddir@/target/@RUST_TARGET_SUBDIR@/libbupsplit_rs.a @@ -36,13 +34,6 @@ noinst_LTLIBRARIES += libbupsplit.la libbupsplit_la_SOURCES = src/libostree/bupsplit.h src/libostree/bupsplit.c endif # ENABLE_RUST -libostree_kernel_args_la_SOURCES = \ - src/libostree/ostree-kernel-args.h \ - src/libostree/ostree-kernel-args.c \ - $(NULL) -libostree_kernel_args_la_CFLAGS = -I$(srcdir)/libglnx $(OT_INTERNAL_GIO_UNIX_CFLAGS) -libostree_kernel_args_la_LIBADD = $(OT_INTERNAL_GIO_UNIX_LIBS) - lib_LTLIBRARIES += libostree-1.la libostreeincludedir = $(includedir)/ostree-1 @@ -147,6 +138,8 @@ libostree_1_la_SOURCES = \ src/libostree/ostree-repo-finder-config.c \ src/libostree/ostree-repo-finder-mount.c \ src/libostree/ostree-repo-finder-override.c \ + src/libostree/ostree-kernel-args.h \ + src/libostree/ostree-kernel-args.c \ $(NULL) if USE_LIBARCHIVE libostree_1_la_SOURCES += src/libostree/ostree-libarchive-input-stream.h \ @@ -191,7 +184,7 @@ libostree_1_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/bsdiff -I$(srcdir)/libglnx -I$( $(OT_INTERNAL_GIO_UNIX_CFLAGS) $(OT_INTERNAL_GPGME_CFLAGS) $(OT_DEP_LZMA_CFLAGS) $(OT_DEP_ZLIB_CFLAGS) $(OT_DEP_CRYPTO_CFLAGS) \ -fvisibility=hidden '-D_OSTREE_PUBLIC=__attribute__((visibility("default"))) extern' libostree_1_la_LDFLAGS = -version-number 1:0:0 -Bsymbolic-functions $(addprefix $(wl_versionscript_arg),$(symbol_files)) -libostree_1_la_LIBADD = libotutil.la libglnx.la libbsdiff.la libostree-kernel-args.la $(OT_INTERNAL_GIO_UNIX_LIBS) $(OT_INTERNAL_GPGME_LIBS) \ +libostree_1_la_LIBADD = libotutil.la libglnx.la libbsdiff.la $(OT_INTERNAL_GIO_UNIX_LIBS) $(OT_INTERNAL_GPGME_LIBS) \ $(OT_DEP_LZMA_LIBS) $(OT_DEP_ZLIB_LIBS) $(OT_DEP_CRYPTO_LIBS) # Some change between rust-1.21.0-1.fc27 and rust-1.22.1-1.fc27.x86_64 if ENABLE_RUST diff --git a/Makefile-ostree.am b/Makefile-ostree.am index 8d352e38..92881a31 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -125,7 +125,7 @@ ostree_bin_shared_ldadd = $(AM_LDFLAGS) libglnx.la libotutil.la libostree-1.la \ $(OT_INTERNAL_GIO_UNIX_LIBS) ostree_CFLAGS = $(ostree_bin_shared_cflags) -ostree_LDADD = $(ostree_bin_shared_ldadd) libbsdiff.la libostree-kernel-args.la $(LIBSYSTEMD_LIBS) +ostree_LDADD = $(ostree_bin_shared_ldadd) libbsdiff.la $(LIBSYSTEMD_LIBS) if USE_CURL_OR_SOUP diff --git a/Makefile-tests.am b/Makefile-tests.am index 2c0916f6..ac59b94b 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -246,7 +246,7 @@ endif _installed_or_uninstalled_test_programs = tests/test-varint tests/test-ot-unix-utils tests/test-bsdiff tests/test-mutable-tree \ tests/test-keyfile-utils tests/test-ot-opt-utils tests/test-ot-tool-util \ tests/test-gpg-verify-result tests/test-checksum tests/test-lzma tests/test-rollsum \ - tests/test-basic-c tests/test-sysroot-c tests/test-pull-c tests/test-repo tests/test-include-ostree-h + tests/test-basic-c tests/test-sysroot-c tests/test-pull-c tests/test-repo tests/test-include-ostree-h tests/test-kargs if USE_AVAHI test_programs += tests/test-repo-finder-avahi @@ -295,6 +295,10 @@ tests_test_repo_finder_avahi_CFLAGS = $(TESTS_CFLAGS) tests_test_repo_finder_avahi_LDADD = $(TESTS_LDADD) endif +tests_test_kargs_SOURCES = src/libostree/ostree-kernel-args.c tests/test-kargs.c +tests_test_kargs_CFLAGS = $(TESTS_CFLAGS) +tests_test_kargs_LDADD = $(TESTS_LDADD) + tests_test_repo_finder_config_SOURCES = tests/test-repo-finder-config.c tests_test_repo_finder_config_CFLAGS = $(TESTS_CFLAGS) tests_test_repo_finder_config_LDADD = $(TESTS_LDADD) diff --git a/apidoc/Makefile.am b/apidoc/Makefile.am index d64ce451..98a84d5f 100644 --- a/apidoc/Makefile.am +++ b/apidoc/Makefile.am @@ -78,7 +78,6 @@ IGNORE_HFILES= \ ostree-fetcher.h \ ostree-gpg-verifier.h \ ostree-gpg-verify-result-private.h \ - ostree-kernel-args.h \ ostree-libarchive-input-stream.h \ ostree-lzma-compressor.h \ ostree-lzma-decompressor.h \ diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index 7a59b5a7..3838be4c 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -669,3 +669,25 @@ ostree_repo_set_collection_ref_immediate ostree_repo_transaction_set_collection_ref ostree_repo_resolve_collection_ref + +
+ostree-kernel-args +OstreeKernelArgs +ostree_kernel_args_free +ostree_kernel_args_new +ostree_kernel_args_cleanup +ostree_kernel_args_replace_take +ostree_kernel_args_replace +ostree_kernel_args_replace_argv +ostree_kernel_args_append +ostree_kernel_args_append_argv +ostree_kernel_args_append_argv_filtered +ostree_kernel_args_new_replace +ostree_kernel_args_delete +ostree_kernel_args_delete_key_entry +ostree_kernel_args_append_proc_cmdline +ostree_kernel_args_parse_append +ostree_kernel_args_from_string +ostree_kernel_args_to_strv +ostree_kernel_args_to_string +
diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index cdbdbb32..125d9457 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -21,6 +21,23 @@ LIBOSTREE_2019.3 { global: ostree_repo_write_archive_to_mtree_from_fd; + ostree_kernel_args_free; + ostree_kernel_args_new; + ostree_kernel_args_cleanup; + ostree_kernel_args_replace_take; + ostree_kernel_args_replace; + ostree_kernel_args_replace_argv; + ostree_kernel_args_append; + ostree_kernel_args_append_argv; + ostree_kernel_args_append_argv_filtered; + ostree_kernel_args_new_replace; + ostree_kernel_args_delete; + ostree_kernel_args_delete_key_entry; + ostree_kernel_args_append_proc_cmdline; + ostree_kernel_args_parse_append; + ostree_kernel_args_from_string; + ostree_kernel_args_to_strv; + ostree_kernel_args_to_string; } LIBOSTREE_2018.9; /* Stub section for the stable release *after* this development one; don't diff --git a/src/libostree/ostree-bootloader-uboot.c b/src/libostree/ostree-bootloader-uboot.c index 87a93806..4cd955d5 100644 --- a/src/libostree/ostree-bootloader-uboot.c +++ b/src/libostree/ostree-bootloader-uboot.c @@ -78,8 +78,8 @@ append_system_uenv (OstreeBootloaderUboot *self, const char *uenv_path = NULL; const char *ostree_arg = NULL; - kargs = _ostree_kernel_args_from_string (bootargs); - ostree_arg = _ostree_kernel_args_get_last_value (kargs, "ostree"); + kargs = ostree_kernel_args_from_string (bootargs); + ostree_arg = ostree_kernel_args_get_last_value (kargs, "ostree"); if (!ostree_arg) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, diff --git a/src/libostree/ostree-kernel-args.c b/src/libostree/ostree-kernel-args.c index ac4014fa..dbf2ec8a 100644 --- a/src/libostree/ostree-kernel-args.c +++ b/src/libostree/ostree-kernel-args.c @@ -21,6 +21,7 @@ #include "ostree-kernel-args.h" #include "libglnx.h" +#include "otutil.h" #include @@ -60,8 +61,24 @@ _arg_has_prefix (const char *arg, return FALSE; } +static gboolean +strcmp0_equal (gconstpointer v1, + gconstpointer v2) +{ + return g_strcmp0 (v1, v2) == 0; +} + +/** + * ostree_kernel_args_new: (skip) + * + * Initializes a new OstreeKernelArgs structure and returns it + * + * Returns: (transfer full): A newly created #OstreeKernelArgs for kernel arguments + * + * Since: 2019.3 + **/ OstreeKernelArgs * -_ostree_kernel_args_new (void) +ostree_kernel_args_new (void) { OstreeKernelArgs *ret; ret = g_new0 (OstreeKernelArgs, 1); @@ -71,8 +88,16 @@ _ostree_kernel_args_new (void) return ret; } +/** + * ostree_kernel_args_free: + * @kargs: An OstreeKernelArgs that represents kernel arguments + * + * Frees the kargs structure + * + * Since: 2019.3 + **/ void -_ostree_kernel_args_free (OstreeKernelArgs *kargs) +ostree_kernel_args_free (OstreeKernelArgs *kargs) { if (!kargs) return; @@ -81,9 +106,238 @@ _ostree_kernel_args_free (OstreeKernelArgs *kargs) g_free (kargs); } +/** + * ostree_kernel_args_cleanup: + * @loc: Address of an OstreeKernelArgs pointer + * + * Frees the OstreeKernelArgs structure pointed by *loc + * + * Since: 2019.3 + **/ void -_ostree_kernel_args_replace_take (OstreeKernelArgs *kargs, - char *arg) +ostree_kernel_args_cleanup (void *loc) +{ + ostree_kernel_args_free (*((OstreeKernelArgs**)loc)); +} + +/** + * _ostree_kernel_arg_get_kargs_table: + * @kargs: An OstreeKernelArgs that represents kernel arguments + * + * Returns: (transfer none): #GHashTable that associates with the @kargs + * + * Note: this function is private for now, since the data structures underneath might be changed + * + * Since: 2019.3 + **/ +GHashTable* +_ostree_kernel_arg_get_kargs_table (OstreeKernelArgs *kargs) +{ + if (kargs != NULL) + return kargs->table; + return NULL; +} + +/** + * _ostree_kernel_arg_get_key_array: + * @kargs: An OstreeKernelArgs that represents kernel arguments + * + * Returns: (transfer none) (element-type utf8): #GPtrArray that associates with @kargs + * + * Note: this function is private for now, since the data structures underneath might be changed + * + * Since: 2019.3 + **/ +GPtrArray* +_ostree_kernel_arg_get_key_array (OstreeKernelArgs *kargs) +{ + if (kargs != NULL) + return kargs->order; + return NULL; +} + +/** + * ostree_kernel_args_new_replace: + * @kargs: OstreeKernelArgs instance + * @arg: a string argument + * @error: error instance + * + * This function implements the basic logic behind key/value pair + * replacement. Do note that the arg need to be properly formatted + * + * When replacing key with exact one value, the arg can be in + * the form: + * key, key=new_val, or key=old_val=new_val + * The first one swaps the old_val with the key to an empty value + * The second and third replace the old_val into the new_val + * + * When replacing key with multiple values, the arg can only be + * in the form of: + * key=old_val=new_val. Unless there is a special case where + * there is an empty value associated with the key, then + * key=new_val will work because old_val is empty. The empty + * val will be swapped with the new_val in that case + * + * Returns: %TRUE on success, %FALSE on failure (and in some other instances such as: + * 1. key not found in @kargs + * 2. old value not found when @arg is in the form of key=old_val=new_val + * 3. multiple old values found when @arg is in the form of key=old_val) + * + * Since: 2019.3 + **/ +gboolean +ostree_kernel_args_new_replace (OstreeKernelArgs *kargs, + const char *arg, + GError **error) +{ + g_autofree char *arg_owned = g_strdup (arg); + const char *key = arg_owned; + const char *val = split_keyeq (arg_owned); + + GPtrArray *values = g_hash_table_lookup (kargs->table, key); + if (!values) + return glnx_throw (error, "No key '%s' found", key); + g_assert_cmpuint (values->len, >, 0); + + /* first handle the case where the user just wants to replace an old value */ + if (val && strchr (val, '=')) + { + g_autofree char *old_val = g_strdup (val); + const char *new_val = split_keyeq (old_val); + g_assert (new_val); + + guint i = 0; + if (!ot_ptr_array_find_with_equal_func (values, old_val, strcmp0_equal, &i)) + return glnx_throw (error, "No karg '%s=%s' found", key, old_val); + + g_clear_pointer (&values->pdata[i], g_free); + values->pdata[i] = g_strdup (new_val); + return TRUE; + } + + /* can't know which val to replace without the old_val=new_val syntax */ + if (values->len > 1) + return glnx_throw (error, "Multiple values for key '%s' found", key); + + g_clear_pointer (&values->pdata[0], g_free); + values->pdata[0] = g_strdup (val); + return TRUE; +} + +/** + * ostree_kernel_args_delete_key_entry + * @kargs: an OstreeKernelArgs instance + * @key: the key to remove + * @error: an GError instance + * + * This function removes the key entry from the hashtable + * as well from the order pointer array inside kargs + * + * Note: since both table and order inside kernel args + * are with free function, no extra free functions are + * being called as they are done automatically by GLib + * + * Returns: %TRUE on success, %FALSE on failure + * + * Since: 2019.3 + **/ +gboolean +ostree_kernel_args_delete_key_entry (OstreeKernelArgs *kargs, + const char *key, + GError **error) +{ + if (!g_hash_table_remove (kargs->table, key)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Failed to find kernel argument '%s'", + key); + return FALSE; + } + + /* Then remove the key from order table */ + guint key_index; + g_assert (ot_ptr_array_find_with_equal_func (kargs->order, key, g_str_equal, &key_index)); + g_assert (g_ptr_array_remove_index (kargs->order, key_index)); + return TRUE; +} + +/** + * ostree_kernel_args_delete: + * @kargs: a OstreeKernelArgs instance + * @arg: key or key/value pair for deletion + * @error: an GError instance + * + * There are few scenarios being handled for deletion: + * + * 1: for input arg with a single key(i.e without = for split), + * the key/value pair will be deleted if there is only + * one value that is associated with the key + * + * 2: for input arg wth key/value pair, the specific key + * value pair will be deleted from the pointer array + * if those exist. + * + * 3: If the found key has only one value + * associated with it, the key entry in the table will also + * be removed, and the key will be removed from order table + * + * Returns: %TRUE on success, %FALSE on failure + * + * Since: 2019.3 + **/ +gboolean +ostree_kernel_args_delete (OstreeKernelArgs *kargs, + const char *arg, + GError **error) +{ + g_autofree char *arg_owned = g_strdup (arg); + const char *key = arg_owned; + const char *val = split_keyeq (arg_owned); + + GPtrArray *values = g_hash_table_lookup (kargs->table, key); + if (!values) + return glnx_throw (error, "No key '%s' found", key); + g_assert_cmpuint (values->len, >, 0); + + /* special-case: we allow deleting by key only if there's only one val */ + if (values->len == 1) + { + /* but if a specific val was passed, check that it's the same */ + if (val && !strcmp0_equal (val, values->pdata[0])) + return glnx_throw (error, "No karg '%s=%s' found", key, val); + return ostree_kernel_args_delete_key_entry (kargs, key, error); + } + + /* note val might be NULL here, in which case we're looking for `key`, not `key=` or + * `key=val` */ + guint i = 0; + if (!ot_ptr_array_find_with_equal_func (values, val, strcmp0_equal, &i)) + { + if (!val) + /* didn't find NULL -> only key= key=val1 key=val2 style things left, so the user + * needs to be more specific */ + return glnx_throw (error, "Multiple values for key '%s' found", arg); + return glnx_throw (error, "No karg '%s' found", arg); + } + + g_ptr_array_remove_index (values, i); + return TRUE; +} + +/** + * ostree_kernel_args_replace_take: + * @kargs: a OstreeKernelArgs instance + * @arg: (transfer full): key or key/value pair for replacement + * + * Finds and replaces the old key if @arg is already in the hash table, + * otherwise adds @arg as new key and split_keyeq (arg) as value. + * Note that when replacing old key, the old values are freed. + * + * Since: 2019.3 + **/ +void +ostree_kernel_args_replace_take (OstreeKernelArgs *kargs, + char *arg) { gboolean existed; GPtrArray *values = g_ptr_array_new_with_free_func (g_free); @@ -105,16 +359,38 @@ _ostree_kernel_args_replace_take (OstreeKernelArgs *kargs, } } +/** + * ostree_kernel_args_replace: + * @kargs: a OstreeKernelArgs instance + * @arg: key or key/value pair for replacement + * + * Finds and replaces the old key if @arg is already in the hash table, + * otherwise adds @arg as new key and split_keyeq (arg) as value. + * Note that when replacing old key value pair, the old values are freed. + * + * Since: 2019.3 + **/ void -_ostree_kernel_args_replace (OstreeKernelArgs *kargs, - const char *arg) +ostree_kernel_args_replace (OstreeKernelArgs *kargs, + const char *arg) { - _ostree_kernel_args_replace_take (kargs, g_strdup (arg)); + ostree_kernel_args_replace_take (kargs, g_strdup (arg)); } +/** + * ostree_kernel_args_append: + * @kargs: a OstreeKernelArgs instance + * @arg: key or key/value pair to be added + * + * Appends @arg which is in the form of key=value pair to the hash table kargs->table + * (appends to the value list if key is already in the hash table) + * and appends key to kargs->order if it is not in the hash table already. + * + * Since: 2019.3 + **/ void -_ostree_kernel_args_append (OstreeKernelArgs *kargs, - const char *arg) +ostree_kernel_args_append (OstreeKernelArgs *kargs, + const char *arg) { gboolean existed = TRUE; GPtrArray *values; @@ -141,23 +417,44 @@ _ostree_kernel_args_append (OstreeKernelArgs *kargs, } } +/** + * ostree_kernel_args_replace_argv: + * @kargs: a OstreeKernelArgs instance + * @argv: an array of key or key/value pairs + * + * Finds and replaces each non-null arguments of @argv in the hash table, + * otherwise adds individual arg as new key and split_keyeq (arg) as value. + * Note that when replacing old key value pair, the old values are freed. + * + * Since: 2019.3 + **/ void -_ostree_kernel_args_replace_argv (OstreeKernelArgs *kargs, - char **argv) +ostree_kernel_args_replace_argv (OstreeKernelArgs *kargs, + char **argv) { char **strviter; for (strviter = argv; strviter && *strviter; strviter++) { const char *arg = *strviter; - _ostree_kernel_args_replace (kargs, arg); + ostree_kernel_args_replace (kargs, arg); } } +/** + * ostree_kernel_args_append_argv_filtered: + * @kargs: a OstreeKernelArgs instance + * @argv: an array of key=value argument pairs + * @prefixes: an array of prefix strings + * + * Appends each argument that does not have one of the @prefixes as prefix to the @kargs + * + * Since: 2019.3 + **/ void -_ostree_kernel_args_append_argv_filtered (OstreeKernelArgs *kargs, - char **argv, - char **prefixes) +ostree_kernel_args_append_argv_filtered (OstreeKernelArgs *kargs, + char **argv, + char **prefixes) { char **strviter; @@ -166,21 +463,44 @@ _ostree_kernel_args_append_argv_filtered (OstreeKernelArgs *kargs, const char *arg = *strviter; if (!_arg_has_prefix (arg, prefixes)) - _ostree_kernel_args_append (kargs, arg); + ostree_kernel_args_append (kargs, arg); } } +/** + * ostree_kernel_args_append_argv: + * @kargs: a OstreeKernelArgs instance + * @argv: an array of key=value argument pairs + * + * Appends each value in @argv to the corresponding value array and + * appends key to kargs->order if it is not in the hash table already. + * + * Since: 2019.3 + **/ void -_ostree_kernel_args_append_argv (OstreeKernelArgs *kargs, - char **argv) +ostree_kernel_args_append_argv (OstreeKernelArgs *kargs, + char **argv) { - _ostree_kernel_args_append_argv_filtered (kargs, argv, NULL); + ostree_kernel_args_append_argv_filtered (kargs, argv, NULL); } +/** + * ostree_kernel_args_append_proc_cmdline: + * @kargs: a OstreeKernelArgs instance + * @cancellable: optional GCancellable object, NULL to ignore + * @error: an GError instance + * + * Appends the command line arguments in the file "/proc/cmdline" + * that does not have "BOOT_IMAGE=" and "initrd=" as prefixes to the @kargs + * + * Returns: %TRUE on success, %FALSE on failure + * + * Since: 2019.3 + **/ gboolean -_ostree_kernel_args_append_proc_cmdline (OstreeKernelArgs *kargs, - GCancellable *cancellable, - GError **error) +ostree_kernel_args_append_proc_cmdline (OstreeKernelArgs *kargs, + GCancellable *cancellable, + GError **error) { g_autoptr(GFile) proc_cmdline_path = g_file_new_for_path ("/proc/cmdline"); g_autofree char *proc_cmdline = NULL; @@ -202,44 +522,76 @@ _ostree_kernel_args_append_proc_cmdline (OstreeKernelArgs *kargs, g_strchomp (proc_cmdline); proc_cmdline_args = g_strsplit (proc_cmdline, " ", -1); - _ostree_kernel_args_append_argv_filtered (kargs, proc_cmdline_args, + ostree_kernel_args_append_argv_filtered (kargs, proc_cmdline_args, filtered_prefixes); return TRUE; } +/** + * ostree_kernel_args_parse_append: + * @kargs: a OstreeKernelArgs instance + * @options: a string representing command line arguments + * + * Parses @options by separating it by whitespaces and appends each argument to @kargs + * + * Since: 2019.3 + **/ void -_ostree_kernel_args_parse_append (OstreeKernelArgs *kargs, - const char *options) +ostree_kernel_args_parse_append (OstreeKernelArgs *kargs, + const char *options) { char **args = NULL; char **iter; if (!options) return; - + args = g_strsplit (options, " ", -1); for (iter = args; *iter; iter++) { char *arg = *iter; - _ostree_kernel_args_append (kargs, arg); + ostree_kernel_args_append (kargs, arg); } g_strfreev (args); } +/** + * ostree_kernel_args_from_string: (skip) + * @options: a string representing command line arguments + * + * Initializes a new OstreeKernelArgs then parses and appends @options + * to the empty OstreeKernelArgs + * + * Returns: (transfer full): newly allocated #OstreeKernelArgs with @options appended + * + * Since: 2019.3 + **/ OstreeKernelArgs * -_ostree_kernel_args_from_string (const char *options) +ostree_kernel_args_from_string (const char *options) { OstreeKernelArgs *ret; - ret = _ostree_kernel_args_new (); - _ostree_kernel_args_parse_append (ret, options); + ret = ostree_kernel_args_new (); + ostree_kernel_args_parse_append (ret, options); return ret; } +/** + * ostree_kernel_args_to_strv: + * @kargs: a OstreeKernelArgs instance + * + * Extracts all key value pairs in @kargs and appends to a temporary + * array in forms of "key=value" or "key" if value is NULL, and returns + * the temporary array with the GPtrArray wrapper freed + * + * Returns: (transfer full): an array of "key=value" pairs or "key" if value is NULL + * + * Since: 2019.3 + **/ char ** -_ostree_kernel_args_to_strv (OstreeKernelArgs *kargs) +ostree_kernel_args_to_strv (OstreeKernelArgs *kargs) { GPtrArray *strv = g_ptr_array_new (); guint i; @@ -266,8 +618,25 @@ _ostree_kernel_args_to_strv (OstreeKernelArgs *kargs) return (char**)g_ptr_array_free (strv, FALSE); } +/** + * ostree_kernel_args_to_string: + * @kargs: a OstreeKernelArgs instance + * + * Extracts all key value pairs in @kargs and appends to a temporary + * GString in forms of "key=value" or "key" if value is NULL separated + * by a single whitespace, and returns the temporary string with the + * GString wrapper freed + * + * Note: the application will be terminated if one of the values array + * in @kargs is NULL + * + * Returns: (transfer full): a string of "key=value" pairs or "key" if value is NULL, + * separated by single whitespaces + * + * Since: 2019.3 + **/ char * -_ostree_kernel_args_to_string (OstreeKernelArgs *kargs) +ostree_kernel_args_to_string (OstreeKernelArgs *kargs) { GString *buf = g_string_new (""); gboolean first = TRUE; @@ -302,8 +671,22 @@ _ostree_kernel_args_to_string (OstreeKernelArgs *kargs) return g_string_free (buf, FALSE); } +/** + * ostree_kernel_args_get_last_value: + * @kargs: a OstreeKernelArgs instance + * @key: a key to look for in @kargs hash table + * + * Finds and returns the last element of value array + * corresponding to the @key in @kargs hash table. Note that the application + * will be terminated if the @key is found but the value array is empty + * + * Returns: NULL if @key is not found in the @kargs hash table, + * otherwise returns last element of value array corresponding to @key + * + * Since: 2019.3 + **/ const char * -_ostree_kernel_args_get_last_value (OstreeKernelArgs *kargs, const char *key) +ostree_kernel_args_get_last_value (OstreeKernelArgs *kargs, const char *key) { GPtrArray *values = g_hash_table_lookup (kargs->table, key); diff --git a/src/libostree/ostree-kernel-args.h b/src/libostree/ostree-kernel-args.h index 0bc43704..d7beca51 100644 --- a/src/libostree/ostree-kernel-args.h +++ b/src/libostree/ostree-kernel-args.h @@ -19,41 +19,90 @@ #pragma once -#include "libglnx.h" +#include +#include +#include +#include "ostree-types.h" G_BEGIN_DECLS typedef struct _OstreeKernelArgs OstreeKernelArgs; -void _ostree_kernel_args_free (OstreeKernelArgs *kargs); -G_DEFINE_AUTOPTR_CLEANUP_FUNC(OstreeKernelArgs, _ostree_kernel_args_free); -OstreeKernelArgs *_ostree_kernel_args_new (void); -void _ostree_kernel_args_replace_take (OstreeKernelArgs *kargs, - char *key); -void _ostree_kernel_args_replace (OstreeKernelArgs *kargs, - const char *key); -void _ostree_kernel_args_replace_argv (OstreeKernelArgs *kargs, - char **argv); -void _ostree_kernel_args_append (OstreeKernelArgs *kargs, - const char *key); -void _ostree_kernel_args_append_argv (OstreeKernelArgs *kargs, - char **argv); -void _ostree_kernel_args_append_argv_filtered (OstreeKernelArgs *kargs, - char **argv, - char **prefixes); +GHashTable *_ostree_kernel_arg_get_kargs_table (OstreeKernelArgs *kargs); -gboolean _ostree_kernel_args_append_proc_cmdline (OstreeKernelArgs *kargs, - GCancellable *cancellable, - GError **error); +GPtrArray *_ostree_kernel_arg_get_key_array (OstreeKernelArgs *kargs); -void _ostree_kernel_args_parse_append (OstreeKernelArgs *kargs, - const char *options); +_OSTREE_PUBLIC +void ostree_kernel_args_free (OstreeKernelArgs *kargs); -const char *_ostree_kernel_args_get_last_value (OstreeKernelArgs *kargs, const char *key); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(OstreeKernelArgs, ostree_kernel_args_free) -OstreeKernelArgs * _ostree_kernel_args_from_string (const char *options); +_OSTREE_PUBLIC +OstreeKernelArgs *ostree_kernel_args_new (void); -char ** _ostree_kernel_args_to_strv (OstreeKernelArgs *kargs); -char * _ostree_kernel_args_to_string (OstreeKernelArgs *kargs); +_OSTREE_PUBLIC +void ostree_kernel_args_cleanup (void *loc); + +_OSTREE_PUBLIC +void ostree_kernel_args_replace_take (OstreeKernelArgs *kargs, + char *arg); + +_OSTREE_PUBLIC +void ostree_kernel_args_replace (OstreeKernelArgs *kargs, + const char *arg); + +_OSTREE_PUBLIC +void ostree_kernel_args_replace_argv (OstreeKernelArgs *kargs, + char **argv); + +_OSTREE_PUBLIC +void ostree_kernel_args_append (OstreeKernelArgs *kargs, + const char *arg); + +_OSTREE_PUBLIC +void ostree_kernel_args_append_argv (OstreeKernelArgs *kargs, + char **argv); + +_OSTREE_PUBLIC +void ostree_kernel_args_append_argv_filtered (OstreeKernelArgs *kargs, + char **argv, + char **prefixes); + +_OSTREE_PUBLIC +gboolean ostree_kernel_args_new_replace (OstreeKernelArgs *kargs, + const char *arg, + GError **error); + +_OSTREE_PUBLIC +gboolean ostree_kernel_args_delete (OstreeKernelArgs *kargs, + const char *arg, + GError **error); + +_OSTREE_PUBLIC +gboolean ostree_kernel_args_delete_key_entry (OstreeKernelArgs *kargs, + const char *key, + GError **error); + +_OSTREE_PUBLIC +gboolean ostree_kernel_args_append_proc_cmdline (OstreeKernelArgs *kargs, + GCancellable *cancellable, + GError **error); + +_OSTREE_PUBLIC +void ostree_kernel_args_parse_append (OstreeKernelArgs *kargs, + const char *options); + +_OSTREE_PUBLIC +const char *ostree_kernel_args_get_last_value (OstreeKernelArgs *kargs, + const char *key); + +_OSTREE_PUBLIC +OstreeKernelArgs *ostree_kernel_args_from_string (const char *options); + +_OSTREE_PUBLIC +char **ostree_kernel_args_to_strv (OstreeKernelArgs *kargs); + +_OSTREE_PUBLIC +char *ostree_kernel_args_to_string (OstreeKernelArgs *kargs); G_END_DECLS diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index 1096b0b0..d47f6438 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -1759,7 +1759,7 @@ install_deployment_kernel (OstreeSysroot *sysroot, ostree_bootconfig_parser_set (bootconfig, "linux", boot_relpath); val = ostree_bootconfig_parser_get (bootconfig, "options"); - g_autoptr(OstreeKernelArgs) kargs = _ostree_kernel_args_from_string (val); + g_autoptr(OstreeKernelArgs) kargs = ostree_kernel_args_from_string (val); if (kernel_layout->initramfs_namever) { @@ -1772,7 +1772,7 @@ install_deployment_kernel (OstreeSysroot *sysroot, prepare_root_arg = g_strdup_printf ("init=/ostree/boot.%d/%s/%s/%d/usr/lib/ostree/ostree-prepare-root", new_bootversion, osname, bootcsum, ostree_deployment_get_bootserial (deployment)); - _ostree_kernel_args_replace_take (kargs, g_steal_pointer (&prepare_root_arg)); + ostree_kernel_args_replace_take (kargs, g_steal_pointer (&prepare_root_arg)); } if (kernel_layout->devicetree_namever) @@ -1785,9 +1785,9 @@ install_deployment_kernel (OstreeSysroot *sysroot, g_autofree char *ostree_kernel_arg = g_strdup_printf ("ostree=/ostree/boot.%d/%s/%s/%d", new_bootversion, osname, bootcsum, ostree_deployment_get_bootserial (deployment)); - _ostree_kernel_args_replace_take (kargs, g_steal_pointer (&ostree_kernel_arg)); + ostree_kernel_args_replace_take (kargs, g_steal_pointer (&ostree_kernel_arg)); - g_autofree char *options_key = _ostree_kernel_args_to_string (kargs); + g_autofree char *options_key = ostree_kernel_args_to_string (kargs); ostree_bootconfig_parser_set (bootconfig, "options", options_key); glnx_autofd int bootconf_dfd = -1; @@ -1895,9 +1895,9 @@ get_deployment_nonostree_kargs (OstreeDeployment *deployment) /* pick up kernel arguments but filter out ostree= */ OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (deployment); const char *boot_options = ostree_bootconfig_parser_get (bootconfig, "options"); - g_autoptr(OstreeKernelArgs) kargs = _ostree_kernel_args_from_string (boot_options); - _ostree_kernel_args_replace (kargs, "ostree"); - return _ostree_kernel_args_to_string (kargs); + g_autoptr(OstreeKernelArgs) kargs = ostree_kernel_args_from_string (boot_options); + ostree_kernel_args_replace (kargs, "ostree"); + return ostree_kernel_args_to_string (kargs); } static char* @@ -2463,9 +2463,9 @@ _ostree_deployment_set_bootconfig_from_kargs (OstreeDeployment *deployment, */ if (override_kernel_argv) { - g_autoptr(OstreeKernelArgs) kargs = _ostree_kernel_args_new (); - _ostree_kernel_args_append_argv (kargs, override_kernel_argv); - g_autofree char *new_options = _ostree_kernel_args_to_string (kargs); + g_autoptr(OstreeKernelArgs) kargs = ostree_kernel_args_new (); + ostree_kernel_args_append_argv (kargs, override_kernel_argv); + g_autofree char *new_options = ostree_kernel_args_to_string (kargs); ostree_bootconfig_parser_set (bootconfig, "options", new_options); } } @@ -3004,9 +3004,9 @@ ostree_sysroot_deployment_set_kargs (OstreeSysroot *self, g_autoptr(OstreeDeployment) new_deployment = ostree_deployment_clone (deployment); OstreeBootconfigParser *new_bootconfig = ostree_deployment_get_bootconfig (new_deployment); - g_autoptr(OstreeKernelArgs) kargs = _ostree_kernel_args_new (); - _ostree_kernel_args_append_argv (kargs, new_kargs); - g_autofree char *new_options = _ostree_kernel_args_to_string (kargs); + g_autoptr(OstreeKernelArgs) kargs = ostree_kernel_args_new (); + ostree_kernel_args_append_argv (kargs, new_kargs); + g_autofree char *new_options = ostree_kernel_args_to_string (kargs); ostree_bootconfig_parser_set (new_bootconfig, "options", new_options); g_autoptr(GPtrArray) new_deployments = g_ptr_array_new_with_free_func (g_object_unref); diff --git a/src/libostree/ostree-sysroot-private.h b/src/libostree/ostree-sysroot-private.h index e4b2039e..858673c5 100644 --- a/src/libostree/ostree-sysroot-private.h +++ b/src/libostree/ostree-sysroot-private.h @@ -23,7 +23,6 @@ #include "libglnx.h" #include "ostree.h" -#include "ostree-kernel-args.h" #include "ostree-bootloader.h" G_BEGIN_DECLS diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index 4862b7b4..b3ad2498 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -1688,12 +1688,12 @@ clone_deployment (OstreeSysroot *sysroot, /* Copy the bootloader config options */ OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (merge_deployment); g_auto(GStrv) previous_args = g_strsplit (ostree_bootconfig_parser_get (bootconfig, "options"), " ", -1); - g_autoptr(OstreeKernelArgs) kargs = _ostree_kernel_args_new (); - _ostree_kernel_args_append_argv (kargs, previous_args); + g_autoptr(OstreeKernelArgs) kargs = ostree_kernel_args_new (); + ostree_kernel_args_append_argv (kargs, previous_args); /* Deploy the copy */ g_autoptr(OstreeDeployment) new_deployment = NULL; - g_auto(GStrv) kargs_strv = _ostree_kernel_args_to_strv (kargs); + g_auto(GStrv) kargs_strv = ostree_kernel_args_to_strv (kargs); if (!ostree_sysroot_deploy_tree (sysroot, ostree_deployment_get_osname (target_deployment), ostree_deployment_get_csum (target_deployment), diff --git a/src/libostree/ostree.h b/src/libostree/ostree.h index cbeb99b2..ca19bc85 100644 --- a/src/libostree/ostree.h +++ b/src/libostree/ostree.h @@ -41,3 +41,4 @@ #include #include #include +#include diff --git a/src/libotutil/ot-tool-util.c b/src/libotutil/ot-tool-util.c index d6f37bc9..35e6a343 100644 --- a/src/libotutil/ot-tool-util.c +++ b/src/libotutil/ot-tool-util.c @@ -62,3 +62,55 @@ ot_parse_keyvalue (const char *keyvalue, *out_value = g_strdup (eq + 1); return TRUE; } + +/** + * Note: temporarily copied from GLib: https://github.com/GNOME/glib/blob/a419146578a42c760cff684292465b38df855f75/glib/garray.c#L1664 + * See documentation at: https://developer.gnome.org/glib/stable/glib-Pointer-Arrays.html#g-ptr-array-find-with-equal-func + * + * ot_ptr_array_find_with_equal_func: (skip) + * @haystack: pointer array to be searched + * @needle: pointer to look for + * @equal_func: (nullable): the function to call for each element, which should + * return %TRUE when the desired element is found; or %NULL to use pointer + * equality + * @index_: (optional) (out caller-allocates): return location for the index of + * the element, if found + * + * Checks whether @needle exists in @haystack, using the given @equal_func. + * If the element is found, %TRUE is returned and the element’s index is + * returned in @index_ (if non-%NULL). Otherwise, %FALSE is returned and @index_ + * is undefined. If @needle exists multiple times in @haystack, the index of + * the first instance is returned. + * + * @equal_func is called with the element from the array as its first parameter, + * and @needle as its second parameter. If @equal_func is %NULL, pointer + * equality is used. + * + * Returns: %TRUE if @needle is one of the elements of @haystack + * Since: 2.54 + */ +gboolean +ot_ptr_array_find_with_equal_func (GPtrArray *haystack, + gconstpointer needle, + GEqualFunc equal_func, + guint *index_) +{ + guint i; + + g_return_val_if_fail (haystack != NULL, FALSE); + + if (equal_func == NULL) + equal_func = g_direct_equal; + + for (i = 0; i < haystack->len; i++) + { + if (equal_func (g_ptr_array_index (haystack, i), needle)) + { + if (index_ != NULL) + *index_ = i; + return TRUE; + } + } + + return FALSE; +} diff --git a/src/libotutil/ot-tool-util.h b/src/libotutil/ot-tool-util.h index e3381730..70f7d55d 100644 --- a/src/libotutil/ot-tool-util.h +++ b/src/libotutil/ot-tool-util.h @@ -34,5 +34,10 @@ ot_parse_keyvalue (const char *keyvalue, char **out_key, char **out_value, GError **error); +gboolean +ot_ptr_array_find_with_equal_func (GPtrArray *haystack, + gconstpointer needle, + GEqualFunc equal_func, + guint *index_); G_END_DECLS diff --git a/src/libotutil/otutil.h b/src/libotutil/otutil.h index eced95f9..8f1bd4e7 100644 --- a/src/libotutil/otutil.h +++ b/src/libotutil/otutil.h @@ -62,3 +62,4 @@ #include #include #include +#include diff --git a/src/ostree/ot-admin-builtin-deploy.c b/src/ostree/ot-admin-builtin-deploy.c index 38ec923f..c1c3353d 100644 --- a/src/ostree/ot-admin-builtin-deploy.c +++ b/src/ostree/ot-admin-builtin-deploy.c @@ -29,8 +29,6 @@ #include "ostree.h" #include "otutil.h" -#include "../libostree/ostree-kernel-args.h" - #include static gboolean opt_retain; @@ -133,20 +131,20 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat g_autoptr(OstreeKernelArgs) kargs = NULL; if (opt_kernel_arg_none) { - kargs = _ostree_kernel_args_new (); + kargs = ostree_kernel_args_new (); } else if (opt_kernel_proc_cmdline) { - kargs = _ostree_kernel_args_new (); - if (!_ostree_kernel_args_append_proc_cmdline (kargs, cancellable, error)) + kargs = ostree_kernel_args_new (); + if (!ostree_kernel_args_append_proc_cmdline (kargs, cancellable, error)) return FALSE; } else if (merge_deployment && (opt_kernel_argv || opt_kernel_argv_append)) { OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (merge_deployment); g_auto(GStrv) previous_args = g_strsplit (ostree_bootconfig_parser_get (bootconfig, "options"), " ", -1); - kargs = _ostree_kernel_args_new (); - _ostree_kernel_args_append_argv (kargs, previous_args); + kargs = ostree_kernel_args_new (); + ostree_kernel_args_append_argv (kargs, previous_args); } /* Now replace/extend the above set. Note that if no options are specified, @@ -156,19 +154,19 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat if (opt_kernel_argv) { if (!kargs) - kargs = _ostree_kernel_args_new (); - _ostree_kernel_args_replace_argv (kargs, opt_kernel_argv); + kargs = ostree_kernel_args_new (); + ostree_kernel_args_replace_argv (kargs, opt_kernel_argv); } if (opt_kernel_argv_append) { if (!kargs) - kargs = _ostree_kernel_args_new (); - _ostree_kernel_args_append_argv (kargs, opt_kernel_argv_append); + kargs = ostree_kernel_args_new (); + ostree_kernel_args_append_argv (kargs, opt_kernel_argv_append); } g_autoptr(OstreeDeployment) new_deployment = NULL; - g_auto(GStrv) kargs_strv = kargs ? _ostree_kernel_args_to_strv (kargs) : NULL; + g_auto(GStrv) kargs_strv = kargs ? ostree_kernel_args_to_strv (kargs) : NULL; if (opt_stage) { if (opt_retain_pending || opt_retain_rollback) diff --git a/src/ostree/ot-admin-builtin-unlock.c b/src/ostree/ot-admin-builtin-unlock.c index f0efa44a..cd466183 100644 --- a/src/ostree/ot-admin-builtin-unlock.c +++ b/src/ostree/ot-admin-builtin-unlock.c @@ -27,8 +27,6 @@ #include "ostree.h" #include "otutil.h" -#include "../libostree/ostree-kernel-args.h" - #include #include diff --git a/src/ostree/ot-admin-instutil-builtin-set-kargs.c b/src/ostree/ot-admin-instutil-builtin-set-kargs.c index 666e5369..fb5c7d2d 100644 --- a/src/ostree/ot-admin-instutil-builtin-set-kargs.c +++ b/src/ostree/ot-admin-instutil-builtin-set-kargs.c @@ -26,8 +26,7 @@ #include "ot-admin-instutil-builtins.h" #include "otutil.h" - -#include "../libostree/ostree-kernel-args.h" +#include "ostree.h" static gboolean opt_proc_cmdline; static gboolean opt_merge; @@ -69,14 +68,14 @@ ot_admin_instutil_builtin_set_kargs (int argc, char **argv, OstreeCommandInvocat } first_deployment = deployments->pdata[0]; - kargs = _ostree_kernel_args_new (); + kargs = ostree_kernel_args_new (); /* If they want the current kernel's args, they very likely don't * want the ones from the merge. */ if (opt_proc_cmdline) { - if (!_ostree_kernel_args_append_proc_cmdline (kargs, cancellable, error)) + if (!ostree_kernel_args_append_proc_cmdline (kargs, cancellable, error)) goto out; } else if (opt_merge) @@ -84,24 +83,24 @@ ot_admin_instutil_builtin_set_kargs (int argc, char **argv, OstreeCommandInvocat OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (first_deployment); g_auto(GStrv) previous_args = g_strsplit (ostree_bootconfig_parser_get (bootconfig, "options"), " ", -1); - _ostree_kernel_args_append_argv (kargs, previous_args); + ostree_kernel_args_append_argv (kargs, previous_args); } if (opt_replace) { - _ostree_kernel_args_replace_argv (kargs, opt_replace); + ostree_kernel_args_replace_argv (kargs, opt_replace); } if (opt_append) { - _ostree_kernel_args_append_argv (kargs, opt_append); + ostree_kernel_args_append_argv (kargs, opt_append); } for (i = 1; i < argc; i++) - _ostree_kernel_args_append (kargs, argv[i]); + ostree_kernel_args_append (kargs, argv[i]); { - g_auto(GStrv) kargs_strv = _ostree_kernel_args_to_strv (kargs); + g_auto(GStrv) kargs_strv = ostree_kernel_args_to_strv (kargs); if (!ostree_sysroot_deployment_set_kargs (sysroot, first_deployment, kargs_strv, diff --git a/tests/.gitignore b/tests/.gitignore index 8f03c026..f5e95e49 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -22,3 +22,4 @@ test-repo-finder-avahi test-repo-finder-config test-repo-finder-mount test-rollsum-cli +test-kargs diff --git a/tests/test-kargs.c b/tests/test-kargs.c new file mode 100644 index 00000000..8d34f73c --- /dev/null +++ b/tests/test-kargs.c @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.0+ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "ostree-kernel-args.h" +#include "otutil.h" + +static gboolean +check_string_existance (OstreeKernelArgs *karg, + const char *string_to_find) +{ + g_autofree gchar* string_with_spaces = ostree_kernel_args_to_string (karg); + g_auto(GStrv) string_list = g_strsplit (string_with_spaces, " ", -1); + return g_strv_contains ((const char* const*) string_list, string_to_find); +} + +static void +test_kargs_delete (void) +{ + g_autoptr(GError) error = NULL; + gboolean ret; + __attribute__((cleanup(ostree_kernel_args_cleanup))) OstreeKernelArgs *karg = ostree_kernel_args_new (); + + ostree_kernel_args_append (karg, "single_key=test"); + ostree_kernel_args_append (karg, "test=firstval"); + ostree_kernel_args_append (karg, "test=secondval"); + ostree_kernel_args_append (karg, "test="); + ostree_kernel_args_append (karg, "test"); + + /* Delete a non-existant key should fail */ + ret = ostree_kernel_args_delete (karg, "non_existant_key", &error); + g_assert (!ret); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_clear_error (&error); + + /* Delete a key with multiple values when only specifying key should work if a no-value + * variant exists */ + ret = ostree_kernel_args_delete (karg, "test", &error); + g_assert_no_error (error); + g_assert (ret); + g_assert (!check_string_existance (karg, "test")); + + /* Trying again now should fail since there are only kargs with various values */ + ret = ostree_kernel_args_delete (karg, "test", &error); + g_assert (!ret); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_clear_error (&error); + + /* Delete a key with a non existant value should fail */ + ret = ostree_kernel_args_delete (karg, "test=non_existant_value", &error); + g_assert (!ret); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_clear_error (&error); + + /* Delete a key with only one value should fail if the value doesn't match */ + ret = ostree_kernel_args_delete (karg, "single_key=non_existent_value", &error); + g_assert (!ret); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_clear_error (&error); + + /* Delete a key with only one value should succeed by only specifying key */ + ret = ostree_kernel_args_delete (karg, "single_key", &error); + g_assert_no_error (error); + g_assert (ret); + /* verify the value array is properly updated */ + GPtrArray *kargs_array = _ostree_kernel_arg_get_key_array (karg); + g_assert (!ot_ptr_array_find_with_equal_func (kargs_array, "single_key", g_str_equal, NULL)); + g_assert (!check_string_existance (karg, "single_key")); + + /* Delete specific key/value pair */ + ret = ostree_kernel_args_delete (karg, "test=secondval", &error); + g_assert_no_error (error); + g_assert (ret); + g_assert (!check_string_existance (karg, "test=secondval")); + + /* Delete key/value pair with empty string value */ + ret = ostree_kernel_args_delete (karg, "test=", &error); + g_assert_no_error (error); + g_assert (ret); + g_assert (!check_string_existance (karg, "test=")); + + ret = ostree_kernel_args_delete (karg, "test=firstval", &error); + g_assert_no_error (error); + g_assert (ret); + g_assert (!check_string_existance (karg, "test=firstval")); + + /* Check that we can delete duplicate keys */ + ostree_kernel_args_append (karg, "test=foo"); + ostree_kernel_args_append (karg, "test=foo"); + check_string_existance (karg, "test=foo"); + ret = ostree_kernel_args_delete (karg, "test=foo", &error); + g_assert_no_error (error); + g_assert (ret); + g_assert (check_string_existance (karg, "test=foo")); + ret = ostree_kernel_args_delete (karg, "test=foo", &error); + g_assert_no_error (error); + g_assert (ret); + g_assert (!check_string_existance (karg, "test=foo")); + + /* Make sure we also gracefully do this for key-only args */ + ostree_kernel_args_append (karg, "nosmt"); + ostree_kernel_args_append (karg, "nosmt"); + check_string_existance (karg, "nosmt"); + ret = ostree_kernel_args_delete (karg, "nosmt", &error); + g_assert_no_error (error); + g_assert (ret); + g_assert (check_string_existance (karg, "nosmt")); + ret = ostree_kernel_args_delete (karg, "nosmt", &error); + g_assert_no_error (error); + g_assert (ret); + g_assert (!check_string_existance (karg, "nosmt")); +} + +static void +test_kargs_replace (void) +{ + g_autoptr(GError) error = NULL; + gboolean ret; + __attribute__((cleanup(ostree_kernel_args_cleanup))) OstreeKernelArgs *karg = ostree_kernel_args_new (); + + ostree_kernel_args_append (karg, "single_key"); + ostree_kernel_args_append (karg, "test=firstval"); + ostree_kernel_args_append (karg, "test=secondval"); + + /* Replace when the input key is non-existant should fail */ + ret = ostree_kernel_args_new_replace (karg, "nonexistantkey", &error); + g_assert (!ret); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_clear_error (&error); + + /* Replace non-existant value with input key=nonexistantvalue=newvalue should fail */ + ret = ostree_kernel_args_new_replace (karg, "single_key=nonexistantval=newval", &error); + g_assert (!ret); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_clear_error (&error); + + /* Replace with input key=value will fail for a key with multiple values */ + ret = ostree_kernel_args_new_replace (karg, "test=newval", &error); + g_assert (!ret); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_clear_error (&error); + + /* Replace with input key=value for a key with single value should succeed + * Also note, we also allow ''(empty string) valid to be a value + */ + ret = ostree_kernel_args_new_replace (karg, "single_key=newvalue", &error); + g_assert_no_error (error); + g_assert (ret); + g_assert (!check_string_existance (karg, "single_key")); + g_assert (check_string_existance (karg, "single_key=newvalue")); + + /* Replace with input key=value=newvalue if key and value both + * exist, the action should succeed + */ + ret = ostree_kernel_args_new_replace (karg, "test=firstval=newval", &error); + g_assert_no_error (error); + g_assert (ret); + g_assert (!check_string_existance (karg, "test=firstval")); + g_assert (check_string_existance (karg, "test=newval")); +} + +static gboolean +strcmp0_equal (gconstpointer v1, + gconstpointer v2) +{ + return g_strcmp0 (v1, v2) == 0; +} + +/* In this function, we want to verify that ostree_kernel_args_append + * and ostree_kernel_args_to_string is correct. After that + * we will use these two functions(append and tostring) in other tests: delete and replace + */ +static void +test_kargs_append (void) +{ + __attribute__((cleanup(ostree_kernel_args_cleanup))) OstreeKernelArgs *append_arg = ostree_kernel_args_new (); + /* Some valid cases (key=value) pair */ + ostree_kernel_args_append (append_arg, "test=valid"); + ostree_kernel_args_append (append_arg, "test=secondvalid"); + ostree_kernel_args_append (append_arg, "test="); + ostree_kernel_args_append (append_arg, "test"); + ostree_kernel_args_append (append_arg, "second_test"); + + /* We loops through the kargs inside table to verify + * the functionality of append because at this stage + * we have yet to find the conversion kargs to string fully "functional" + */ + GHashTable *kargs_table = _ostree_kernel_arg_get_kargs_table (append_arg); + GLNX_HASH_TABLE_FOREACH_KV (kargs_table, const char*, key, GPtrArray*, value_array) + { + if (g_str_equal (key, "test")) + { + g_assert (ot_ptr_array_find_with_equal_func (value_array, "valid", strcmp0_equal, NULL)); + g_assert (ot_ptr_array_find_with_equal_func (value_array, "secondvalid", strcmp0_equal, NULL)); + g_assert (ot_ptr_array_find_with_equal_func (value_array, "", strcmp0_equal, NULL)); + g_assert (ot_ptr_array_find_with_equal_func (value_array, NULL, strcmp0_equal, NULL)); + } + else + { + g_assert_cmpstr (key, ==, "second_test"); + g_assert (ot_ptr_array_find_with_equal_func (value_array, NULL, strcmp0_equal, NULL)); + } + } + + /* verify the value array is properly updated */ + GPtrArray *kargs_array = _ostree_kernel_arg_get_key_array (append_arg); + g_assert (ot_ptr_array_find_with_equal_func (kargs_array, "test", g_str_equal, NULL)); + g_assert (ot_ptr_array_find_with_equal_func (kargs_array, "second_test", g_str_equal, NULL)); + + /* Up till this point, we verified that the above was all correct, we then + * check ostree_kernel_args_to_string has the right result + */ + g_autofree gchar* kargs_str = ostree_kernel_args_to_string (append_arg); + g_auto(GStrv) kargs_list = g_strsplit(kargs_str, " ", -1); + g_assert (g_strv_contains ((const char* const *)kargs_list, "test=valid")); + g_assert (g_strv_contains ((const char* const *)kargs_list, "test=secondvalid")); + g_assert (g_strv_contains ((const char* const *)kargs_list, "test=")); + g_assert (g_strv_contains ((const char* const *)kargs_list, "test")); + g_assert (g_strv_contains ((const char* const *)kargs_list, "second_test")); + g_assert_cmpint (5, ==, g_strv_length (kargs_list)); +} + +int +main (int argc, + char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/kargs/kargs_append", test_kargs_append); + g_test_add_func ("/kargs/kargs_delete", test_kargs_delete); + g_test_add_func ("/kargs/kargs_replace", test_kargs_replace); + return g_test_run (); +}