Skip to content
This repository has been archived by the owner on Oct 4, 2023. It is now read-only.

Commit

Permalink
Add module for parsing command line arguments.
Browse files Browse the repository at this point in the history
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
zyga committed Dec 1, 2016
1 parent 0021496 commit f7ee6df
Show file tree
Hide file tree
Showing 4 changed files with 502 additions and 2 deletions.
7 changes: 5 additions & 2 deletions src/Makefile.am
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
262 changes: 262 additions & 0 deletions src/snap-confine-args-test.c
@@ -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);
}

0 comments on commit f7ee6df

Please sign in to comment.