/*
* test-load.c: test the aug_load functionality
*
* Copyright (C) 2009-2016 David Lutterkort
*
* 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.1 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
*
* Author: David Lutterkort <lutter@redhat.com>
*/
#include <config.h>
#include <sys/types.h>
#include <unistd.h>
#include "augeas.h"
#include "cutest.h"
#include "internal.h"
static const char *abs_top_srcdir;
static const char *abs_top_builddir;
static char *root = NULL;
static char *loadpath;
#define die(msg) \
do { \
fprintf(stderr, "%d: Fatal error: %s\n", __LINE__, msg); \
exit(EXIT_FAILURE); \
} while(0)
static char *setup_hosts(CuTest *tc) {
char *etcdir, *build_root;
if (asprintf(&build_root, "%s/build/test-load/%s",
abs_top_builddir, tc->name) < 0) {
CuFail(tc, "failed to set build_root");
}
if (asprintf(&etcdir, "%s/etc", build_root) < 0)
CuFail(tc, "asprintf etcdir failed");
run(tc, "test -d %s && chmod -R u+rw %s || :", build_root, build_root);
run(tc, "rm -rf %s", build_root);
run(tc, "mkdir -p %s", etcdir);
run(tc, "cp -pr %s/etc/hosts %s", root, etcdir);
free(etcdir);
return build_root;
}
static struct augeas *setup_hosts_aug(CuTest *tc, char *build_root) {
struct augeas *aug = NULL;
int r;
aug = aug_init(build_root, loadpath, AUG_NO_MODL_AUTOLOAD);
CuAssertPtrNotNull(tc, aug);
r = aug_set(aug, "/augeas/load/Hosts/lens", "Hosts.lns");
CuAssertRetSuccess(tc, r);
r = aug_set(aug, "/augeas/load/Hosts/incl", "/etc/hosts");
CuAssertRetSuccess(tc, r);
free(build_root);
return aug;
}
static struct augeas *setup_writable_hosts(CuTest *tc) {
char *build_root = setup_hosts(tc);
run(tc, "chmod -R u+w %s", build_root);
return setup_hosts_aug(tc, build_root);
}
static struct augeas *setup_unreadable_hosts(CuTest *tc) {
char *build_root = setup_hosts(tc);
run(tc, "chmod -R a-r %s/etc/hosts", build_root);
return setup_hosts_aug(tc, build_root);
}
static void testDefault(CuTest *tc) {
augeas *aug = NULL;
int nmatches, r;
aug = aug_init(root, loadpath, AUG_NO_STDINC);
CuAssertPtrNotNull(tc, aug);
nmatches = aug_match(aug, "/augeas/load/*", NULL);
CuAssertPositive(tc, nmatches);
nmatches = aug_match(aug, "/files/etc/hosts/1", NULL);
CuAssertIntEquals(tc, 1, nmatches);
r = aug_rm(aug, "/augeas/load/*");
CuAssertTrue(tc, r >= 0);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
nmatches = aug_match(aug, "/files/*", NULL);
CuAssertZero(tc, nmatches);
aug_close(aug);
}
static void testNoLoad(CuTest *tc) {
augeas *aug = NULL;
int nmatches, r;
aug = aug_init(root, loadpath, AUG_NO_STDINC|AUG_NO_LOAD);
CuAssertPtrNotNull(tc, aug);
nmatches = aug_match(aug, "/augeas/load/*", NULL);
CuAssertPositive(tc, nmatches);
nmatches = aug_match(aug, "/files/*", NULL);
CuAssertZero(tc, nmatches);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
nmatches = aug_match(aug, "/files/*", NULL);
CuAssertPositive(tc, nmatches);
/* Now load /etc/hosts only */
r = aug_rm(aug, "/augeas/load/*[label() != 'Hosts']");
CuAssertTrue(tc, r >= 0);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
nmatches = aug_match(aug, "/files/etc/*", NULL);
CuAssertIntEquals(tc, 1, nmatches);
aug_close(aug);
}
static void testNoAutoload(CuTest *tc) {
augeas *aug = NULL;
int nmatches, r;
aug = aug_init(root, loadpath, AUG_NO_MODL_AUTOLOAD);
CuAssertPtrNotNull(tc, aug);
nmatches = aug_match(aug, "/augeas/load/*", NULL);
CuAssertZero(tc, nmatches);
r = aug_set(aug, "/augeas/load/Hosts/lens", "Hosts.lns");
CuAssertRetSuccess(tc, r);
r = aug_set(aug, "/augeas/load/Hosts/incl", "/etc/hosts");
CuAssertRetSuccess(tc, r);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
nmatches = aug_match(aug, "/files/etc/hosts/*[ipaddr]", NULL);
CuAssertIntEquals(tc, 2, nmatches);
aug_close(aug);
}
static void invalidLens(CuTest *tc, augeas *aug, const char *lens) {
int r, nmatches;
r = aug_set(aug, "/augeas/load/Junk/lens", lens);
CuAssertRetSuccess(tc, r);
r = aug_set(aug, "/augeas/load/Junk/incl", "/dev/null");
CuAssertRetSuccess(tc, r);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
nmatches = aug_match(aug, "/augeas/load/Junk/error", NULL);
CuAssertIntEquals(tc, 1, nmatches);
}
static void testInvalidLens(CuTest *tc) {
augeas *aug = NULL;
int r;
aug = aug_init(root, loadpath, AUG_NO_STDINC|AUG_NO_LOAD);
CuAssertPtrNotNull(tc, aug);
r = aug_rm(aug, "/augeas/load/*");
CuAssertTrue(tc, r >= 0);
invalidLens(tc, aug, NULL);
invalidLens(tc, aug, "@Nomodule");
invalidLens(tc, aug, "@Util");
invalidLens(tc, aug, "Nomodule.noelns");
aug_close(aug);
}
static void testLoadSave(CuTest *tc) {
augeas *aug = NULL;
int r;
aug = setup_writable_hosts(tc);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
r = aug_save(aug);
CuAssertRetSuccess(tc, r);
r = aug_match(aug, "/augeas/events/saved", NULL);
CuAssertIntEquals(tc, 0, r);
aug_close(aug);
}
/* Tests bug #79 */
static void testLoadDefined(CuTest *tc) {
augeas *aug = NULL;
int r;
aug = aug_init(root, loadpath, AUG_NO_STDINC);
CuAssertPtrNotNull(tc, aug);
r = aug_defvar(aug, "v", "/files/etc/hosts/*/ipaddr");
CuAssertIntEquals(tc, 2, r);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
r = aug_match(aug, "$v", NULL);
CuAssertIntEquals(tc, 2, r);
aug_close(aug);
}
static void testDefvarExpr(CuTest *tc) {
static const char *const expr = "/files/etc/hosts/*/ipaddr";
static const char *const expr2 = "/files/etc/hosts/*/canonical";
augeas *aug = NULL;
const char *v;
int r;
aug = aug_init(root, loadpath, AUG_NO_STDINC);
CuAssertPtrNotNull(tc, aug);
r = aug_defvar(aug, "v", expr);
CuAssertIntEquals(tc, 2, r);
r = aug_get(aug, "/augeas/variables/v", &v);
CuAssertIntEquals(tc, 1, r);
CuAssertStrEquals(tc, expr, v);
r = aug_defvar(aug, "v", expr2);
CuAssertIntEquals(tc, 2, r);
r = aug_get(aug, "/augeas/variables/v", &v);
CuAssertIntEquals(tc, 1, r);
CuAssertStrEquals(tc, expr2, v);
r = aug_defvar(aug, "v", NULL);
CuAssertIntEquals(tc, 0, r);
r = aug_get(aug, "/augeas/variables/v", &v);
CuAssertIntEquals(tc, 0, r);
CuAssertStrEquals(tc, NULL, v);
aug_close(aug);
}
static void testReloadChanged(CuTest *tc) {
FILE *fp;
augeas *aug = NULL;
const char *build_root, *mtime2, *s;
char *mtime1;
char *hosts = NULL;
int r;
aug = setup_writable_hosts(tc);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
r = aug_get(aug, "/augeas/root", &build_root);
CuAssertIntEquals(tc, 1, r);
r = aug_get(aug, "/augeas/files/etc/hosts/mtime", &s);
CuAssertIntEquals(tc, 1, r);
mtime1 = strdup(s);
CuAssertPtrNotNull(tc, mtime1);
/* Tickle /etc/hosts behind augeas' back */
r = asprintf(&hosts, "%setc/hosts", build_root);
CuAssertPositive(tc, r);
fp = fopen(hosts, "a");
CuAssertPtrNotNull(tc, fp);
r = fprintf(fp, "192.168.0.1 other.example.com\n");
CuAssertTrue(tc, r > 0);
r = fclose(fp);
CuAssertRetSuccess(tc, r);
/* Unsaved changes are discarded */
r = aug_set(aug, "/files/etc/hosts/1/ipaddr", "127.0.0.2");
CuAssertRetSuccess(tc, r);
/* Check that we really did load the right file*/
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
r = aug_get(aug, "/augeas/files/etc/hosts/mtime", &mtime2);
CuAssertIntEquals(tc, 1, r);
CuAssertStrNotEqual(tc, mtime1, mtime2);
r = aug_match(aug, "/files/etc/hosts/*[ipaddr = '192.168.0.1']", NULL);
CuAssertIntEquals(tc, 1, r);
r = aug_match(aug, "/files/etc/hosts/1[ipaddr = '127.0.0.1']", NULL);
CuAssertIntEquals(tc, 1, r);
free(mtime1);
free(hosts);
aug_close(aug);
}
static void testReloadDirty(CuTest *tc) {
augeas *aug = NULL;
int r;
aug = setup_writable_hosts(tc);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
/* Unsaved changes are discarded */
r = aug_set(aug, "/files/etc/hosts/1/ipaddr", "127.0.0.2");
CuAssertRetSuccess(tc, r);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
r = aug_match(aug, "/files/etc/hosts/1[ipaddr = '127.0.0.1']", NULL);
CuAssertIntEquals(tc, 1, r);
aug_close(aug);
}
static void testReloadDeleted(CuTest *tc) {
augeas *aug = NULL;
int r;
aug = setup_writable_hosts(tc);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
/* A missing file causes a reload */
r = aug_rm(aug, "/files/etc/hosts");
CuAssertPositive(tc, r);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
r = aug_match(aug, "/files/etc/hosts/1[ipaddr = '127.0.0.1']", NULL);
CuAssertIntEquals(tc, 1, r);
/* A missing entry in a file causes a reload */
r = aug_rm(aug, "/files/etc/hosts/1/ipaddr");
CuAssertPositive(tc, r);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
r = aug_match(aug, "/files/etc/hosts/1[ipaddr = '127.0.0.1']", NULL);
CuAssertIntEquals(tc, 1, r);
aug_close(aug);
}
static void testReloadDeletedMeta(CuTest *tc) {
augeas *aug = NULL;
int r;
aug = setup_writable_hosts(tc);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
/* Unsaved changes are discarded */
r = aug_rm(aug, "/augeas/files/etc/hosts");
CuAssertPositive(tc, r);
r = aug_set(aug, "/files/etc/hosts/1/ipaddr", "127.0.0.2");
CuAssertRetSuccess(tc, r);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
r = aug_match(aug, "/files/etc/hosts/1[ipaddr = '127.0.0.1']", NULL);
CuAssertIntEquals(tc, 1, r);
aug_close(aug);
}
/* BZ 613967 - segfault when reloading a file that has been externally
* modified, and we have a variable pointing into the old tree
*/
static void testReloadExternalMod(CuTest *tc) {
augeas *aug = NULL;
int r, created;
const char *aug_root, *s;
char *mtime;
aug = setup_writable_hosts(tc);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
r = aug_get(aug, "/augeas/files/etc/hosts/mtime", &s);
CuAssertIntEquals(tc, 1, r);
mtime = strdup(s);
CuAssertPtrNotNull(tc, mtime);
/* Set up a new entry and save */
r = aug_defnode(aug, "new", "/files/etc/hosts/3", NULL, &created);
CuAssertIntEquals(tc, 1, r);
CuAssertIntEquals(tc, 1, created);
r = aug_set(aug, "$new/ipaddr", "172.31.42.1");
CuAssertRetSuccess(tc, r);
r = aug_set(aug, "$new/canonical", "new.example.com");
CuAssertRetSuccess(tc, r);
r = aug_save(aug);
CuAssertRetSuccess(tc, r);
/* Fake the mtime to be old */
r = aug_set(aug, "/augeas/files/etc/hosts/mtime", mtime);
CuAssertRetSuccess(tc, r);
/* Now modify the file outside of Augeas */
r = aug_get(aug, "/augeas/root", &aug_root);
CuAssertIntEquals(tc, 1, r);
run(tc, "sed -e '1,2d' %setc/hosts > %setc/hosts.new", aug_root, aug_root);
run(tc, "mv %setc/hosts.new %setc/hosts", aug_root, aug_root);
/* Reload and save again */
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
r = aug_save(aug);
CuAssertRetSuccess(tc, r);
r = aug_match(aug, "/files/etc/hosts/#comment", NULL);
CuAssertIntEquals(tc, 2, r);
r = aug_match(aug, "/files/etc/hosts/*", NULL);
CuAssertIntEquals(tc, 5, r);
free(mtime);
aug_close(aug);
}
/* Bug #259 - after save with /augeas/save = newfile, make sure we discard
* changes and reload files.
*/
static void testReloadAfterSaveNewfile(CuTest *tc) {
augeas *aug = NULL;
int r;
aug = setup_writable_hosts(tc);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
r = aug_set(aug, "/augeas/save", "newfile");
CuAssertRetSuccess(tc, r);
r = aug_set(aug, "/files/etc/hosts/1/ipaddr", "127.0.0.2");
CuAssertRetSuccess(tc, r);
r = aug_save(aug);
CuAssertRetSuccess(tc, r);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
r = aug_match(aug, "/files/etc/hosts/1[ipaddr = '127.0.0.1']", NULL);
CuAssertIntEquals(tc, 1, r);
aug_close(aug);
}
/* Make sure parse errors from applying a lens to a file that does not
* match get reported under /augeas//error
*
* Tests bug #138
*/
static void testParseErrorReported(CuTest *tc) {
augeas *aug = NULL;
int nmatches, r;
aug = aug_init(root, loadpath, AUG_NO_MODL_AUTOLOAD);
CuAssertPtrNotNull(tc, aug);
r = aug_set(aug, "/augeas/load/Bad/lens", "Yum.lns");
CuAssertRetSuccess(tc, r);
r = aug_set(aug, "/augeas/load/Bad/incl", "/etc/fstab");
CuAssertRetSuccess(tc, r);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
nmatches = aug_match(aug, "/augeas/files/etc/fstab/error", NULL);
CuAssertIntEquals(tc, 1, nmatches);
aug_close(aug);
}
/* Test failed file opening is reported, e.g. EACCES */
static void testPermsErrorReported(CuTest *tc) {
if (getuid() == 0) {
puts("pending (testPermsErrorReported): can't test permissions under root account");
return;
}
augeas *aug = NULL;
int r;
const char *s;
aug = setup_unreadable_hosts(tc);
r = aug_load(aug);
CuAssertRetSuccess(tc, r);
r = aug_match(aug, "/files/etc/hosts", NULL);
CuAssertIntEquals(tc, 0, r);
r = aug_get(aug, "/augeas/files/etc/hosts/error", &s);
CuAssertIntEquals(tc, 1, r);
CuAssertStrEquals(tc, "read_failed", s);
r = aug_get(aug, "/augeas/files/etc/hosts/error/message", &s);
CuAssertIntEquals(tc, 1, r);
aug_close(aug);
}
/* Test bug #252 - excl patterns have no effect when loading with a root */
static void testLoadExclWithRoot(CuTest *tc) {
augeas *aug = NULL;
static const char *const cmds =
"set /augeas/context /augeas/load\n"
"set Hosts/lens Hosts.lns\n"
"set Hosts/incl /etc/hosts\n"
"set Fstab/lens Fstab.lns\n"
"set Fstab/incl /etc/ho*\n"
"set Fstab/excl /etc/hosts\n"
"load";
int r;
aug = aug_init(root, loadpath, AUG_NO_STDINC|AUG_NO_MODL_AUTOLOAD);
CuAssertPtrNotNull(tc, aug);
r = aug_srun(aug, stderr, cmds);
CuAssertIntEquals(tc, 7, r);
r = aug_match(aug, "/augeas//error", NULL);
CuAssertIntEquals(tc, 0, r);
aug_close(aug);
}
/* Test excl patterns matching the end of a filename work, e.g. *.bak */
static void testLoadTrailingExcl(CuTest *tc) {
augeas *aug = NULL;
static const char *const cmds =
"set /augeas/context /augeas/load/Shellvars\n"
"set lens Shellvars.lns\n"
"set incl /etc/sysconfig/network-scripts/ifcfg-lo*\n"
"set excl *.rpmsave\n"
"load";
int r;
aug = aug_init(root, loadpath, AUG_NO_STDINC|AUG_NO_MODL_AUTOLOAD);
CuAssertPtrNotNull(tc, aug);
r = aug_srun(aug, stderr, cmds);
CuAssertIntEquals(tc, 5, r);
r = aug_match(aug, "/augeas/files/etc/sysconfig/network-scripts/ifcfg-lo", NULL);
CuAssertIntEquals(tc, 1, r);
r = aug_match(aug, "/augeas/files/etc/sysconfig/network-scripts/ifcfg-lo.rpmsave", NULL);
CuAssertIntEquals(tc, 0, r);
aug_close(aug);
}
static void testMultipleXfm(CuTest *tc) {
augeas *aug = NULL;
static const char *const cmds =
"set /augeas/context /augeas/load\n"
"set P1/lens Passwd.lns\n"
"set P1/incl /etc/passwd\n"
"set P2/lens @Passwd\n"
"set P2/incl /etc/passwd\n"
"load";
int r;
const char *v;
aug = aug_init(root, loadpath, AUG_NO_STDINC|AUG_NO_LOAD);
CuAssertPtrNotNull(tc, aug);
/* The initial setup is fine: two different transforms that want to use
the same lens to load a file */
r = aug_srun(aug, stderr, cmds);
CuAssertIntEquals(tc, 6, r);
r = aug_match(aug, "/augeas/files/etc/passwd", NULL);
CuAssertIntEquals(tc, 1, r);
/* Now change things so we use two different lenses for the same file */
r = aug_set(aug, "/augeas/load/P2/lens", "Hosts.lns");
CuAssertIntEquals(tc, 0, r);
r = aug_rm(aug, "/files/*");
CuAssertPositive(tc, r);
r = aug_load(aug);
CuAssertIntEquals(tc, 0, r);
r = aug_get(aug, "/augeas/files/etc/passwd/error", &v);
CuAssertIntEquals(tc, 1, r);
CuAssertStrEquals(tc, "mxfm_load", v);
aug_close(aug);
}
int main(void) {
char *output = NULL;
CuSuite* suite = CuSuiteNew();
CuSuiteSetup(suite, NULL, NULL);
SUITE_ADD_TEST(suite, testDefault);
SUITE_ADD_TEST(suite, testNoLoad);
SUITE_ADD_TEST(suite, testNoAutoload);
SUITE_ADD_TEST(suite, testInvalidLens);
SUITE_ADD_TEST(suite, testLoadSave);
SUITE_ADD_TEST(suite, testLoadDefined);
SUITE_ADD_TEST(suite, testDefvarExpr);
SUITE_ADD_TEST(suite, testReloadChanged);
SUITE_ADD_TEST(suite, testReloadDirty);
SUITE_ADD_TEST(suite, testReloadDeleted);
SUITE_ADD_TEST(suite, testReloadDeletedMeta);
SUITE_ADD_TEST(suite, testReloadExternalMod);
SUITE_ADD_TEST(suite, testReloadAfterSaveNewfile);
SUITE_ADD_TEST(suite, testParseErrorReported);
SUITE_ADD_TEST(suite, testPermsErrorReported);
SUITE_ADD_TEST(suite, testLoadExclWithRoot);
SUITE_ADD_TEST(suite, testLoadTrailingExcl);
SUITE_ADD_TEST(suite, testMultipleXfm);
abs_top_srcdir = getenv("abs_top_srcdir");
if (abs_top_srcdir == NULL)
die("env var abs_top_srcdir must be set");
abs_top_builddir = getenv("abs_top_builddir");
if (abs_top_builddir == NULL)
die("env var abs_top_builddir must be set");
if (asprintf(&root, "%s/tests/root", abs_top_srcdir) < 0) {
die("failed to set root");
}
if (asprintf(&loadpath, "%s/lenses", abs_top_srcdir) < 0) {
die("failed to set loadpath");
}
CuSuiteRun(suite);
CuSuiteSummary(suite, &output);
CuSuiteDetails(suite, &output);
printf("%s\n", output);
free(output);
int result = suite->failCount;
CuSuiteFree(suite);
return result;
}
/*
* Local variables:
* indent-tabs-mode: nil
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4
* End:
*/