diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 00:45:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 00:45:20 +0000 |
commit | 9a08cbfcc1ef900a04580f35afe2a4592d7d6030 (patch) | |
tree | 004cc7027bca2f2c0bcb5806527c8e0c48df2d6e /src/main.c | |
parent | Initial commit. (diff) | |
download | dpkg-9a08cbfcc1ef900a04580f35afe2a4592d7d6030.tar.xz dpkg-9a08cbfcc1ef900a04580f35afe2a4592d7d6030.zip |
Adding upstream version 1.19.8.upstream/1.19.8upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/main.c')
-rw-r--r-- | src/main.c | 788 |
1 files changed, 788 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..2bb0a22 --- /dev/null +++ b/src/main.c @@ -0,0 +1,788 @@ +/* + * dpkg - main program for package management + * main.c - main program + * + * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2006-2016 Guillem Jover <guillem@debian.org> + * Copyright © 2010 Canonical Ltd. + * written by Martin Pitt <martin.pitt@canonical.com> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/wait.h> + +#include <errno.h> +#include <limits.h> +#if HAVE_LOCALE_H +#include <locale.h> +#endif +#include <string.h> +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/macros.h> +#include <dpkg/i18n.h> +#include <dpkg/c-ctype.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/arch.h> +#include <dpkg/subproc.h> +#include <dpkg/command.h> +#include <dpkg/pager.h> +#include <dpkg/options.h> +#include <dpkg/db-fsys.h> + +#include "main.h" +#include "filters.h" + +static void DPKG_ATTR_NORET +printversion(const struct cmdinfo *ci, const char *value) +{ + printf(_("Debian '%s' package management program version %s.\n"), + DPKG, PACKAGE_RELEASE); + printf(_( +"This is free software; see the GNU General Public License version 2 or\n" +"later for copying conditions. There is NO warranty.\n")); + + m_output(stdout, _("<standard output>")); + + exit(0); +} + +/* + * FIXME: Options that need fixing: + * dpkg --command-fd + */ + +static void DPKG_ATTR_NORET +usage(const struct cmdinfo *ci, const char *value) +{ + printf(_( +"Usage: %s [<option> ...] <command>\n" +"\n"), DPKG); + + printf(_( +"Commands:\n" +" -i|--install <.deb file name>... | -R|--recursive <directory>...\n" +" --unpack <.deb file name>... | -R|--recursive <directory>...\n" +" -A|--record-avail <.deb file name>... | -R|--recursive <directory>...\n" +" --configure <package>... | -a|--pending\n" +" --triggers-only <package>... | -a|--pending\n" +" -r|--remove <package>... | -a|--pending\n" +" -P|--purge <package>... | -a|--pending\n" +" -V|--verify [<package>...] Verify the integrity of package(s).\n" +" --get-selections [<pattern>...] Get list of selections to stdout.\n" +" --set-selections Set package selections from stdin.\n" +" --clear-selections Deselect every non-essential package.\n" +" --update-avail [<Packages-file>] Replace available packages info.\n" +" --merge-avail [<Packages-file>] Merge with info from file.\n" +" --clear-avail Erase existing available info.\n" +" --forget-old-unavail Forget uninstalled unavailable pkgs.\n" +" -s|--status [<package>...] Display package status details.\n" +" -p|--print-avail [<package>...] Display available version details.\n" +" -L|--listfiles <package>... List files 'owned' by package(s).\n" +" -l|--list [<pattern>...] List packages concisely.\n" +" -S|--search <pattern>... Find package(s) owning file(s).\n" +" -C|--audit [<package>...] Check for broken package(s).\n" +" --yet-to-unpack Print packages selected for installation.\n" +" --predep-package Print pre-dependencies to unpack.\n" +" --add-architecture <arch> Add <arch> to the list of architectures.\n" +" --remove-architecture <arch> Remove <arch> from the list of architectures.\n" +" --print-architecture Print dpkg architecture.\n" +" --print-foreign-architectures Print allowed foreign architectures.\n" +" --assert-<feature> Assert support for the specified feature.\n" +" --validate-<thing> <string> Validate a <thing>'s <string>.\n" +" --compare-versions <a> <op> <b> Compare version numbers - see below.\n" +" --force-help Show help on forcing.\n" +" -Dh|--debug=help Show help on debugging.\n" +"\n")); + + printf(_( +" -?, --help Show this help message.\n" +" --version Show the version.\n" +"\n")); + + printf(_( +"Assertable features: support-predepends, working-epoch, long-filenames,\n" +" multi-conrep, multi-arch, versioned-provides.\n" +"\n")); + + printf(_( +"Validatable things: pkgname, archname, trigname, version.\n" +"\n")); + + printf(_( +"Use dpkg with -b, --build, -c, --contents, -e, --control, -I, --info,\n" +" -f, --field, -x, --extract, -X, --vextract, --ctrl-tarfile, --fsys-tarfile\n" +"on archives (type %s --help).\n" +"\n"), BACKEND); + + printf(_( +"Options:\n" +" --admindir=<directory> Use <directory> instead of %s.\n" +" --root=<directory> Install on a different root directory.\n" +" --instdir=<directory> Change installation dir without changing admin dir.\n" +" --path-exclude=<pattern> Do not install paths which match a shell pattern.\n" +" --path-include=<pattern> Re-include a pattern after a previous exclusion.\n" +" -O|--selected-only Skip packages not selected for install/upgrade.\n" +" -E|--skip-same-version Skip packages whose same version is installed.\n" +" -G|--refuse-downgrade Skip packages with earlier version than installed.\n" +" -B|--auto-deconfigure Install even if it would break some other package.\n" +" --[no-]triggers Skip or force consequential trigger processing.\n" +" --verify-format=<format> Verify output format (supported: 'rpm').\n" +" --no-debsig Do not try to verify package signatures.\n" +" --no-act|--dry-run|--simulate\n" +" Just say what we would do - don't do it.\n" +" -D|--debug=<octal> Enable debugging (see -Dhelp or --debug=help).\n" +" --status-fd <n> Send status change updates to file descriptor <n>.\n" +" --status-logger=<command> Send status change updates to <command>'s stdin.\n" +" --log=<filename> Log status changes and actions to <filename>.\n" +" --ignore-depends=<package>,...\n" +" Ignore dependencies involving <package>.\n" +" --force-... Override problems (see --force-help).\n" +" --no-force-...|--refuse-...\n" +" Stop when problems encountered.\n" +" --abort-after <n> Abort after encountering <n> errors.\n" +"\n"), ADMINDIR); + + printf(_( +"Comparison operators for --compare-versions are:\n" +" lt le eq ne ge gt (treat empty version as earlier than any version);\n" +" lt-nl le-nl ge-nl gt-nl (treat empty version as later than any version);\n" +" < << <= = >= >> > (only for compatibility with control file syntax).\n" +"\n")); + + printf(_( +"Use 'apt' or 'aptitude' for user-friendly package management.\n")); + + m_output(stdout, _("<standard output>")); + + exit(0); +} + +static const char printforhelp[] = N_( +"Type dpkg --help for help about installing and deinstalling packages [*];\n" +"Use 'apt' or 'aptitude' for user-friendly package management;\n" +"Type dpkg -Dhelp for a list of dpkg debug flag values;\n" +"Type dpkg --force-help for a list of forcing options;\n" +"Type dpkg-deb --help for help about manipulating *.deb files;\n" +"\n" +"Options marked [*] produce a lot of output - pipe it through 'less' or 'more' !"); + +int f_pending=0, f_recursive=0, f_alsoselect=1, f_skipsame=0, f_noact=0; +int f_autodeconf=0, f_nodebsig=0; +int f_triggers = 0; + +int errabort = 50; +static const char *admindir = ADMINDIR; +const char *instdir= ""; +struct pkg_list *ignoredependss = NULL; + +#define DBG_DEF(n, d) \ + { .flag = dbg_##n, .name = #n, .desc = d } + +static const struct debuginfo { + int flag; + const char *name; + const char *desc; +} debuginfos[] = { + DBG_DEF(general, N_("Generally helpful progress information")), + DBG_DEF(scripts, N_("Invocation and status of maintainer scripts")), + DBG_DEF(eachfile, N_("Output for each file processed")), + DBG_DEF(eachfiledetail, N_("Lots of output for each file processed")), + DBG_DEF(conff, N_("Output for each configuration file")), + DBG_DEF(conffdetail, N_("Lots of output for each configuration file")), + DBG_DEF(depcon, N_("Dependencies and conflicts")), + DBG_DEF(depcondetail, N_("Lots of dependencies/conflicts output")), + DBG_DEF(triggers, N_("Trigger activation and processing")), + DBG_DEF(triggersdetail, N_("Lots of output regarding triggers")), + DBG_DEF(triggersstupid, N_("Silly amounts of output regarding triggers")), + DBG_DEF(veryverbose, N_("Lots of drivel about eg the dpkg/info directory")), + DBG_DEF(stupidlyverbose, N_("Insane amounts of drivel")), + { 0, NULL, NULL } +}; + +static void +set_debug(const struct cmdinfo *cpi, const char *value) +{ + char *endp; + long mask; + const struct debuginfo *dip; + + if (*value == 'h') { + printf(_( +"%s debugging option, --debug=<octal> or -D<octal>:\n" +"\n" +" Number Ref. in source Description\n"), DPKG); + + for (dip = debuginfos; dip->name; dip++) + printf(" %6o %-16s %s\n", dip->flag, dip->name, gettext(dip->desc)); + + printf(_("\n" +"Debugging options can be mixed using bitwise-or.\n" +"Note that the meanings and values are subject to change.\n")); + m_output(stdout, _("<standard output>")); + exit(0); + } + + errno = 0; + mask = strtol(value, &endp, 8); + if (value == endp || *endp || mask < 0 || errno == ERANGE) + badusage(_("--%s requires a positive octal argument"), cpi->olong); + + debug_set_mask(mask); +} + +static void +set_no_pager(const struct cmdinfo *ci, const char *value) +{ + pager_enable(false); + + /* Let's communicate this to our backends. */ + setenv("DPKG_PAGER", "cat", 1); +} + +static void +set_filter(const struct cmdinfo *cip, const char *value) +{ + filter_add(value, cip->arg_int); +} + +static void +set_verify_format(const struct cmdinfo *cip, const char *value) +{ + if (!verify_set_output(value)) + badusage(_("unknown verify output format '%s'"), value); +} + +static void +set_instdir(const struct cmdinfo *cip, const char *value) +{ + instdir = dpkg_fsys_set_dir(value); +} + +static void +set_root(const struct cmdinfo *cip, const char *value) +{ + instdir = dpkg_fsys_set_dir(value); + admindir = dpkg_fsys_get_path(ADMINDIR); +} + +static void +set_ignore_depends(const struct cmdinfo *cip, const char *value) +{ + char *copy, *p; + + copy= m_malloc(strlen(value)+2); + strcpy(copy,value); + copy[strlen(value) + 1] = '\0'; + for (p=copy; *p; p++) { + if (*p != ',') continue; + *p++ = '\0'; + if (!*p || *p==',' || p==copy+1) + badusage(_("null package name in --%s comma-separated list '%.250s'"), + cip->olong, value); + } + p= copy; + while (*p) { + struct pkginfo *pkg; + + pkg = dpkg_options_parse_pkgname(cip, p); + pkg_list_prepend(&ignoredependss, pkg); + + p+= strlen(p)+1; + } + + free(copy); +} + +static void +set_integer(const struct cmdinfo *cip, const char *value) +{ + *cip->iassignto = dpkg_options_parse_arg_int(cip, value); +} + +static void +set_pipe(const struct cmdinfo *cip, const char *value) +{ + long v; + + v = dpkg_options_parse_arg_int(cip, value); + + statusfd_add(v); +} + +static bool +is_invoke_action(enum action action) +{ + switch (action) { + case act_unpack: + case act_configure: + case act_install: + case act_triggers: + case act_remove: + case act_purge: + case act_arch_add: + case act_arch_remove: + return true; + default: + return false; + } +} + +struct invoke_list pre_invoke_hooks = { .head = NULL, .tail = &pre_invoke_hooks.head }; +struct invoke_list post_invoke_hooks = { .head = NULL, .tail = &post_invoke_hooks.head }; +struct invoke_list status_loggers = { .head = NULL, .tail = &status_loggers.head }; + +static void +set_invoke_hook(const struct cmdinfo *cip, const char *value) +{ + struct invoke_list *hook_list = cip->arg_ptr; + struct invoke_hook *hook_new; + + hook_new = m_malloc(sizeof(*hook_new)); + hook_new->command = m_strdup(value); + hook_new->next = NULL; + + /* Add the new hook at the tail of the list to preserve the order. */ + *hook_list->tail = hook_new; + hook_list->tail = &hook_new->next; +} + +static void +run_invoke_hooks(const char *action, struct invoke_list *hook_list) +{ + struct invoke_hook *hook; + + setenv("DPKG_HOOK_ACTION", action, 1); + + for (hook = hook_list->head; hook; hook = hook->next) { + int status; + + /* XXX: As an optimization, use exec instead if no shell metachar are + * used “!$=&|\\`'"^~;<>{}[]()?*#”. */ + status = system(hook->command); + if (status != 0) + ohshit(_("error executing hook '%s', exit code %d"), hook->command, + status); + } + + unsetenv("DPKG_HOOK_ACTION"); +} + +static void +free_invoke_hooks(struct invoke_list *hook_list) +{ + struct invoke_hook *hook, *hook_next; + + for (hook = hook_list->head; hook; hook = hook_next) { + hook_next = hook->next; + free(hook->command); + free(hook); + } +} + +static int +run_logger(struct invoke_hook *hook, const char *name) +{ + pid_t pid; + int p[2]; + + m_pipe(p); + + pid = subproc_fork(); + if (pid == 0) { + /* Setup stdin and stdout. */ + m_dup2(p[0], 0); + close(1); + + close(p[0]); + close(p[1]); + + command_shell(hook->command, name); + } + close(p[0]); + + return p[1]; +} + +static void +run_status_loggers(struct invoke_list *hook_list) +{ + struct invoke_hook *hook; + + for (hook = hook_list->head; hook; hook = hook->next) { + int fd; + + fd = run_logger(hook, _("status logger")); + statusfd_add(fd); + } +} + +static int +arch_add(const char *const *argv) +{ + struct dpkg_arch *arch; + const char *archname = *argv++; + + if (archname == NULL || *argv) + badusage(_("--%s takes exactly one argument"), cipaction->olong); + + dpkg_arch_load_list(); + + arch = dpkg_arch_add(archname); + switch (arch->type) { + case DPKG_ARCH_NATIVE: + case DPKG_ARCH_FOREIGN: + break; + case DPKG_ARCH_ILLEGAL: + ohshit(_("architecture '%s' is illegal: %s"), archname, + dpkg_arch_name_is_illegal(archname)); + default: + ohshit(_("architecture '%s' is reserved and cannot be added"), archname); + } + + dpkg_arch_save_list(); + + return 0; +} + +static int +arch_remove(const char *const *argv) +{ + const char *archname = *argv++; + struct dpkg_arch *arch; + struct pkg_hash_iter *iter; + struct pkginfo *pkg; + + if (archname == NULL || *argv) + badusage(_("--%s takes exactly one argument"), cipaction->olong); + + modstatdb_open(msdbrw_readonly); + + arch = dpkg_arch_find(archname); + if (arch->type != DPKG_ARCH_FOREIGN) { + warning(_("cannot remove non-foreign architecture '%s'"), arch->name); + return 0; + } + + /* Check if it's safe to remove the architecture from the db. */ + iter = pkg_hash_iter_new(); + while ((pkg = pkg_hash_iter_next_pkg(iter))) { + if (pkg->status < PKG_STAT_HALFINSTALLED) + continue; + if (pkg->installed.arch == arch) { + if (in_force(FORCE_ARCHITECTURE)) + warning(_("removing architecture '%s' currently in use by database"), + arch->name); + else + ohshit(_("cannot remove architecture '%s' currently in use by the database"), + arch->name); + break; + } + } + pkg_hash_iter_free(iter); + + dpkg_arch_unmark(arch); + dpkg_arch_save_list(); + + modstatdb_shutdown(); + + return 0; +} + +int execbackend(const char *const *argv) DPKG_ATTR_NORET; +int commandfd(const char *const *argv); + +/* This table has both the action entries in it and the normal options. + * The action entries are made with the ACTION macro, as they all + * have a very similar structure. */ +static const struct cmdinfo cmdinfos[]= { +#define ACTIONBACKEND(longopt, shortopt, backend) \ + { longopt, shortopt, 0, NULL, NULL, setaction, 0, (void *)backend, execbackend } + + ACTION( "install", 'i', act_install, archivefiles ), + ACTION( "unpack", 0, act_unpack, archivefiles ), + ACTION( "record-avail", 'A', act_avail, archivefiles ), + ACTION( "configure", 0, act_configure, packages ), + ACTION( "remove", 'r', act_remove, packages ), + ACTION( "purge", 'P', act_purge, packages ), + ACTION( "triggers-only", 0, act_triggers, packages ), + ACTION( "verify", 'V', act_verify, verify ), + ACTIONBACKEND( "listfiles", 'L', DPKGQUERY), + ACTIONBACKEND( "status", 's', DPKGQUERY), + ACTION( "get-selections", 0, act_getselections, getselections ), + ACTION( "set-selections", 0, act_setselections, setselections ), + ACTION( "clear-selections", 0, act_clearselections, clearselections ), + ACTIONBACKEND( "print-avail", 'p', DPKGQUERY), + ACTION( "update-avail", 0, act_avreplace, updateavailable ), + ACTION( "merge-avail", 0, act_avmerge, updateavailable ), + ACTION( "clear-avail", 0, act_avclear, updateavailable ), + ACTION( "forget-old-unavail", 0, act_forgetold, forgetold ), + ACTION( "audit", 'C', act_audit, audit ), + ACTION( "yet-to-unpack", 0, act_unpackchk, unpackchk ), + ACTIONBACKEND( "list", 'l', DPKGQUERY), + ACTIONBACKEND( "search", 'S', DPKGQUERY), + ACTION( "assert-support-predepends", 0, act_assertpredep, assertpredep ), + ACTION( "assert-working-epoch", 0, act_assertepoch, assertepoch ), + ACTION( "assert-long-filenames", 0, act_assertlongfilenames, assertlongfilenames ), + ACTION( "assert-multi-conrep", 0, act_assertmulticonrep, assertmulticonrep ), + ACTION( "assert-multi-arch", 0, act_assertmultiarch, assertmultiarch ), + ACTION( "assert-versioned-provides", 0, act_assertverprovides, assertverprovides ), + ACTION( "add-architecture", 0, act_arch_add, arch_add ), + ACTION( "remove-architecture", 0, act_arch_remove, arch_remove ), + ACTION( "print-architecture", 0, act_printarch, printarch ), + ACTION( "print-foreign-architectures", 0, act_printforeignarches, print_foreign_arches ), + ACTION( "predep-package", 0, act_predeppackage, predeppackage ), + ACTION( "validate-pkgname", 0, act_validate_pkgname, validate_pkgname ), + ACTION( "validate-trigname", 0, act_validate_trigname, validate_trigname ), + ACTION( "validate-archname", 0, act_validate_archname, validate_archname ), + ACTION( "validate-version", 0, act_validate_version, validate_version ), + ACTION( "compare-versions", 0, act_cmpversions, cmpversions ), +/* + ACTION( "command-fd", 'c', act_commandfd, commandfd ), +*/ + + { "pre-invoke", 0, 1, NULL, NULL, set_invoke_hook, 0, &pre_invoke_hooks }, + { "post-invoke", 0, 1, NULL, NULL, set_invoke_hook, 0, &post_invoke_hooks }, + { "path-exclude", 0, 1, NULL, NULL, set_filter, 0 }, + { "path-include", 0, 1, NULL, NULL, set_filter, 1 }, + { "verify-format", 0, 1, NULL, NULL, set_verify_format }, + { "status-logger", 0, 1, NULL, NULL, set_invoke_hook, 0, &status_loggers }, + { "status-fd", 0, 1, NULL, NULL, set_pipe, 0 }, + { "log", 0, 1, NULL, &log_file, NULL, 0 }, + { "pending", 'a', 0, &f_pending, NULL, NULL, 1 }, + { "recursive", 'R', 0, &f_recursive, NULL, NULL, 1 }, + { "no-act", 0, 0, &f_noact, NULL, NULL, 1 }, + { "dry-run", 0, 0, &f_noact, NULL, NULL, 1 }, + { "simulate", 0, 0, &f_noact, NULL, NULL, 1 }, + { "no-pager", 0, 0, NULL, NULL, set_no_pager, 0 }, + { "no-debsig", 0, 0, &f_nodebsig, NULL, NULL, 1 }, + /* Alias ('G') for --refuse. */ + { NULL, 'G', 0, NULL, NULL, reset_force_option, FORCE_DOWNGRADE }, + { "selected-only", 'O', 0, &f_alsoselect, NULL, NULL, 0 }, + { "triggers", 0, 0, &f_triggers, NULL, NULL, 1 }, + { "no-triggers", 0, 0, &f_triggers, NULL, NULL, -1 }, + /* FIXME: Remove ('N') sometime. */ + { "no-also-select", 'N', 0, &f_alsoselect, NULL, NULL, 0 }, + { "skip-same-version", 'E', 0, &f_skipsame, NULL, NULL, 1 }, + { "auto-deconfigure", 'B', 0, &f_autodeconf, NULL, NULL, 1 }, + { "root", 0, 1, NULL, NULL, set_root, 0 }, + { "abort-after", 0, 1, &errabort, NULL, set_integer, 0 }, + { "admindir", 0, 1, NULL, &admindir, NULL, 0 }, + { "instdir", 0, 1, NULL, NULL, set_instdir, 0 }, + { "ignore-depends", 0, 1, NULL, NULL, set_ignore_depends, 0 }, + { "force", 0, 2, NULL, NULL, set_force_option, 1 }, + { "refuse", 0, 2, NULL, NULL, set_force_option, 0 }, + { "no-force", 0, 2, NULL, NULL, set_force_option, 0 }, + { "debug", 'D', 1, NULL, NULL, set_debug, 0 }, + { "help", '?', 0, NULL, NULL, usage, 0 }, + { "version", 0, 0, NULL, NULL, printversion, 0 }, + ACTIONBACKEND( "build", 'b', BACKEND), + ACTIONBACKEND( "contents", 'c', BACKEND), + ACTIONBACKEND( "control", 'e', BACKEND), + ACTIONBACKEND( "info", 'I', BACKEND), + ACTIONBACKEND( "field", 'f', BACKEND), + ACTIONBACKEND( "extract", 'x', BACKEND), + ACTIONBACKEND( "vextract", 'X', BACKEND), + ACTIONBACKEND( "ctrl-tarfile", 0, BACKEND), + ACTIONBACKEND( "fsys-tarfile", 0, BACKEND), + { NULL, 0, 0, NULL, NULL, NULL, 0 } +}; + +int +execbackend(const char *const *argv) +{ + struct command cmd; + + command_init(&cmd, cipaction->arg_ptr, NULL); + command_add_arg(&cmd, cipaction->arg_ptr); + command_add_arg(&cmd, str_fmt("--%s", cipaction->olong)); + + /* Explicitly separate arguments from options as any user-supplied + * separator got stripped by the option parser */ + command_add_arg(&cmd, "--"); + command_add_argl(&cmd, (const char **)argv); + + command_exec(&cmd); +} + +int +commandfd(const char *const *argv) +{ + struct varbuf linevb = VARBUF_INIT; + const char * pipein; + const char **newargs = NULL, **endargs; + char *ptr, *endptr; + FILE *in; + long infd; + int ret = 0; + int c, lno, i; + bool skipchar; + + pipein = *argv++; + if (pipein == NULL || *argv) + badusage(_("--%s takes exactly one argument"), cipaction->olong); + + infd = dpkg_options_parse_arg_int(cipaction, pipein); + in = fdopen(infd, "r"); + if (in == NULL) + ohshite(_("couldn't open '%i' for stream"), (int)infd); + + for (;;) { + bool mode = false; + int argc= 1; + lno= 0; + + push_error_context(); + + do { + c = getc(in); + if (c == '\n') + lno++; + } while (c != EOF && c_isspace(c)); + if (c == EOF) break; + if (c == '#') { + do { c= getc(in); if (c == '\n') lno++; } while (c != EOF && c != '\n'); + continue; + } + varbuf_reset(&linevb); + do { + varbuf_add_char(&linevb, c); + c= getc(in); + if (c == '\n') lno++; + + /* This isn't fully accurate, but overestimating can't hurt. */ + if (c_isspace(c)) + argc++; + } while (c != EOF && c != '\n'); + if (c == EOF) + ohshit(_("unexpected end of file before end of line %d"), lno); + if (!argc) continue; + varbuf_end_str(&linevb); + newargs = m_realloc(newargs, sizeof(const char *) * (argc + 1)); + argc= 1; + ptr= linevb.buf; + endptr = ptr + linevb.used + 1; + skipchar = false; + while(ptr < endptr) { + if (skipchar) { + skipchar = false; + } else if (*ptr == '\\') { + memmove(ptr, (ptr+1), (linevb.used-(linevb.buf - ptr)-1)); + endptr--; + skipchar = true; + continue; + } else if (c_isspace(*ptr)) { + if (mode == true) { + *ptr = '\0'; + mode = false; + } + } else { + if (mode == false) { + newargs[argc]= ptr; + argc++; + mode = true; + } + } + ptr++; + } + *ptr = '\0'; + newargs[argc++] = NULL; + + /* We strdup() each argument, but never free it, because the + * error messages contain references back to these strings. + * Freeing them, and reusing the memory, would make those + * error messages confusing, to say the least. */ + for(i=1;i<argc;i++) + if (newargs[i]) + newargs[i] = m_strdup(newargs[i]); + endargs = newargs; + + setaction(NULL, NULL); + dpkg_options_parse((const char *const **)&endargs, cmdinfos, printforhelp); + if (!cipaction) badusage(_("need an action option")); + + fsys_hash_init(); + + ret |= cipaction->action(endargs); + + fsys_hash_reset(); + + pop_error_context(ehflag_normaltidy); + } + + fclose(in); + + return ret; +} + +int main(int argc, const char *const *argv) { + int ret; + + dpkg_locales_init(PACKAGE); + dpkg_program_init("dpkg"); + set_force_default(FORCE_ALL); + dpkg_options_load(DPKG, cmdinfos); + dpkg_options_parse(&argv, cmdinfos, printforhelp); + + /* When running as root, make sure our primary group is also root, so + * that files created by maintainer scripts have correct ownership. */ + if (!in_force(FORCE_NON_ROOT) && getuid() == 0) + if (setgid(0) < 0) + ohshite(_("cannot set primary group ID to root")); + + if (!cipaction) badusage(_("need an action option")); + + admindir = dpkg_db_set_dir(admindir); + + /* Always set environment, to avoid possible security risks. */ + if (setenv("DPKG_ADMINDIR", admindir, 1) < 0) + ohshite(_("unable to setenv for subprocesses")); + if (setenv("DPKG_ROOT", instdir, 1) < 0) + ohshite(_("unable to setenv for subprocesses")); + if (setenv("DPKG_FORCE", get_force_string(), 1) < 0) + ohshite(_("unable to setenv for subprocesses")); + + if (!f_triggers) + f_triggers = (cipaction->arg_int == act_triggers && *argv) ? -1 : 1; + + if (is_invoke_action(cipaction->arg_int)) { + run_invoke_hooks(cipaction->olong, &pre_invoke_hooks); + run_status_loggers(&status_loggers); + } + + fsys_hash_init(); + + ret = cipaction->action(argv); + + if (is_invoke_action(cipaction->arg_int)) + run_invoke_hooks(cipaction->olong, &post_invoke_hooks); + + free_invoke_hooks(&pre_invoke_hooks); + free_invoke_hooks(&post_invoke_hooks); + + dpkg_program_done(); + + return reportbroken_retexitstatus(ret); +} |