Chris Reeves <chris.reeves@york.ac.uk>
Gerlof Fokkema <gerlof.fokkema@gmail.com>
Daniel Trebbien <dtrebbien@gmail.com>
+ Robert Moucha <robert.moucha@gooddata.com>
+ Craig Miskell <craig@catalyst.net.nz>
+ Anton Baranov <abaranov@linuxfoundation.org>
+ Josef Reidinger <jreidinger@suse.cz>
-1.5.0 - 2015-05-11
+1.6.0 - 2016-08-05
+ - General changes/additions
+ * augtool: add --load-file option, and corresponding load-file command
+ to load individual files based on the autoload information in lenses
+ * path expressions: numbers in path expressions are now 64 bit integers
+ rather than whatever the C compiler decided 'int' would be
+ - API changes
+ * add aug_load_file to load individual files, bug #135
+ - Lens changes/additions
+ * Httpd: follow line continuations in comments
+ * Nginx: look for nginx.conf in /usr/local/etc, too (Omer Katz)
+ * Ntp: allow 'pool' (Craig Miskell) (Issue #378);
+ fix restrict to allow also -4 and also fix
+ save/store ability (Josef Reidinger) (Issue #386)
+ * Pam: use spaces instead of tabs as the separator in new entries
+ (Loren Gordon) (Issue #236)
+ * Postfix_Passwordmap: New lens to parse Postfix password maps
+ (Anton Baranov) (Issue #380)
+ * Rsyslog: Support for rsyslog RainerScript syntax
+ (Craig Miskell) (Issue #379)
+ * Shellvars: Load /etc/lbu/lbu.conf, the config for Alpine's Local
+ Backup Utility (Kaarle Ritvanen)
+ Load /etc/profile, /etc/profile.d/*, and /etc/byobu
+ * Vsftpd: Add allow_writeable_chroot boolead option
+ (Robert Moucha) (Issue #376)
+
+1.5.0 - 2016-05-11
- General changes/additions
* augtool: new --timing option that prints after each operation how long
it took
-AC_INIT(augeas, 1.5.0)
+AC_INIT(augeas, 1.6.0)
AC_CONFIG_SRCDIR([src/augeas.c])
AC_CONFIG_AUX_DIR([build/ac-aux])
AM_CONFIG_HEADER([config.h])
fi
dnl Version info in libtool's notation
-AC_SUBST([LIBAUGEAS_VERSION_INFO], [20:1:20])
-AC_SUBST([LIBFA_VERSION_INFO], [5:2:4])
+AC_SUBST([LIBAUGEAS_VERSION_INFO], [21:0:21])
+AC_SUBST([LIBFA_VERSION_INFO], [5:3:4])
AC_GNU_SOURCE
Extension: aug
Block Comment: (* *)
- Augeas Variable Prototype Enders: \n let test module filter
Augeas Test Prototype Enders: \n let test module filter
Augeas Lens Prototype Enders: \n let test module filter
+ Augeas Variable Prototype Enders: \n let test module filter
Perl Package: NaturalDocs::Languages::Augeas
File: Passwd (passwd.aug)
File: Pbuilder (pbuilder.aug)
File: Pg_Hba (pg_hba.aug)
+ File: Postfix_Passwordmap (postfix_passwordmap.aug)
File: Postfix_Transport (postfix_transport.aug)
File: Postfix_Virtual (postfix_virtual.aug)
File: Postgresql (postgresql.aug)
File: Test_OpenShift_Config (tests/test_openshift_config.aug)
File: Test_OpenShift_Http (tests/test_openshift_http.aug)
File: Test_OpenShift_Quickstarts (tests/test_openshift_quickstarts.aug)
+ File: Test_Postfix_Passwordmap (tests/test_postfix_passwordmap.aug)
File: Test_Postfix_Transport (tests/test_postfix_transport.aug)
File: Test_Postfix_Virtual (tests/test_postfix_virtual.aug)
File: Test_Postgresql (tests/test_postgresql.aug)
let nmtoken = /[a-zA-Z:_][a-zA-Z0-9:_.-]*/
let word = /[a-z][a-z0-9._-]*/i
-let comment = Util.comment
let eol = Util.doseol
let empty = Util.empty_dos
let indent = Util.indent
+let comment_val_re = /([^ \t\r\n](.|\\\\\r?\n)*[^ \\\t\r\n]|[^ \t\r\n])/
+let comment = [ label "#comment" . del /[ \t]*#[ \t]*/ "# "
+ . store comment_val_re . eol ]
+
(* borrowed from shellvars.aug *)
let char_arg_dir = /([^\\ '"{\t\r\n]|[^ '"{\t\r\n]+[^\\ \t\r\n])|\\\\"|\\\\'|\\\\ /
let char_arg_sec = /([^\\ '"\t\r\n>]|[^ '"\t\r\n>]+[^\\ \t\r\n>])|\\\\"|\\\\'|\\\\ /
let filter = incl "/etc/nginx/nginx.conf"
. incl "/etc/nginx/conf.d/*.conf"
. incl "/usr/portage/www-servers/nginx/files/nginx.conf"
+ . incl "/usr/local/etc/nginx/nginx.conf"
+ . incl "/usr/local/etc/nginx/conf.d/*.conf"
let xfm = transform lns filter
sep_spc . store word ]
| [ sep_spc . key (/autokey|burst|iburst|noselect|preempt/ |
/prefer|true|dynamic/) ] in
- let cmd = /server|peer|broadcast|manycastclient/
+ let cmd = /pool|server|peer|broadcast|manycastclient/
| /multicastclient|manycastserver/ in
record cmd opt*
(* Define restrict *)
let restrict_record =
- let action = [ label "action" . sep_spc . store word ] in
- [ key "restrict" . sep_spc .
- [ label "ipv6" . Util.del_str "-6" . sep_spc ]? .
- store (word - "-6") . action* . eol ]
+ let ip6_restrict = [ label "ipv6" . sep_spc . Util.del_str "-6" ] in
+ let ip4_restrict = [ label "ipv4" . sep_spc . Util.del_str "-4" ] in
+ let action = [ label "action" . sep_spc . store /[^,# \n\t-][^,# \n\t]*/ ] in
+ [ key "restrict" . (ip6_restrict | ip4_restrict)? . sep_spc . store /[^,# \n\t-][^,# \n\t]*/ . action* . eol ]
(* Define statistics *)
let statistics_flag (kw:string) = [ sep_spc . key kw ]
(* Shared with PamConf *)
let record = [ label "optional" . del "-" "-" ]? .
[ label "type" . store types ] .
- Util.del_ws_tab .
+ Sep.space .
[ label "control" . store control] .
- Util.del_ws_tab .
+ Sep.space .
[ label "module" . store word ] .
- [ Util.del_ws_tab . label "argument" . store argument ]* .
+ [ Sep.space . label "argument" . store argument ]* .
comment_or_eol
let record_svc = [ seq "record" . indent . record ]
let record = [ seq "record" . indent .
[ label "service" . store service ] .
- Util.del_ws_tab .
+ Sep.space .
Pam.record ]
let lns = ( empty | comment | include | record ) *
--- /dev/null
+(*
+Module: Postfix_Passwordmap
+ Parses /etc/postfix/*passwd
+
+Author: Anton Baranov <abaranov@linuxfoundation.org>
+
+About: Reference
+ This lens tries to keep as close as possible to `man 5 postconf` and
+ http://www.postfix.org/SASL_README.html#client_sasl_enable where possible.
+
+About: License
+ This file is licenced under the LGPL v2+, like the rest of Augeas.
+
+About: Configuration files
+ This lens applies to /etc/postfix/*passwd. See <filter>.
+
+About: Examples
+ The <Test_Postfix_Passwordmap> file contains various examples and tests.
+*)
+
+module Postfix_Passwordmap =
+
+autoload xfm
+
+(* View: space_or_eol *)
+let space_or_eol = del /([ \t]*\n)?[ \t]+/ " "
+
+(* View: word *)
+let word = store /[A-Za-z0-9@_\+\*.-]+/
+
+(* View: colon *)
+let colon = Sep.colon
+
+(* View: username *)
+let username = [ label "username" . word ]
+
+(* View: password *)
+let password = [ label "password" . (store Rx.space_in)? ]
+
+(* View: record *)
+let record = [ label "pattern" . store /\[?[A-Za-z0-9@\*.-]+\]?(:?[A-Za-z0-9]*)*/
+ . space_or_eol . username . colon . password
+ . Util.eol ]
+
+(* View: lns *)
+let lns = (Util.empty | Util.comment | record)*
+
+(* Variable: filter *)
+let filter = incl "/etc/postfix/*passwd"
+
+let xfm = transform lns filter
let macro_rx = /[^,# \n\t][^#\n]*[^,# \n\t]|[^,# \n\t]/
let macro = [ key /$[A-Za-z0-9]+/ . Sep.space . store macro_rx . Util.comment_or_eol ]
+let config_object_param = [ key /[A-Za-z]+/ . Sep.equal . Quote.dquote
+ . store /[^"]+/ . Quote.dquote . Sep.opt_space ]
+let config_object = [ key /action|global|input|module|parser|timezone/ . Sep.lbracket
+ . config_object_param+ . Sep.rbracket . Util.comment_or_eol ]
+
(* View: users
Map :omusrmsg: and a list of users, or a single *
*)
in [ label "filter" . prop_name . sep . prop_oper . sep . prop_val .
Sep.space . prop_act . Util.eol ]
-let entries = ( Syslog.empty | Syslog.comment | entry | macro | prop_filter )*
+let entries = ( Syslog.empty | Syslog.comment | entry | macro | config_object | prop_filter )*
let lns = entries . ( Syslog.program | Syslog.hostname )*
. excl "/etc/default/rmt"
. excl "/etc/default/star"
. excl "/etc/default/whoopsie"
+ . incl "/etc/profile"
+ . incl "/etc/profile.d/*"
let filter_misc = incl "/etc/arno-iptables-firewall/debconf.cfg"
. incl "/etc/conf.d/*"
. incl "/etc/cron-apt/config"
. incl "/etc/cvs-pserver.conf"
. incl "/etc/devscripts.conf"
. incl "/etc/kamailio/kamctlrc"
+ . incl "/etc/lbu/lbu.conf"
. incl "/etc/lintianrc"
. incl "/etc/lsb-release"
. incl "/etc/os-release"
. incl "/etc/ucf.conf"
. incl "/etc/locale.conf"
. incl "/etc/vconsole.conf"
+ . incl "/etc/byobu/*"
let filter = filter_sysconfig
. filter_default
test Httpd.lns get "<FilesMatch \ test\.php$></FilesMatch>\n" =
{ "FilesMatch"
{ "arg" = "\ test\.php$" } }
+
+(* Continuations in comments cause the comment to be continued without a new comment character *)
+test Httpd.lns get "#ServerRoot \\\n /var/www\n" =
+ { "#comment" = "ServerRoot \\\n /var/www" }
{ "action" = "nopeer" }
{ "action" = "noquery" } }
+ test Ntp.lns put
+ "restrict default kod nomodify notrap nopeer noquery\n"
+ after
+ insb "ipv4" "restrict/action[1]" =
+ "restrict -4 default kod nomodify notrap nopeer noquery\n"
+
+ test Ntp.lns get
+ "restrict -4 default notrap nomodify nopeer noquery\n" =
+ { "restrict" = "default"
+ { "ipv4" }
+ { "action" = "notrap" }
+ { "action" = "nomodify" }
+ { "action" = "nopeer" }
+ { "action" = "noquery" } }
+
test Ntp.lns get
"includefile /etc/ntp/crypto/pw\n" =
{ "includefile" = "/etc/ntp/crypto/pw" }
--- /dev/null
+(*
+Module: Test_Postfix_Passwordmap
+ Provides unit tests and examples for the <Postfix_Passwordmap> lens.
+*)
+
+module Test_Postfix_Passwordmap =
+
+(* View: conf *)
+let conf = "# comment
+* username:password
+[mail.isp.example] username:password
+[mail.isp.example]:submission username:password
+[mail.isp.example]:587 username:password
+mail.isp.example username:password
+user@mail.isp.example username:
+mail.isp.example
+ username2:password2
+"
+
+(* Test: Postfix_Passwordmap.lns *)
+test Postfix_Passwordmap.lns get conf =
+ { "#comment" = "comment" }
+ { "pattern" = "*"
+ { "username" = "username" }
+ { "password" = "password" } }
+ { "pattern" = "[mail.isp.example]"
+ { "username" = "username" }
+ { "password" = "password" } }
+ { "pattern" = "[mail.isp.example]:submission"
+ { "username" = "username" }
+ { "password" = "password" } }
+ { "pattern" = "[mail.isp.example]:587"
+ { "username" = "username" }
+ { "password" = "password" } }
+ { "pattern" = "mail.isp.example"
+ { "username" = "username" }
+ { "password" = "password" } }
+ { "pattern" = "user@mail.isp.example"
+ { "username" = "username" }
+ { "password" } }
+ { "pattern" = "mail.isp.example"
+ { "username" = "username2" }
+ { "password" = "password2" } }
$ModLoad imuxsock # provides support for local system logging (e.g. via logger command)
$ModLoad imklog # provides kernel logging support (previously done by rklogd)
+module(load=\"immark\") #provides --MARK-- message capability
+
+timezone(id=\"CET\" offset=\"+01:00\")
+
$UDPServerRun 514
$InputTCPServerRun 514
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
{ "$ModLoad" = "imklog"
{ "#comment" = "provides kernel logging support (previously done by rklogd)" }
}
+ { "module"
+ { "load" = "immark" }
+ { "#comment" = "provides --MARK-- message capability" }
+ }
+ { }
+ { "timezone"
+ { "id" = "CET" }
+ { "offset" = "+01:00" }
+ }
+ { }
{ "$UDPServerRun" = "514" }
{ "$InputTCPServerRun" = "514" }
{ "$ActionFileDefaultTemplate" = "RSYSLOG_TraditionalFileFormat" }
pam_service_name=vsftpd
userlist_enable=YES
tcp_wrappers=YES
+allow_writeable_chroot=YES
"
{ "pam_service_name" = "vsftpd" }
{ "userlist_enable" = "YES" }
{ "tcp_wrappers" = "YES" }
+ { "allow_writeable_chroot" = "YES" }
{}
let empty = Util.empty
let comment = Util.comment
-let bool_option_re = /anonymous_enable|isolate|isolate_network|local_enable|pasv_enable|port_enable|chroot_local_user|write_enable|anon_upload_enable|anon_mkdir_write_enable|anon_other_write_enable|chown_uploads|connect_from_port_20|xferlog_enable|dirmessage_enable|anon_world_readable_only|async_abor_enable|ascii_upload_enable|ascii_download_enable|one_process_model|xferlog_std_format|pasv_promiscuous|deny_email_enable|chroot_list_enable|setproctitle_enable|text_userdb_names|ls_recurse_enable|log_ftp_protocol|guest_enable|userlist_enable|userlist_deny|use_localtime|check_shell|hide_ids|listen|port_promiscuous|passwd_chroot_enable|no_anon_password|tcp_wrappers|use_sendfile|force_dot_files|listen_ipv6|dual_log_enable|syslog_enable|background|virtual_use_local_privs|session_support|download_enable|dirlist_enable|chmod_enable|secure_email_list_enable|run_as_launching_user|no_log_lock|ssl_enable|allow_anon_ssl|force_local_logins_ssl|force_local_data_ssl|ssl_sslv2|ssl_sslv3|ssl_tlsv1|tilde_user_enable|force_anon_logins_ssl|force_anon_data_ssl|mdtm_write|lock_upload_files|pasv_addr_resolve|debug_ssl|require_cert|validate_cert|require_ssl_reuse/
+let bool_option_re = /anonymous_enable|isolate|isolate_network|local_enable|pasv_enable|port_enable|chroot_local_user|write_enable|anon_upload_enable|anon_mkdir_write_enable|anon_other_write_enable|chown_uploads|connect_from_port_20|xferlog_enable|dirmessage_enable|anon_world_readable_only|async_abor_enable|ascii_upload_enable|ascii_download_enable|one_process_model|xferlog_std_format|pasv_promiscuous|deny_email_enable|chroot_list_enable|setproctitle_enable|text_userdb_names|ls_recurse_enable|log_ftp_protocol|guest_enable|userlist_enable|userlist_deny|use_localtime|check_shell|hide_ids|listen|port_promiscuous|passwd_chroot_enable|no_anon_password|tcp_wrappers|use_sendfile|force_dot_files|listen_ipv6|dual_log_enable|syslog_enable|background|virtual_use_local_privs|session_support|download_enable|dirlist_enable|chmod_enable|secure_email_list_enable|run_as_launching_user|no_log_lock|ssl_enable|allow_anon_ssl|force_local_logins_ssl|force_local_data_ssl|ssl_sslv2|ssl_sslv3|ssl_tlsv1|tilde_user_enable|force_anon_logins_ssl|force_anon_data_ssl|mdtm_write|lock_upload_files|pasv_addr_resolve|debug_ssl|require_cert|validate_cert|require_ssl_reuse|allow_writeable_chroot/
let uint_option_re = /accept_timeout|connect_timeout|local_umask|anon_umask|ftp_data_port|idle_session_timeout|data_connection_timeout|pasv_min_port|pasv_max_port|anon_max_rate|local_max_rate|listen_port|max_clients|file_open_mode|max_per_ip|trans_chunk_size|delay_failed_login|delay_successful_login|max_login_fails|chown_upload_mode/
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
+.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.31)
.\"
.\" Standard preamble:
.\" ========================================================================
.\" ========================================================================
.\"
.IX Title "AUGTOOL 1"
-.TH AUGTOOL 1 "2015-10-03" "Augeas 1.4.0" "Augeas"
+.TH AUGTOOL 1 "2016-08-05" "Augeas 1.5.0" "Augeas"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.IX Item "-t, --transform=XFM"
Add a file transform; uses the 'transform' command syntax,
e.g. \f(CW\*(C`\-t \*(AqFstab incl /etc/fstab.bak\*(Aq\*(C'\fR.
+.IP "\fB\-l\fR, \fB\-\-load\-file\fR=\fI\s-1FILE\s0\fR" 4
+.IX Item "-l, --load-file=FILE"
+Load an invididual \s-1FILE\s0 into the tree. The lens to use is determined
+automatically (based on autoload information in the lenses) and will be the
+same that is used for this file when the entire tree is loaded. The option
+can be specified multiple times to load several files, e.g. \f(CW\*(C`\-l /etc/fstab
+\&\-l /etc/hosts\*(C'\fR. This lens implies \f(CW\*(C`\-\-noload\*(C'\fR so that only the files
+specified with this option will be loaded.
.IP "\fB\-f\fR, \fB\-\-file\fR=\fI\s-1FILE\s0\fR" 4
.IX Item "-f, --file=FILE"
Read commands from \s-1FILE.\s0
assumed. The \s-1FILTER\s0 must be either \*(L"incl\*(R" or \*(L"excl\*(R". If the filter is
\&\*(L"incl\*(R", the \s-1FILE\s0 will be parsed by the \s-1LENS. \s0 If the filter is \*(L"excl\*(R",
the \s-1FILE\s0 will be excluded from the \s-1LENS. FILE\s0 may contain wildcards.
+.IP "\fBload-file\fR <\s-1FILE\s0>" 4
+.IX Item "load-file <FILE>"
+Load a specific \s-1FILE,\s0 automatically determining the proper lens from the
+information in \fI/augeas/load\fR; without further intervention, the lens that
+would oridnarily be used for this file will be used.
.SS "\s-1READ COMMANDS\s0"
.IX Subsection "READ COMMANDS"
The following commands are used to retrieve data from the Augeas tree.
Add a file transform; uses the 'transform' command syntax,
e.g. C<-t 'Fstab incl /etc/fstab.bak'>.
+=item B<-l>, B<--load-file>=I<FILE>
+
+Load an invididual FILE into the tree. The lens to use is determined
+automatically (based on autoload information in the lenses) and will be the
+same that is used for this file when the entire tree is loaded. The option
+can be specified multiple times to load several files, e.g. C<-l /etc/fstab
+-l /etc/hosts>. This lens implies C<--noload> so that only the files
+specified with this option will be loaded.
+
=item B<-f>, B<--file>=I<FILE>
Read commands from FILE.
"incl", the FILE will be parsed by the LENS. If the filter is "excl",
the FILE will be excluded from the LENS. FILE may contain wildcards.
+=item B<load-file> E<lt>FILEE<gt>
+
+Load a specific FILE, automatically determining the proper lens from the
+information in F</augeas/load>; without further intervention, the lens that
+would oridnarily be used for this file will be used.
+
=back
=head2 READ COMMANDS
list_for_each(xfm, load->children) {
if (transform_validate(aug, xfm) == 0)
- transform_load(aug, xfm);
+ transform_load(aug, xfm, NULL);
}
/* This makes it possible to spot 'directories' that are now empty
return result;
}
+int aug_load_file(struct augeas *aug, const char *file) {
+ int result = -1;
+ struct tree *meta = tree_child_cr(aug->origin, s_augeas);
+ struct tree *load = tree_child_cr(meta, s_load);
+ char *tree_path = NULL;
+ bool found = false;
+
+ api_entry(aug);
+
+ ERR_NOMEM(load == NULL, aug);
+
+ list_for_each(xfm, load->children) {
+ if (filter_matches(xfm, file)) {
+ transform_load(aug, xfm, file);
+ found = true;
+ break;
+ }
+ }
+
+ ERR_THROW(!found, aug, AUG_ENOLENS,
+ "can not determine lens to load file %s", file);
+
+ /* Mark the nodes we just loaded as clean so they won't get saved
+ without additional modifications */
+ xasprintf(&tree_path, "/files/%s", file);
+ struct tree *t = tree_fpath(aug, tree_path);
+ if (t != NULL) {
+ tree_clean(t);
+ }
+
+ result = 0;
+error:
+ api_exit(aug);
+ free(tree_path);
+ return result;
+}
+
int aug_print(const struct augeas *aug, FILE *out, const char *pathin) {
struct pathx *p;
int result;
int aug_transform(augeas *aug, const char *lens, const char *file, int excl);
/*
+ * Function: aug_load_file
+ *
+ * Load a FILE using the lens that would ordinarily be used by aug_load,
+ * i.e. the lens whose autoload statement matches the FILE. Similar to
+ * aug_load, this function returns successfully even if FILE does not exist
+ * or if the FILE can not be processed by the associated lens. It is an
+ * error though if no lens can be found to process FILE. In that case, the
+ * error code in AUG will be set to AUG_ENOLENS.
+ *
+ * Returns:
+ * 0 on success, -1 on failure
+ */
+int aug_load_file(augeas *aug, const char *file);
+
+/*
* Function: aug_srun
*
* Run one or more newline-separated commands. The output of the commands
global:
aug_escape_name;
} AUGEAS_0.18.0;
+
+AUGEAS_0.21.0 {
+ global:
+ aug_load_file;
+} AUGEAS_0.20.0;
.help = cmd_transform_help
};
+static void cmd_load_file(struct command *cmd) {
+ const char *file = arg_value(cmd, "file");
+ int r = 0;
+
+ r = aug_load_file(cmd->aug, file);
+ if (r < 0)
+ ERR_REPORT(cmd, AUG_ECMDRUN,
+ "Failed to load file %s", file);
+}
+
+static const struct command_opt_def cmd_load_file_opts[] = {
+ { .type = CMD_PATH, .name = "file", .optional = false,
+ .help = "the file to load" },
+ CMD_OPT_DEF_LAST
+};
+
+static const char const cmd_load_file_help[] =
+ "Load a specific FILE, using autoload statements.\n";
+
+static const struct command_def cmd_load_file_def = {
+ .name = "load-file",
+ .opts = cmd_load_file_opts,
+ .handler = cmd_load_file,
+ .synopsis = "load a specific file",
+ .help = cmd_load_file_help
+};
+
static void cmd_save(struct command *cmd) {
int r;
r = aug_save(cmd->aug);
&cmd_save_def,
&cmd_store_def,
&cmd_transform_def,
+ &cmd_load_file_def,
&cmd_def_last
}
};
const char *root = NULL;
char *loadpath = NULL;
char *transforms = NULL;
+char *loadonly = NULL;
size_t transformslen = 0;
+size_t loadonlylen = 0;
const char *inputfile = NULL;
int echo_commands = 0; /* Gets also changed in main_loop */
bool print_version = false;
"quit", "clear", "defnode", "defvar",
"get", "label", "ins", "load", "ls", "match",
"mv", "cp", "rename", "print", "dump-xml", "rm", "save", "set", "setm",
- "clearm", "span", "store", "retrieve", "transform",
+ "clearm", "span", "store", "retrieve", "transform", "load-file",
"help", "touch", "insert", "move", "copy", "errors", NULL };
static int current = 0;
fprintf(stderr, "Run '%s help' to get a list of possible commands.\n",
progname);
fprintf(stderr, "\nOptions:\n\n");
- fprintf(stderr, " -c, --typecheck typecheck lenses\n");
- fprintf(stderr, " -b, --backup preserve originals of modified files with\n"
- " extension '.augsave'\n");
- fprintf(stderr, " -n, --new save changes in files with extension '.augnew',\n"
- " leave original unchanged\n");
- fprintf(stderr, " -r, --root ROOT use ROOT as the root of the filesystem\n");
- fprintf(stderr, " -I, --include DIR search DIR for modules; can be given multiple times\n");
- fprintf(stderr, " -t, --transform XFM add a file transform; uses the 'transform' command\n"
- " syntax, e.g. -t 'Fstab incl /etc/fstab.bak'\n");
- fprintf(stderr, " -e, --echo echo commands when reading from a file\n");
- fprintf(stderr, " -f, --file FILE read commands from FILE\n");
- fprintf(stderr, " -s, --autosave automatically save at the end of instructions\n");
- fprintf(stderr, " -i, --interactive run an interactive shell after evaluating\n"
- " the commands in STDIN and FILE\n");
- fprintf(stderr, " -S, --nostdinc do not search the builtin default directories\n"
- " for modules\n");
- fprintf(stderr, " -L, --noload do not load any files into the tree on startup\n");
- fprintf(stderr, " -A, --noautoload do not autoload modules from the search path\n");
- fprintf(stderr, " --span load span positions for nodes related to a file\n");
- fprintf(stderr, " --timing after executing each command, show how long it took\n");
- fprintf(stderr, " --version print version information and exit.\n");
+ fprintf(stderr, " -c, --typecheck typecheck lenses\n");
+ fprintf(stderr, " -b, --backup preserve originals of modified files with\n"
+ " extension '.augsave'\n");
+ fprintf(stderr, " -n, --new save changes in files with extension '.augnew',\n"
+ " leave original unchanged\n");
+ fprintf(stderr, " -r, --root ROOT use ROOT as the root of the filesystem\n");
+ fprintf(stderr, " -I, --include DIR search DIR for modules; can be given multiple times\n");
+ fprintf(stderr, " -t, --transform XFM add a file transform; uses the 'transform' command\n"
+ " syntax, e.g. -t 'Fstab incl /etc/fstab.bak'\n");
+ fprintf(stderr, " -l, --load-file FILE load individual FILE in the tree\n");
+ fprintf(stderr, " -e, --echo echo commands when reading from a file\n");
+ fprintf(stderr, " -f, --file FILE read commands from FILE\n");
+ fprintf(stderr, " -s, --autosave automatically save at the end of instructions\n");
+ fprintf(stderr, " -i, --interactive run an interactive shell after evaluating\n"
+ " the commands in STDIN and FILE\n");
+ fprintf(stderr, " -S, --nostdinc do not search the builtin default directories\n"
+ " for modules\n");
+ fprintf(stderr, " -L, --noload do not load any files into the tree on startup\n");
+ fprintf(stderr, " -A, --noautoload do not autoload modules from the search path\n");
+ fprintf(stderr, " --span load span positions for nodes related to a file\n");
+ fprintf(stderr, " --timing after executing each command, show how long it took\n");
+ fprintf(stderr, " --version print version information and exit.\n");
exit(EXIT_FAILURE);
}
{ "root", 1, 0, 'r' },
{ "include", 1, 0, 'I' },
{ "transform", 1, 0, 't' },
+ { "load-file", 1, 0, 'l' },
{ "echo", 0, 0, 'e' },
{ "file", 1, 0, 'f' },
{ "autosave", 0, 0, 's' },
};
int idx;
- while ((opt = getopt_long(argc, argv, "hnbcr:I:t:ef:siSLA", options, &idx)) != -1) {
+ while ((opt = getopt_long(argc, argv, "hnbcr:I:t:l:ef:siSLA", options, &idx)) != -1) {
switch(opt) {
case 'c':
flags |= AUG_TYPE_CHECK;
case 't':
argz_add(&transforms, &transformslen, optarg);
break;
+ case 'l':
+ // --load-file implies --noload
+ flags |= AUG_NO_LOAD;
+ argz_add(&loadonly, &loadonlylen, optarg);
+ break;
case 'e':
echo_commands = 1;
break;
}
}
+static void load_files(char *ts, size_t tslen) {
+ char *command;
+ int r;
+ char *t = NULL;
+
+ while ((t = argz_next(ts, tslen, t))) {
+ r = xasprintf(&command, "load-file %s", t);
+ if (r < 0)
+ fprintf(stderr, "error: Failed to load file %s: could not allocate memory\n", t);
+
+ r = aug_srun(aug, stdout, command);
+ if (r < 0)
+ fprintf(stderr, "error: Failed to load file %s: %s\n", t, aug_error_message(aug));
+
+ free(command);
+ }
+}
+
int main(int argc, char **argv) {
int r;
struct timeval start, stop;
print_aug_error();
exit(EXIT_FAILURE);
}
+ load_files(loadonly, loadonlylen);
add_transforms(transforms, transformslen);
if (print_version) {
print_version_info();
continue;
struct state *r = add_state(fa, 0);
+ if (r == NULL)
+ goto error;
r->trans = p->trans;
r->tused = p->tused;
r->tsize = p->tsize;
len += strlen(*path) + 1;
if (REALLOC_N(*path, len) == -1) {
FREE(*path);
+ va_end(ap);
return -1;
}
if (strlen(*path) == 0 || (*path)[strlen(*path)-1] != SEP)
seg += 1;
strcat(*path, seg);
} else {
- if ((*path = malloc(len)) == NULL)
+ if ((*path = malloc(len)) == NULL) {
+ va_end(ap);
return -1;
+ }
strcpy(*path, seg);
}
}
enum type tag;
union {
struct nodeset *nodeset; /* T_NODESET */
- int number; /* T_NUMBER */
+ int64_t number; /* T_NUMBER */
char *string; /* T_STRING */
bool boolval; /* T_BOOLEAN */
struct regexp *regexp; /* T_REGEXP */
goto done;
}
-static int filter_matches(struct tree *xfm, const char *path) {
+int filter_matches(struct tree *xfm, const char *path) {
int found = 0;
list_for_each(f, xfm->children) {
if (is_incl(f) && fnmatch_normalize(f->value, path, fnm_flags) == 0) {
return result;
}
-int transform_load(struct augeas *aug, struct tree *xfm) {
+int transform_load(struct augeas *aug, struct tree *xfm, const char *file) {
int nmatches = 0;
char **matches;
const char *lens_name;
// FIXME: Record an error and return 0
return -1;
}
+
r = filter_generate(xfm, aug->root, &nmatches, &matches);
if (r == -1)
return -1;
for (int i=0; i < nmatches; i++) {
const char *filename = matches[i] + strlen(aug->root) - 1;
struct tree *finfo = file_info(aug, filename);
+
+ if (file != NULL && STRNEQ(filename, file))
+ continue;
+
if (finfo != NULL && !finfo->dirty &&
tree_child(finfo, s_lens) != NULL) {
const char *s = xfm_lens_name(finfo);
* applying the TRANSFORM's lens to their contents and putting the
* resulting tree under "/files" + filename. Also stores some information
* about filename underneath "/augeas/files" + filename
+ * If a FILE is passed, only this FILE will be loaded.
*/
-int transform_load(struct augeas *aug, struct tree *xfm);
+int transform_load(struct augeas *aug, struct tree *xfm, const char *file);
+
+/* Return 1 if TRANSFORM applies to PATH, 0 otherwise.
+ * PATH must not include "/files/".
+ */
+int filter_matches(struct tree *xfm, const char *path);
/* Return 1 if TRANSFORM applies to PATH, 0 otherwise. The TRANSFORM
* applies to PATH if (1) PATH starts with "/files/" and (2) the rest of
lens-postfix_access.sh \
lens-postfix_main.sh \
lens-postfix_master.sh \
+ lens-postfix_passwordmap.sh \
lens-postfix_sasl_smtpd.sh \
lens-postfix_transport.sh \
lens-postfix_virtual.sh \
CuAssertIntEquals(tc, 5, r);
}
+static void testLoadFile(CuTest *tc) {
+ struct augeas *aug;
+ const char *value;
+ int r;
+
+ aug = aug_init(root, loadpath, AUG_NO_STDINC|AUG_NO_LOAD);
+ CuAssertPtrNotNull(tc, aug);
+ CuAssertIntEquals(tc, AUG_NOERROR, aug_error(aug));
+
+ /* augeas should load a single file */
+ r = aug_load_file(aug, "/etc/fstab");
+ CuAssertRetSuccess(tc, r);
+ r = aug_get(aug, "/files/etc/fstab/1/vfstype", &value);
+ CuAssertIntEquals(tc, 1, r);
+ CuAssertPtrNotNull(tc, value);
+
+ /* Only one file should be loaded */
+ r = aug_match(aug, "/files/etc/*", NULL);
+ CuAssertIntEquals(tc, 1, r);
+
+ /* augeas should return an error when no lens can be found for a file */
+ r = aug_load_file(aug, "/etc/unknown.conf");
+ CuAssertIntEquals(tc, -1, r);
+ CuAssertIntEquals(tc, AUG_ENOLENS, aug_error(aug));
+
+ /* augeas should return without an error when trying to load a
+ nonexistant file that would be handled by a lens */
+ r = aug_load_file(aug, "/etc/mtab");
+ CuAssertRetSuccess(tc, r);
+ r = aug_match(aug, "/files/etc/mtab", NULL);
+ CuAssertIntEquals(tc, 0, r);
+}
+
int main(void) {
char *output = NULL;
CuSuite* suite = CuSuiteNew();
SUITE_ADD_TEST(suite, testTextRetrieve);
SUITE_ADD_TEST(suite, testAugEscape);
SUITE_ADD_TEST(suite, testRm);
+ SUITE_ADD_TEST(suite, testLoadFile);
abs_top_srcdir = getenv("abs_top_srcdir");
if (abs_top_srcdir == NULL)
account include\tsystem-auth
password include\tsystem-auth
session required\tpam_namespace.so unmnt_remnt no_unmount_on_close
-+auth\tinclude\tsystem-auth'
++auth include system-auth'
#%PAM-1.0
auth include\tsystem-auth
account include\tsystem-auth
-+session\tinclude\tsystem-auth
++session include system-auth
password include\tsystem-auth
session required\tpam_namespace.so unmnt_remnt no_unmount_on_close'
auth include\tsystem-auth
account include\tsystem-auth
-password include\tsystem-auth
-+password include\tother_module\tother_module_opts
++password include\tother_module other_module_opts
session required\tpam_namespace.so unmnt_remnt no_unmount_on_close'