[pacman-dev] [PATCH 0/4] add pacman-conf utility
Parsing pacman.conf is so hard that even our own scripts don't do it correctly. pacman-conf is a dedicated utility scripts can use to correctly parse values from pacman.conf. This is a conversion of an earlier version of pacconf from pacutils that has been relicensed and renamed to avoid a file conflict. I make no promises that I've correctly adjusted the automake configuration for this type of build, but it works for me. Andrew Gregory (4): extract raw config file parser extract default settings to separate function add pacman-conf utility use pacman-conf in scripts scripts/completion/zsh_completion.in | 4 +- scripts/pacman-db-upgrade.sh.in | 21 +- scripts/pacman-key.sh.in | 2 +- src/pacman/conf.c | 179 +++++++------- src/pacman/conf.h | 2 + src/util/.gitignore | 2 + src/util/Makefile.am | 21 +- src/util/pacman-conf.c | 437 +++++++++++++++++++++++++++++++++++ 8 files changed, 562 insertions(+), 106 deletions(-) create mode 100644 src/util/pacman-conf.c -- 2.15.1
To allow pacman-conf to parse the configuration file without having to also setup alpm. Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- src/pacman/conf.c | 13 +++++++++---- src/pacman/conf.h | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/pacman/conf.c b/src/pacman/conf.c index a283d7f4..e4980ed2 100644 --- a/src/pacman/conf.c +++ b/src/pacman/conf.c @@ -1036,6 +1036,14 @@ static int _parse_directive(const char *file, int linenum, const char *name, } } +int parseconfigfile(const char *file) +{ + struct section_t section; + memset(§ion, 0, sizeof(struct section_t)); + pm_printf(ALPM_LOG_DEBUG, "config: attempting to read file %s\n", file); + return parse_ini(file, _parse_directive, §ion); +} + /** Parse a configuration file. * @param file path to the config file * @return 0 on success, non-zero on error @@ -1043,10 +1051,7 @@ static int _parse_directive(const char *file, int linenum, const char *name, int parseconfig(const char *file) { int ret; - struct section_t section; - memset(§ion, 0, sizeof(struct section_t)); - pm_printf(ALPM_LOG_DEBUG, "config: attempting to read file %s\n", file); - if((ret = parse_ini(file, _parse_directive, §ion))) { + if((ret = parseconfigfile(file))) { return ret; } pm_printf(ALPM_LOG_DEBUG, "config: finished parsing %s\n", file); diff --git a/src/pacman/conf.h b/src/pacman/conf.h index 53b44be6..e937051b 100644 --- a/src/pacman/conf.h +++ b/src/pacman/conf.h @@ -242,6 +242,7 @@ void config_repo_free(config_repo_t *repo); int config_set_arch(const char *arch); int parseconfig(const char *file); +int parseconfigfile(const char *file); #endif /* PM_CONF_H */ /* vim: set noet: */ -- 2.15.1
Default values for configuration settings were being set during alpm setup and in some cases were never saved back to the original config struct. Refactoring all default settings into a separate function and saving them onto the original config struct will allow pacman-conf to resolve the defaults without having to setup alpm. Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- src/pacman/conf.c | 166 +++++++++++++++++++++++++++++------------------------- src/pacman/conf.h | 1 + 2 files changed, 89 insertions(+), 78 deletions(-) diff --git a/src/pacman/conf.c b/src/pacman/conf.c index e4980ed2..1ad75405 100644 --- a/src/pacman/conf.c +++ b/src/pacman/conf.c @@ -616,37 +616,39 @@ static int _parse_options(const char *key, char *value, return 0; } -static int _add_mirror(alpm_db_t *db, char *value) +static char *replace_server_vars(config_t *c, config_repo_t *r, const char *s) { - const char *dbname = alpm_db_get_name(db); - /* let's attempt a replacement for the current repo */ - char *temp = strreplace(value, "$repo", dbname); - /* let's attempt a replacement for the arch */ - const char *arch = config->arch; - char *server; - if(arch) { - server = strreplace(temp, "$arch", arch); + if(c->arch == NULL && strstr(s, "$arch")) { + pm_printf(ALPM_LOG_ERROR, + _("mirror '%s' contains the '%s' variable, but no '%s' is defined.\n"), + s, "$arch", "Architecture"); + return NULL; + } + + if(c->arch) { + char *temp, *replaced; + + replaced = strreplace(s, "$arch", c->arch); + + temp = replaced; + replaced = strreplace(temp, "$repo", r->name); free(temp); + + return replaced; } else { - if(strstr(temp, "$arch")) { - free(temp); - pm_printf(ALPM_LOG_ERROR, - _("mirror '%s' contains the '%s' variable, but no '%s' is defined.\n"), - value, "$arch", "Architecture"); - return 1; - } - server = temp; + return strreplace(s, "$repo", r->name); } +} - if(alpm_db_add_server(db, server) != 0) { +static int _add_mirror(alpm_db_t *db, char *value) +{ + if(alpm_db_add_server(db, value) != 0) { /* pm_errno is set by alpm_db_setserver */ pm_printf(ALPM_LOG_ERROR, _("could not add server URL to database '%s': %s (%s)\n"), - dbname, server, alpm_strerror(alpm_errno(config->handle))); - free(server); + alpm_db_get_name(db), value, alpm_strerror(alpm_errno(config->handle))); return 1; } - free(server); return 0; } @@ -655,9 +657,6 @@ static int register_repo(config_repo_t *repo) alpm_list_t *i; alpm_db_t *db; - repo->siglevel = merge_siglevel(config->siglevel, - repo->siglevel, repo->siglevel_mask); - db = alpm_register_syncdb(config->handle, repo->name, repo->siglevel); if(db == NULL) { pm_printf(ALPM_LOG_ERROR, _("could not register '%s' database (%s)\n"), @@ -665,18 +664,12 @@ static int register_repo(config_repo_t *repo) return 1; } - pm_printf(ALPM_LOG_DEBUG, - "setting usage of %d for %s repository\n", - repo->usage == 0 ? ALPM_DB_USAGE_ALL : repo->usage, - repo->name); - alpm_db_set_usage(db, repo->usage == 0 ? ALPM_DB_USAGE_ALL : repo->usage); + pm_printf(ALPM_LOG_DEBUG, "setting usage of %d for %s repository\n", + repo->usage, repo->name); + alpm_db_set_usage(db, repo->usage); for(i = repo->servers; i; i = alpm_list_next(i)) { - char *value = i->data; - if(_add_mirror(db, value) != 0) { - pm_printf(ALPM_LOG_ERROR, - _("could not add mirror '%s' to database '%s' (%s)\n"), - value, repo->name, alpm_strerror(alpm_errno(config->handle))); + if(_add_mirror(db, i->data) != 0) { return 1; } } @@ -699,25 +692,6 @@ static int setup_libalpm(void) pm_printf(ALPM_LOG_DEBUG, "setup_libalpm called\n"); - /* Configure root path first. If it is set and dbpath/logfile were not - * set, then set those as well to reside under the root. */ - if(config->rootdir) { - char path[PATH_MAX]; - if(!config->dbpath) { - snprintf(path, PATH_MAX, "%s/%s", config->rootdir, DBPATH + 1); - config->dbpath = strdup(path); - } - if(!config->logfile) { - snprintf(path, PATH_MAX, "%s/%s", config->rootdir, LOGFILE + 1); - config->logfile = strdup(path); - } - } else { - config->rootdir = strdup(ROOTDIR); - if(!config->dbpath) { - config->dbpath = strdup(DBPATH); - } - } - /* initialize library */ handle = alpm_initialize(config->rootdir, config->dbpath, &err); if(!handle) { @@ -740,7 +714,6 @@ static int setup_libalpm(void) alpm_option_set_dbext(handle, ".files"); } - config->logfile = config->logfile ? config->logfile : strdup(LOGFILE); ret = alpm_option_set_logfile(handle, config->logfile); if(ret != 0) { pm_printf(ALPM_LOG_ERROR, _("problem setting logfile '%s' (%s)\n"), @@ -750,7 +723,6 @@ static int setup_libalpm(void) /* Set GnuPG's home directory. This is not relative to rootdir, even if * rootdir is defined. Reasoning: gpgdir contains configuration data. */ - config->gpgdir = config->gpgdir ? config->gpgdir : strdup(GPGDIR); ret = alpm_option_set_gpgdir(handle, config->gpgdir); if(ret != 0) { pm_printf(ALPM_LOG_ERROR, _("problem setting gpgdir '%s' (%s)\n"), @@ -760,39 +732,21 @@ static int setup_libalpm(void) /* Set user hook directory. This is not relative to rootdir, even if * rootdir is defined. Reasoning: hookdir contains configuration data. */ - if(config->hookdirs == NULL) { - if((ret = alpm_option_add_hookdir(handle, HOOKDIR)) != 0) { + /* add hook directories 1-by-1 to avoid overwriting the system directory */ + for(i = config->hookdirs; i; i = alpm_list_next(i)) { + if((ret = alpm_option_add_hookdir(handle, i->data)) != 0) { pm_printf(ALPM_LOG_ERROR, _("problem adding hookdir '%s' (%s)\n"), - HOOKDIR, alpm_strerror(alpm_errno(handle))); + (char *) i->data, alpm_strerror(alpm_errno(handle))); return ret; } - } else { - /* add hook directories 1-by-1 to avoid overwriting the system directory */ - for(i = config->hookdirs; i; i = alpm_list_next(i)) { - if((ret = alpm_option_add_hookdir(handle, i->data)) != 0) { - pm_printf(ALPM_LOG_ERROR, _("problem adding hookdir '%s' (%s)\n"), - (char *) i->data, alpm_strerror(alpm_errno(handle))); - return ret; - } - } } - /* add a default cachedir if one wasn't specified */ - if(config->cachedirs == NULL) { - alpm_option_add_cachedir(handle, CACHEDIR); - } else { - alpm_option_set_cachedirs(handle, config->cachedirs); - } + alpm_option_set_cachedirs(handle, config->cachedirs); alpm_option_set_overwrite_files(handle, config->overwrite_files); alpm_option_set_default_siglevel(handle, config->siglevel); - config->localfilesiglevel = merge_siglevel(config->siglevel, - config->localfilesiglevel, config->localfilesiglevel_mask); - config->remotefilesiglevel = merge_siglevel(config->siglevel, - config->remotefilesiglevel, config->remotefilesiglevel_mask); - alpm_option_set_local_file_siglevel(handle, config->localfilesiglevel); alpm_option_set_remote_file_siglevel(handle, config->remotefilesiglevel); @@ -1036,6 +990,59 @@ static int _parse_directive(const char *file, int linenum, const char *name, } } +int setdefaults(config_t *c) +{ + alpm_list_t *i; + +#define SETDEFAULT(opt, val) if(!opt) { opt = val; if(!opt) { return -1; } } + + if(c->rootdir) { + char path[PATH_MAX]; + if(!c->dbpath) { + snprintf(path, PATH_MAX, "%s/%s", c->rootdir, DBPATH + 1); + SETDEFAULT(c->dbpath, strdup(path)); + } + if(!c->logfile) { + snprintf(path, PATH_MAX, "%s/%s", c->rootdir, LOGFILE + 1); + SETDEFAULT(c->logfile, strdup(path)); + } + } else { + SETDEFAULT(c->rootdir, strdup(ROOTDIR)); + SETDEFAULT(c->dbpath, strdup(DBPATH)); + } + + SETDEFAULT(c->logfile, strdup(LOGFILE)); + SETDEFAULT(c->gpgdir, strdup(GPGDIR)); + SETDEFAULT(c->cachedirs, alpm_list_add(NULL, strdup(CACHEDIR))); + SETDEFAULT(c->hookdirs, alpm_list_add(NULL, strdup(HOOKDIR))); + SETDEFAULT(c->cleanmethod, PM_CLEAN_KEEPINST); + + c->localfilesiglevel = merge_siglevel(c->siglevel, + c->localfilesiglevel, c->localfilesiglevel_mask); + c->remotefilesiglevel = merge_siglevel(c->siglevel, + c->remotefilesiglevel, c->remotefilesiglevel_mask); + + for(i = c->repos; i; i = i->next) { + config_repo_t *r = i->data; + alpm_list_t *j; + SETDEFAULT(r->usage, ALPM_DB_USAGE_ALL); + r->siglevel = merge_siglevel(c->siglevel, r->siglevel, r->siglevel_mask); + for(j = r->servers; j; j = j->next) { + char *newurl = replace_server_vars(c, r, j->data); + if(newurl == NULL) { + return -1; + } else { + free(j->data); + j->data = newurl; + } + } + } + +#undef SETDEFAULT + + return 0; +} + int parseconfigfile(const char *file) { struct section_t section; @@ -1054,6 +1061,9 @@ int parseconfig(const char *file) if((ret = parseconfigfile(file))) { return ret; } + if((ret = setdefaults(config))) { + return ret; + } pm_printf(ALPM_LOG_DEBUG, "config: finished parsing %s\n", file); if((ret = setup_libalpm())) { return ret; diff --git a/src/pacman/conf.h b/src/pacman/conf.h index e937051b..786eec6e 100644 --- a/src/pacman/conf.h +++ b/src/pacman/conf.h @@ -243,6 +243,7 @@ void config_repo_free(config_repo_t *repo); int config_set_arch(const char *arch); int parseconfig(const char *file); int parseconfigfile(const char *file); +int setdefaults(config_t *c); #endif /* PM_CONF_H */ /* vim: set noet: */ -- 2.15.1
Parsing pacman's configuration file is non-trivial and extremely difficult to do correctly from scripts; even our own do it incorrectly. pacman-conf is a dedicated tool specifically to allow scripts to parse config files, getting the same value that pacman itself would use. Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- src/util/.gitignore | 2 + src/util/Makefile.am | 21 ++- src/util/pacman-conf.c | 437 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 458 insertions(+), 2 deletions(-) create mode 100644 src/util/pacman-conf.c diff --git a/src/util/.gitignore b/src/util/.gitignore index 4cb3103e..3c557cac 100644 --- a/src/util/.gitignore +++ b/src/util/.gitignore @@ -2,6 +2,8 @@ .libs cleanupdelta cleanupdelta.exe +pacman-conf +pacman-conf.exe testpkg testpkg.exe vercmp diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 562151bc..aa812b99 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -3,8 +3,10 @@ conffile = ${sysconfdir}/pacman.conf dbpath = ${localstatedir}/lib/pacman/ gpgdir = ${sysconfdir}/pacman.d/gnupg/ cachedir = ${localstatedir}/cache/pacman/pkg/ +logfile = ${localstatedir}/log/pacman.log +hookdir = ${sysconfdir}/pacman.d/hooks/ -bin_PROGRAMS = vercmp testpkg cleanupdelta +bin_PROGRAMS = vercmp testpkg cleanupdelta pacman-conf AM_CPPFLAGS = \ -imacros $(top_builddir)/config.h \ @@ -13,7 +15,9 @@ AM_CPPFLAGS = \ -DCONFFILE=\"$(conffile)\" \ -DDBPATH=\"$(dbpath)\" \ -DGPGDIR=\"$(gpgdir)\" \ - -DCACHEDIR=\"$(cachedir)\" + -DCACHEDIR=\"$(cachedir)\" \ + -DHOOKDIR=\"$(hookdir)\" \ + -DLOGFILE=\"$(logfile)\" AM_CFLAGS = -pedantic -D_GNU_SOURCE $(WARNING_CFLAGS) \ $(LIBARCHIVE_CFLAGS) @@ -21,6 +25,19 @@ AM_CFLAGS = -pedantic -D_GNU_SOURCE $(WARNING_CFLAGS) \ cleanupdelta_SOURCES = cleanupdelta.c cleanupdelta_LDADD = $(top_builddir)/lib/libalpm/.libs/libalpm.la +pacman_conf_SOURCES = pacman-conf.c \ + $(top_srcdir)/src/pacman/util.h \ + $(top_srcdir)/src/pacman/util.c \ + $(top_srcdir)/src/pacman/ini.h \ + $(top_srcdir)/src/pacman/ini.c \ + $(top_srcdir)/src/pacman/util-common.h \ + $(top_srcdir)/src/pacman/util-common.c \ + $(top_srcdir)/src/pacman/callback.h \ + $(top_srcdir)/src/pacman/callback.c \ + $(top_srcdir)/src/pacman/conf.h \ + $(top_srcdir)/src/pacman/conf.c +pacman_conf_LDADD = $(top_builddir)/lib/libalpm/.libs/libalpm.la + testpkg_SOURCES = testpkg.c testpkg_LDADD = $(top_builddir)/lib/libalpm/.libs/libalpm.la diff --git a/src/util/pacman-conf.c b/src/util/pacman-conf.c new file mode 100644 index 00000000..b2be3e13 --- /dev/null +++ b/src/util/pacman-conf.c @@ -0,0 +1,437 @@ +/* + * pacman-conf.c - parse pacman configuration files + * + * Copyright (c) 2013-2018 Pacman Development Team <pacman-dev@archlinux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 <getopt.h> +#include <string.h> +#include "../pacman/conf.h" + +const char *myname = "pacman-conf", *myver = "1.0.0"; + +alpm_list_t *directives = NULL; +char sep = '\n', *repo_name = NULL; +const char *config_file = NULL; +int repo_list = 0, verbose = 0; + +static void cleanup(void) +{ + alpm_list_free(directives); + config_free(config); +} + +static void usage(int ret) +{ + FILE *stream = (ret ? stderr : stdout); +#define hputs(x) fputs(x"\n", stream) + hputs("pacman-conf - query pacman's configuration file"); + hputs("usage: pacman-conf [options] [<directive>...]"); + hputs(" pacman-conf (--repo-list|--help|--version)"); + hputs("options:"); + hputs(" --config=<path> set an alternate configuration file"); + hputs(" --rootdir=<path> set an alternate installation root"); + hputs(" --repo=<remote> query options for a specific repo"); + hputs(" --verbose always show directive names"); + hputs(" --repo-list list configured repositories"); + hputs(" --help display this help information"); + hputs(" --version display version information"); +#undef hputs + cleanup(); + exit(ret); +} + +static void parse_opts(int argc, char **argv) +{ + int c; + config_file = CONFFILE; + + const char *short_opts = ""; + struct option long_opts[] = { + { "config" , required_argument , NULL , 'c' }, + { "rootdir" , required_argument , NULL , 'R' }, + { "repo" , required_argument , NULL , 'r' }, + { "repo-list" , no_argument , NULL , 'l' }, + { "verbose" , no_argument , NULL , 'v' }, + { "help" , no_argument , NULL , 'h' }, + { "version" , no_argument , NULL , 'V' }, + { 0, 0, 0, 0 }, + }; + + while((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { + switch(c) { + case 'c': + config_file = optarg; + break; + case 'R': + config->rootdir = strdup(optarg); + break; + case 'l': + repo_list = 1; + break; + case 'r': + repo_name = optarg; + break; + case 'v': + verbose = 1; + break; + case 'h': + usage(0); + break; + case 'V': + printf("%s v%s\n", myname, myver); + cleanup(); + exit(0); + break; + case '?': + default: + usage(1); + break; + } + } + + if(parseconfigfile(config_file) != 0 || setdefaults(config) != 0) { + fprintf(stderr, "error parsing '%s'\n", config_file); + } +} + +static void list_repos(void) +{ + alpm_list_t *r; + for(r = config->repos; r; r = r->next) { + config_repo_t *repo = r->data; + if(!repo_name || strcmp(repo->name, repo_name) == 0) { + printf("%s%c", repo->name, sep); + } + } +} + +static void show_float(const char *directive, float val) +{ + if(verbose) { + printf("%s = ", directive); + } + printf("%f%c", val, sep); +} + +static void show_bool(const char *directive, short unsigned int val) +{ + if(val) { + printf("%s%c", directive, sep); + } +} + +static void show_str(const char *directive, const char *val) +{ + if(!val) { + return; + } + if(verbose) { + printf("%s = ", directive); + } + printf("%s%c", val, sep); +} + +static void show_list_str(const char *directive, alpm_list_t *list) +{ + alpm_list_t *i; + for(i = list; i; i = i->next) { + show_str(directive, i->data); + } +} + +static void show_cleanmethod(const char *directive, unsigned int method) +{ + if(method & PM_CLEAN_KEEPINST) { + show_str(directive, "KeepInstalled"); + } + if(method & PM_CLEAN_KEEPCUR) { + show_str(directive, "KeepCurrent"); + } +} + +static void show_siglevel(const char *directive, alpm_siglevel_t level, int pkgonly) +{ + if(level == ALPM_SIG_USE_DEFAULT) { + return; + } + + if(level & ALPM_SIG_PACKAGE) { + if(level & ALPM_SIG_PACKAGE_OPTIONAL) { + show_str(directive, "PackageOptional"); + } else { + show_str(directive, "PackageRequired"); + } + + if(level & ALPM_SIG_PACKAGE_UNKNOWN_OK) { + show_str(directive, "PackageTrustAll"); + } else { + show_str(directive, "PackageTrustedOnly"); + } + } else { + show_str(directive, "PackageNever"); + } + + if(pkgonly) { + return; + } + + if(level & ALPM_SIG_DATABASE) { + if(level & ALPM_SIG_DATABASE_OPTIONAL) { + show_str(directive, "DatabaseOptional"); + } else { + show_str(directive, "DatabaseRequired"); + } + + if(level & ALPM_SIG_DATABASE_UNKNOWN_OK) { + show_str(directive, "DatabaseTrustAll"); + } else { + show_str(directive, "DatabaseTrustedOnly"); + } + } else { + show_str(directive, "DatabaseNever"); + } +} + +static void show_usage(const char *directive, alpm_db_usage_t usage) +{ + if(usage & ALPM_DB_USAGE_ALL) { + show_str(directive, "All"); + } else { + if(usage & ALPM_DB_USAGE_SYNC) { + show_str(directive, "Sync"); + } + if(usage & ALPM_DB_USAGE_SEARCH) { + show_str(directive, "Search"); + } + if(usage & ALPM_DB_USAGE_INSTALL) { + show_str(directive, "Install"); + } + if(usage & ALPM_DB_USAGE_UPGRADE) { + show_str(directive, "Upgrade"); + } + } +} + +static void dump_repo(config_repo_t *repo) +{ + show_usage("Usage", repo->usage); + show_siglevel("SigLevel", repo->siglevel, 0); + show_list_str("Server", repo->servers); +} + +static void dump_config(void) +{ + alpm_list_t *i; + + printf("[options]%c", sep); + + show_str("RootDir", config->rootdir); + show_str("DBPath", config->dbpath); + show_list_str("CacheDir", config->cachedirs); + show_list_str("HookDir", config->hookdirs); + show_str("GPGDir", config->gpgdir); + show_str("LogFile", config->logfile); + + show_list_str("HoldPkg", config->holdpkg); + show_list_str("IgnorePkg", config->ignorepkg); + show_list_str("IgnoreGroup", config->ignoregrp); + show_list_str("NoUpgrade", config->noupgrade); + show_list_str("NoExtract", config->noextract); + + show_str("Architecture", config->arch); + show_str("XferCommand", config->xfercommand); + + show_bool("UseSyslog", config->usesyslog); + show_bool("Color", config->color); + show_bool("TotalDownload", config->totaldownload); + show_bool("CheckSpace", config->checkspace); + show_bool("VerbosePkgLists", config->verbosepkglists); + show_bool("ILoveCandy", config->chomp); + + show_float("UseDelta", config->deltaratio); + + show_cleanmethod("CleanMethod", config->cleanmethod); + + show_siglevel("SigLevel", config->siglevel, 0); + show_siglevel("LocalFileSigLevel", config->localfilesiglevel, 1); + show_siglevel("RemoteFileSigLevel", config->remotefilesiglevel, 1); + + for(i = config->repos; i; i = i->next) { + config_repo_t *repo = i->data; + printf("[%s]%c", repo->name, sep); + dump_repo(repo); + } +} + +static int list_repo_directives(void) +{ + int ret = 0; + alpm_list_t *i; + config_repo_t *repo = NULL; + + for(i = config->repos; i; i = i->next) { + if(strcmp(repo_name, ((config_repo_t*) i->data)->name) == 0) { + repo = i->data; + break; + } + } + + if(!repo) { + fprintf(stderr, "error: repo '%s' not configured\n", repo_name); + return 1; + } + + if(!directives) { + dump_repo(repo); + return 0; + } + + for(i = directives; i; i = i->next) { + if(strcasecmp(i->data, "Server") == 0) { + show_list_str("Server", repo->servers); + } else if(strcasecmp(i->data, "SigLevel") == 0) { + show_siglevel("SigLevel", repo->siglevel, 0); + } else if(strcasecmp(i->data, "Usage") == 0) { + show_usage("Usage", repo->usage); + } else if(strcasecmp(i->data, "Include") == 0) { + fputs("warning: 'Include' directives cannot be queried\n", stderr); + ret = 1; + } else { + fprintf(stderr, "warning: unknown directive '%s'\n", (char*) i->data); + ret = 1; + } + } + + return ret; +} + +static int list_directives(void) +{ + int ret = 0; + alpm_list_t *i; + + if(!directives) { + dump_config(); + return 0; + } + + for(i = directives; i; i = i->next) { + if(strcasecmp(i->data, "RootDir") == 0) { + show_str("RootDir", config->rootdir); + } else if(strcasecmp(i->data, "DBPath") == 0) { + show_str("DBPath", config->dbpath); + } else if(strcasecmp(i->data, "CacheDir") == 0) { + show_list_str("CacheDir", config->cachedirs); + } else if(strcasecmp(i->data, "HookDir") == 0) { + show_list_str("HookDir", config->hookdirs); + } else if(strcasecmp(i->data, "GPGDir") == 0) { + show_str("GPGDir", config->gpgdir); + } else if(strcasecmp(i->data, "LogFile") == 0) { + show_str("LogFile", config->logfile); + + } else if(strcasecmp(i->data, "HoldPkg") == 0) { + show_list_str("HoldPkg", config->holdpkg); + } else if(strcasecmp(i->data, "IgnorePkg") == 0) { + show_list_str("IgnorePkg", config->ignorepkg); + } else if(strcasecmp(i->data, "IgnoreGroup") == 0) { + show_list_str("IgnoreGroup", config->ignoregrp); + } else if(strcasecmp(i->data, "NoUpgrade") == 0) { + show_list_str("NoUpgrade", config->noupgrade); + } else if(strcasecmp(i->data, "NoExtract") == 0) { + show_list_str("NoExtract", config->noupgrade); + + + } else if(strcasecmp(i->data, "Architecture") == 0) { + show_str("Architecture", config->arch); + } else if(strcasecmp(i->data, "XferCommand") == 0) { + show_str("XferCommand", config->xfercommand); + + } else if(strcasecmp(i->data, "UseSyslog") == 0) { + show_bool("UseSyslog", config->usesyslog); + } else if(strcasecmp(i->data, "Color") == 0) { + show_bool("Color", config->color); + } else if(strcasecmp(i->data, "TotalDownload") == 0) { + show_bool("TotalDownload", config->totaldownload); + } else if(strcasecmp(i->data, "CheckSpace") == 0) { + show_bool("CheckSpace", config->checkspace); + } else if(strcasecmp(i->data, "VerbosePkgLists") == 0) { + show_bool("VerbosePkgLists", config->verbosepkglists); + + } else if(strcasecmp(i->data, "UseDelta") == 0) { + show_float("UseDelta", config->deltaratio); + + } else if(strcasecmp(i->data, "CleanMethod") == 0) { + show_cleanmethod("CleanMethod", config->cleanmethod); + + } else if(strcasecmp(i->data, "SigLevel") == 0) { + show_siglevel("SigLevel", config->siglevel, 0); + } else if(strcasecmp(i->data, "LocalFileSigLevel") == 0) { + show_siglevel("LocalFileSigLevel", config->localfilesiglevel, 1); + } else if(strcasecmp(i->data, "RemoteFileSigLevel") == 0) { + show_siglevel("RemoteFileSigLevel", config->remotefilesiglevel, 1); + + } else if(strcasecmp(i->data, "Include") == 0) { + fputs("warning: 'Include' directives cannot be queried\n", stderr); + ret = 1; + } else { + fprintf(stderr, "warning: unknown directive '%s'\n", (char*) i->data); + ret = 1; + } + } + + return ret; +} + +int main(int argc, char **argv) +{ + int ret = 0; + + config = config_new(); + parse_opts(argc, argv); + if(!config) { + ret = 1; + goto cleanup; + } + + for(; optind < argc; optind++) { + directives = alpm_list_add(directives, argv[optind]); + } + + if(alpm_list_count(directives) != 1) { + verbose = 1; + } + + if(repo_list) { + if(directives) { + fputs("error: directives may not be specified with --repo-list\n", stderr); + ret = 1; + goto cleanup; + } + list_repos(); + } else if(repo_name) { + ret = list_repo_directives(); + } else { + ret = list_directives(); + } + +cleanup: + cleanup(); + + return ret; +} + +/* vim: set ts=2 sw=2 noet: */ -- 2.15.1
On 14/01/18 02:49, Andrew Gregory wrote:
Parsing pacman's configuration file is non-trivial and extremely difficult to do correctly from scripts; even our own do it incorrectly. pacman-conf is a dedicated tool specifically to allow scripts to parse config files, getting the same value that pacman itself would use.
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com>
The automake changes resulted in a big warning that is fixed by enabling subdir-objects in configure.ac and ignoring the .dirstamp file it creates. There was also a directory being created in the src/util with the name '$(top_srcdir)'. This is due to using a variable in the paths (no idea why this is an issue), but replacing it with a relative path "fixes" the issue. I'll squash the following into your patch:
From 4c3d7964da2ba4276e4464920fb795e9f973e3ec Mon Sep 17 00:00:00 2001 From: Allan McRae <allan@archlinux.org> Date: Thu, 18 Jan 2018 14:54:17 +1000 Subject: [PATCH] [SQUASH] Fix automake usage for pacman-conf
Signed-off-by: Allan McRae <allan@archlinux.org> --- .gitignore | 1 + configure.ac | 2 +- src/util/Makefile.am | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 499d499b..7399a120 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *~ *.o +.dirstamp ABOUT-NLS aclocal.m4 autom4te.cache diff --git a/configure.ac b/configure.ac index 86f5bb6e..02afba83 100644 --- a/configure.ac +++ b/configure.ac @@ -60,7 +60,7 @@ AC_CONFIG_AUX_DIR([build-aux]) AC_REQUIRE_AUX_FILE([tap-driver.sh]) AC_CANONICAL_HOST -AM_INIT_AUTOMAKE([1.11 foreign]) +AM_INIT_AUTOMAKE([1.11 foreign subdir-objects]) AM_SILENT_RULES([yes]) LT_INIT diff --git a/src/util/Makefile.am b/src/util/Makefile.am index aa812b99..84598ea0 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -26,16 +26,16 @@ cleanupdelta_SOURCES = cleanupdelta.c cleanupdelta_LDADD = $(top_builddir)/lib/libalpm/.libs/libalpm.la pacman_conf_SOURCES = pacman-conf.c \ - $(top_srcdir)/src/pacman/util.h \ - $(top_srcdir)/src/pacman/util.c \ - $(top_srcdir)/src/pacman/ini.h \ - $(top_srcdir)/src/pacman/ini.c \ - $(top_srcdir)/src/pacman/util-common.h \ - $(top_srcdir)/src/pacman/util-common.c \ - $(top_srcdir)/src/pacman/callback.h \ - $(top_srcdir)/src/pacman/callback.c \ - $(top_srcdir)/src/pacman/conf.h \ - $(top_srcdir)/src/pacman/conf.c + ../pacman/util.h \ + ../pacman/util.c \ + ../pacman/ini.h \ + ../pacman/ini.c \ + ../pacman/util-common.h \ + ../pacman/util-common.c \ + ../pacman/callback.h \ + ../pacman/callback.c \ + ../pacman/conf.h \ + ../pacman/conf.c pacman_conf_LDADD = $(top_builddir)/lib/libalpm/.libs/libalpm.la testpkg_SOURCES = testpkg.c -- 2.15.1
On 18/01/18 14:59, Allan McRae wrote:
On 14/01/18 02:49, Andrew Gregory wrote:
Parsing pacman's configuration file is non-trivial and extremely difficult to do correctly from scripts; even our own do it incorrectly. pacman-conf is a dedicated tool specifically to allow scripts to parse config files, getting the same value that pacman itself would use.
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com>
The automake changes resulted in a big warning that is fixed by enabling subdir-objects in configure.ac and ignoring the .dirstamp file it creates.
Crap... that breaks "make distcheck". More specifically "make distclean" as it tries to remove the generated object files for the common files twice.
There was also a directory being created in the src/util with the name '$(top_srcdir)'. This is due to using a variable in the paths (no idea why this is an issue), but replacing it with a relative path "fixes" the issue.
I'll squash the following into your patch:
From 4c3d7964da2ba4276e4464920fb795e9f973e3ec Mon Sep 17 00:00:00 2001 From: Allan McRae <allan@archlinux.org> Date: Thu, 18 Jan 2018 14:54:17 +1000 Subject: [PATCH] [SQUASH] Fix automake usage for pacman-conf
Signed-off-by: Allan McRae <allan@archlinux.org> --- .gitignore | 1 + configure.ac | 2 +- src/util/Makefile.am | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/.gitignore b/.gitignore index 499d499b..7399a120 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *~ *.o +.dirstamp ABOUT-NLS aclocal.m4 autom4te.cache diff --git a/configure.ac b/configure.ac index 86f5bb6e..02afba83 100644 --- a/configure.ac +++ b/configure.ac @@ -60,7 +60,7 @@ AC_CONFIG_AUX_DIR([build-aux]) AC_REQUIRE_AUX_FILE([tap-driver.sh])
AC_CANONICAL_HOST -AM_INIT_AUTOMAKE([1.11 foreign]) +AM_INIT_AUTOMAKE([1.11 foreign subdir-objects]) AM_SILENT_RULES([yes])
LT_INIT diff --git a/src/util/Makefile.am b/src/util/Makefile.am index aa812b99..84598ea0 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -26,16 +26,16 @@ cleanupdelta_SOURCES = cleanupdelta.c cleanupdelta_LDADD = $(top_builddir)/lib/libalpm/.libs/libalpm.la
pacman_conf_SOURCES = pacman-conf.c \ - $(top_srcdir)/src/pacman/util.h \ - $(top_srcdir)/src/pacman/util.c \ - $(top_srcdir)/src/pacman/ini.h \ - $(top_srcdir)/src/pacman/ini.c \ - $(top_srcdir)/src/pacman/util-common.h \ - $(top_srcdir)/src/pacman/util-common.c \ - $(top_srcdir)/src/pacman/callback.h \ - $(top_srcdir)/src/pacman/callback.c \ - $(top_srcdir)/src/pacman/conf.h \ - $(top_srcdir)/src/pacman/conf.c + ../pacman/util.h \ + ../pacman/util.c \ + ../pacman/ini.h \ + ../pacman/ini.c \ + ../pacman/util-common.h \ + ../pacman/util-common.c \ + ../pacman/callback.h \ + ../pacman/callback.c \ + ../pacman/conf.h \ + ../pacman/conf.c pacman_conf_LDADD = $(top_builddir)/lib/libalpm/.libs/libalpm.la
testpkg_SOURCES = testpkg.c
On 18/01/18 15:29, Allan McRae wrote:
On 18/01/18 14:59, Allan McRae wrote:
On 14/01/18 02:49, Andrew Gregory wrote:
Parsing pacman's configuration file is non-trivial and extremely difficult to do correctly from scripts; even our own do it incorrectly. pacman-conf is a dedicated tool specifically to allow scripts to parse config files, getting the same value that pacman itself would use.
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com>
The automake changes resulted in a big warning that is fixed by enabling subdir-objects in configure.ac and ignoring the .dirstamp file it creates.
Crap... that breaks "make distcheck". More specifically "make distclean" as it tries to remove the generated object files for the common files twice.
Turns out automake with this subdir-objects option (which will become default next release) is buggy as hell. Some patches have landed upstream, but we can't rely on them in the pacman codebase. Options here.... 1) host pacman-conf in the src/pacman directory 2) Use the makefile to copy the needed src/pacman files into src/util/pacman-conf/... and they get compiled twice. 3) create a small library using the needed files and static link it into pacman-conf To be clear, I only included #3 as I saw several projects do this! I see #1 as the easiest. And probably most correct - the overlap in source used here is such that they should be located in the same place. Allan
Because parsing pacman.conf is so difficult that even we can't do it right. Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- scripts/completion/zsh_completion.in | 4 ++-- scripts/pacman-db-upgrade.sh.in | 21 ++------------------- scripts/pacman-key.sh.in | 2 +- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/scripts/completion/zsh_completion.in b/scripts/completion/zsh_completion.in index f74fa297..77449955 100644 --- a/scripts/completion/zsh_completion.in +++ b/scripts/completion/zsh_completion.in @@ -316,7 +316,7 @@ _pacman_completions_all_packages() { typeset -U packages ${seq} _wanted packages expl "packages" compadd ${sep[@]} - "${(@)packages}" - repositories=(${(o)${${${(M)${(f)"$(<@sysconfdir@/pacman.conf)"}:#\[*}/\[/}/\]/}:#options}) + repositories=($(pacman-conf --repo-list)) typeset -U repositories _wanted repo_packages expl "repository/package" compadd -S "/" $repositories fi @@ -348,7 +348,7 @@ _pacman_all_packages() { # provides completions for repository names _pacman_completions_repositories() { local -a cmd repositories - repositories=(${(o)${${${(M)${(f)"$(<@sysconfdir@/pacman.conf)"}:#\[*}/\[/}/\]/}:#options}) + repositories=($(pacman-conf --repo-list)) # Uniq the array typeset -U repositories compadd "$@" -a repositories diff --git a/scripts/pacman-db-upgrade.sh.in b/scripts/pacman-db-upgrade.sh.in index a6ff3b25..0629cc1c 100644 --- a/scripts/pacman-db-upgrade.sh.in +++ b/scripts/pacman-db-upgrade.sh.in @@ -70,18 +70,6 @@ die_r() { die "$@" } -get_opt_from_config() { - local keyname="$1" conffile="$2" - local key value - - while IFS=$'= \t' read -r key value _; do - if [[ $key = $keyname ]]; then - echo "$value" - return - fi - done <"$conffile" -} - resolve_dir() { local d="$(cd "$1"; pwd -P)" [[ $d == */ ]] || d+=/ @@ -121,13 +109,8 @@ while true; do done conffile=${conffile:-@sysconfdir@/pacman.conf} -[[ -z $pacroot ]] && pacroot="$(get_opt_from_config "RootDir" "$conffile")" -[[ -z $dbroot ]] && dbroot="$(get_opt_from_config "DBPath" "$conffile")" - -[[ -z $dbroot && -n $pacroot ]] && dbroot="$pacroot/@localstatedir@/lib/pacman" - -[[ -z $pacroot ]] && pacroot="@rootdir@" -[[ -z $dbroot ]] && dbroot="@localstatedir@/lib/pacman/" +[[ -z $pacroot ]] && pacroot=$(pacman-conf --config="$conffile" rootdir) +[[ -z $dbroot ]] && dbroot=$(pacman-conf --config="$conffile" --rootdir="$pacroot" dbpath) m4_include(library/term_colors.sh) diff --git a/scripts/pacman-key.sh.in b/scripts/pacman-key.sh.in index 7b158da7..293a42de 100644 --- a/scripts/pacman-key.sh.in +++ b/scripts/pacman-key.sh.in @@ -580,7 +580,7 @@ fi # if PACMAN_KEYRING_DIR isn't assigned, try to get it from the config # file, falling back on a hard default -PACMAN_KEYRING_DIR=${PACMAN_KEYRING_DIR:-$(get_from "$CONFIG" "GPGDir" "@sysconfdir@/pacman.d/gnupg")} +PACMAN_KEYRING_DIR=${PACMAN_KEYRING_DIR:-$(pacman-conf --config="$CONFIG" gpgdir)} GPG_PACMAN=(gpg --homedir "${PACMAN_KEYRING_DIR}" --no-permission-warning) if [[ -n ${KEYSERVER} ]]; then -- 2.15.1
On 01/13/2018 11:50 AM, Andrew Gregory wrote:
Because parsing pacman.conf is so difficult that even we can't do it right.
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- scripts/completion/zsh_completion.in | 4 ++-- scripts/pacman-db-upgrade.sh.in | 21 ++------------------- scripts/pacman-key.sh.in | 2 +- 3 files changed, 5 insertions(+), 22 deletions(-)
Our bash completion should also be updated to use pacman-conf, as currently it uses a glorious hack to find the completions for -Sl which involves verbosely printing all packages ever and using `cut|sort -u` to extract repo names and dedupe them. Of course, this was still more accurate than the zsh config file parsing. I'd also like to see some way of listing group names for completing -Qg, I guess, although that isn't really something to solve via better *configuration* parsing, so it is probably offtopic for this. -- Eli Schwartz
On 14/01/18 09:21, Eli Schwartz wrote:
On 01/13/2018 11:50 AM, Andrew Gregory wrote:
Because parsing pacman.conf is so difficult that even we can't do it right.
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- scripts/completion/zsh_completion.in | 4 ++-- scripts/pacman-db-upgrade.sh.in | 21 ++------------------- scripts/pacman-key.sh.in | 2 +- 3 files changed, 5 insertions(+), 22 deletions(-)
Our bash completion should also be updated to use pacman-conf, as currently it uses a glorious hack to find the completions for -Sl which involves verbosely printing all packages ever and using `cut|sort -u` to extract repo names and dedupe them.
patches welcome.
On 14/01/18 02:49, Andrew Gregory wrote:
Parsing pacman.conf is so hard that even our own scripts don't do it correctly. pacman-conf is a dedicated utility scripts can use to correctly parse values from pacman.conf. This is a conversion of an earlier version of pacconf from pacutils that has been relicensed and renamed to avoid a file conflict. I make no promises that I've correctly adjusted the automake configuration for this type of build, but it works for me.
Thanks. I have had a quick look at these and I am very happy! My quick scan of the automake changes did not flag any issue, but I will do so in more detail before I commit the changes. (Reminder to myslef: make sure "make distcheck" does what it is supposed to.) We will need a man page (despite the --help output being complete). This will not prevent these patches being committed, but it will be a blocker for release. I assume Andrew has other things in the code base to look at, so this appears to be a "patches welcome" situation. Volunteers? Cheers, Allan
participants (3)
-
Allan McRae
-
Andrew Gregory
-
Eli Schwartz