Permalink
Browse files

Add module for parsing command line arguments.

This patch adds a new module that does what current snap-confine main()
does with regards to argument parsing. The module is fully tested and
has simple API sufficient to drive rest of snap-confine.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
  • Loading branch information...
1 parent 0021496 commit f7ee6df44916109731ce8a611ff57d9a70954b56 @zyga zyga committed Oct 27, 2016
Showing with 502 additions and 2 deletions.
  1. +5 −2 src/Makefile.am
  2. +262 −0 src/snap-confine-args-test.c
  3. +158 −0 src/snap-confine-args.c
  4. +77 −0 src/snap-confine-args.h
View
@@ -65,7 +65,9 @@ snap_confine_SOURCES = \
apparmor-support.c \
apparmor-support.h \
error.c \
- error.h
+ error.h \
+ snap-confine-args.c \
+ snap-confine-args.h
snap_confine_CFLAGS = -Wall -Werror $(AM_CFLAGS)
snap_confine_LDFLAGS = $(AM_LDFLAGS)
@@ -109,7 +111,8 @@ snap_confine_unit_tests_SOURCES = \
apparmor-support.c \
apparmor-support.h \
mount-opt-test.c \
- error-test.c
+ error-test.c \
+ snap-confine-args-test.c
snap_confine_unit_tests_CFLAGS = $(snap_confine_CFLAGS) $(GLIB_CFLAGS)
snap_confine_unit_tests_LDADD = $(snap_confine_LDADD) $(GLIB_LIBS)
snap_confine_unit_tests_LDFLAGS = $(snap_confine_LDFLAGS)
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2016 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "snap-confine-args.h"
+#include "snap-confine-args.c"
+
+#include <stdarg.h>
+
+#include <glib.h>
+
+/**
+ * Create an argc + argv pair out of a NULL terminated argument list.
+ **/
+static void
+ __attribute__ ((sentinel)) test_argc_argv(int *argcp, char ***argvp, ...)
+{
+ int argc = 0;
+ char **argv = NULL;
+ g_test_queue_free(argv);
+
+ va_list ap;
+ va_start(ap, argvp);
+ const char *arg;
+ do {
+ arg = va_arg(ap, const char *);
+ // XXX: yeah, wrong way but the worse that can happen is for test to fail
+ argv = realloc(argv, sizeof(const char **) * (argc + 1));
+ g_assert_nonnull(argv);
+ if (arg != NULL) {
+ char *arg_copy = strdup(arg);
+ g_test_queue_free(arg_copy);
+ argv[argc] = arg_copy;
+ argc += 1;
+ } else {
+ argv[argc] = NULL;
+ }
+ } while (arg != NULL);
+ va_end(ap);
+
+ *argcp = argc;
+ *argvp = argv;
+}
+
+static void test_test_argc_argv()
+{
+ // Check that test_argc_argv() correctly stores data
+ int argc;
+ char **argv;
+
+ test_argc_argv(&argc, &argv, NULL);
+ g_assert_cmpint(argc, ==, 0);
+ g_assert_null(argv[0]);
+
+ test_argc_argv(&argc, &argv, "zero", "one", "two", NULL);
+ g_assert_cmpint(argc, ==, 3);
+ g_assert_cmpstr(argv[0], ==, "zero");
+ g_assert_cmpstr(argv[1], ==, "one");
+ g_assert_cmpstr(argv[2], ==, "two");
+ g_assert_null(argv[3]);
+}
+
+static void test_sc_nonfatal_parse_args__typical()
+{
+ // Test that typical invocation of snap-confine is parsed correctly.
+ struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL;
+ struct sc_args *args;
+
+ int argc;
+ char **argv;
+ test_argc_argv(&argc, &argv,
+ "/usr/lib/snapd/snap-confine", "snap.app", "--option",
+ "arg", NULL);
+
+ args = sc_nonfatal_parse_args(&argc, &argv, &err);
+ g_test_queue_destroy((GDestroyNotify) sc_args_free, args);
+ g_assert_null(err);
+ g_assert_nonnull(args);
+
+ // Check supported switches and arguments
+ g_assert_cmpstr(sc_args_security_tag(args), ==, "snap.app");
+ g_assert_cmpint(sc_args_is_version_query(args), ==, false);
+
+ // Check remaining arguments
+ g_assert_cmpint(argc, ==, 3);
+ g_assert_cmpstr(argv[0], ==, "/usr/lib/snapd/snap-confine");
+ g_assert_cmpstr(argv[1], ==, "--option");
+ g_assert_cmpstr(argv[2], ==, "arg");
+ g_assert_null(argv[3]);
+}
+
+static void test_sc_nonfatal_parse_args__ubuntu_core_launcher()
+{
+ // Test that typical legacy invocation of snap-confine via the
+ // ubuntu-core-launcher symlink, with duplicated security tag, is parsed
+ // correctly.
+ struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL;
+ struct sc_args *args;
+
+ int argc;
+ char **argv;
+ test_argc_argv(&argc, &argv,
+ "/usr/bin/ubuntu-core-launcher", "snap.app", "snap.app",
+ "--option", "arg", NULL);
+
+ args = sc_nonfatal_parse_args(&argc, &argv, &err);
+ g_test_queue_destroy((GDestroyNotify) sc_args_free, args);
+ g_assert_null(err);
+ g_assert_nonnull(args);
+
+ // Check supported switches and arguments
+ g_assert_cmpstr(sc_args_security_tag(args), ==, "snap.app");
+ g_assert_cmpint(sc_args_is_version_query(args), ==, false);
+
+ // Check remaining arguments
+ g_assert_cmpint(argc, ==, 3);
+ g_assert_cmpstr(argv[0], ==, "/usr/bin/ubuntu-core-launcher");
+ g_assert_cmpstr(argv[1], ==, "--option");
+ g_assert_cmpstr(argv[2], ==, "arg");
+ g_assert_null(argv[3]);
+}
+
+static void test_sc_nonfatal_parse_args__version()
+{
+ // Test that snap-confine --version is detected.
+ struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL;
+ struct sc_args *args;
+
+ int argc;
+ char **argv;
+ test_argc_argv(&argc, &argv,
+ "/usr/lib/snapd/snap-confine", "--version", "ignored",
+ "garbage", NULL);
+
+ args = sc_nonfatal_parse_args(&argc, &argv, &err);
+ g_test_queue_destroy((GDestroyNotify) sc_args_free, args);
+ g_assert_null(err);
+ g_assert_nonnull(args);
+
+ // Check supported switches and arguments
+ g_assert_null(sc_args_security_tag(args));
+ g_assert_cmpint(sc_args_is_version_query(args), ==, true);
+
+ // Check remaining arguments
+ g_assert_cmpint(argc, ==, 3);
+ g_assert_cmpstr(argv[0], ==, "/usr/lib/snapd/snap-confine");
+ g_assert_cmpstr(argv[1], ==, "ignored");
+ g_assert_cmpstr(argv[2], ==, "garbage");
+ g_assert_null(argv[3]);
+}
+
+static void test_sc_nonfatal_parse_args__nothing_to_parse()
+{
+ // Check that calling without any arguments is reported as error.
+ struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL;
+ struct sc_args *args;
+
+ int argc;
+ char **argv;
+ test_argc_argv(&argc, &argv, NULL);
+
+ args = sc_nonfatal_parse_args(&argc, &argv, &err);
+ g_assert_nonnull(err);
+ g_assert_null(args);
+
+ // Check the error that we've got
+ g_assert_cmpstr(sc_error_msg(err), ==,
+ "cannot parse arguments, argc is zero");
+}
+
+static void test_sc_nonfatal_parse_args__no_security_tag()
+{
+ // Check that lack of security tag is reported as error.
+ struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL;
+ struct sc_args *args;
+
+ int argc;
+ char **argv;
+ test_argc_argv(&argc, &argv, "/usr/lib/snapd/snap-confine", NULL);
+
+ args = sc_nonfatal_parse_args(&argc, &argv, &err);
+ g_assert_nonnull(err);
+ g_assert_null(args);
+
+ // Check the error that we've got
+ g_assert_cmpstr(sc_error_msg(err), ==,
+ "application or hook security tag was not provided");
+}
+
+static void test_sc_nonfatal_parse_args__unknown_option()
+{
+ // Check that unrecognized option switch is reported as error.
+ struct sc_error *err __attribute__ ((cleanup(sc_cleanup_error))) = NULL;
+ struct sc_args *args;
+
+ int argc;
+ char **argv;
+ test_argc_argv(&argc, &argv, "/usr/lib/snapd/snap-confine",
+ "--frozbonicator", NULL);
+
+ args = sc_nonfatal_parse_args(&argc, &argv, &err);
+ g_assert_nonnull(err);
+ g_assert_null(args);
+
+ // Check the error that we've got
+ g_assert_cmpstr(sc_error_msg(err), ==,
+ "unrecognized command line option: --frozbonicator");
+}
+
+static void test_sc_nonfatal_parse_args__forwards_error()
+{
+ // Check that sc_nonfatal_parse_args() forwards errors.
+ if (g_test_subprocess()) {
+ int argc;
+ char **argv;
+ test_argc_argv(&argc, &argv, "/usr/lib/snapd/snap-confine",
+ "--frozbonicator", NULL);
+
+ // Call sc_nonfatal_parse_args() without an error handle
+ sc_nonfatal_parse_args(&argc, &argv, NULL);
+
+ g_test_message("expected not to reach this place");
+ g_test_fail();
+ return;
+ }
+ g_test_trap_subprocess(NULL, 0, 0);
+ g_test_trap_assert_failed();
+ g_test_trap_assert_stderr
+ ("unrecognized command line option: --frozbonicator\n");
+}
+
+static void __attribute__ ((constructor)) init()
+{
+ g_test_add_func("/args/test_argc_argv", test_test_argc_argv);
+ g_test_add_func("/args/sc_nonfatal_parse_args/typical",
+ test_sc_nonfatal_parse_args__typical);
+ g_test_add_func("/args/sc_nonfatal_parse_args/ubuntu_core_launcher",
+ test_sc_nonfatal_parse_args__ubuntu_core_launcher);
+ g_test_add_func("/args/sc_nonfatal_parse_args/version",
+ test_sc_nonfatal_parse_args__version);
+ g_test_add_func("/args/sc_nonfatal_parse_args/nothing_to_parse",
+ test_sc_nonfatal_parse_args__nothing_to_parse);
+ g_test_add_func("/args/sc_nonfatal_parse_args/no_security_tag",
+ test_sc_nonfatal_parse_args__no_security_tag);
+ g_test_add_func("/args/sc_nonfatal_parse_args/unknown_option",
+ test_sc_nonfatal_parse_args__unknown_option);
+ g_test_add_func("/args/sc_nonfatal_parse_args/forwards_error",
+ test_sc_nonfatal_parse_args__forwards_error);
+}
Oops, something went wrong.

0 comments on commit f7ee6df

Please sign in to comment.