summaryrefslogtreecommitdiffstats
path: root/src/main/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/main.c')
-rw-r--r--src/main/main.c779
1 files changed, 779 insertions, 0 deletions
diff --git a/src/main/main.c b/src/main/main.c
new file mode 100644
index 0000000..6f0633e
--- /dev/null
+++ b/src/main/main.c
@@ -0,0 +1,779 @@
+/*
+ * 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 int
+printversion(const char *const *argv)
+{
+ if (f_robot) {
+ printf("%s", PACKAGE_VERSION);
+ } else {
+ 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>"));
+
+ return 0;
+}
+
+/*
+ * FIXME: Options that need fixing:
+ * dpkg --command-fd
+ */
+
+static int
+usage(const char *const *argv)
+{
+ 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-help Show help on assertions.\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(_(
+"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"
+" --pre-invoke=<command> Set a pre-invoke hook.\n"
+" --post-invoke=<command> Set a post-invoke hook.\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 with same installed version/arch.\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-pager Disables the use of any pager.\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-<thing>[,...] Override problems (see --force-help).\n"
+" --no-force-<thing>[,...] Stop when problems encountered.\n"
+" --refuse-<thing>[,...] Ditto.\n"
+" --abort-after <n> Abort after encountering <n> errors.\n"
+" --robot Use machine-readable output on some commands.\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>"));
+
+ return 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_robot = 0;
+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;
+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)
+{
+ 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);
+ }
+
+ mask = debug_parse_mask(value);
+ if (mask < 0)
+ badusage(_("--%s requires a positive octal argument"), cpi->olong);
+}
+
+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_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;
+ }
+}
+
+static struct invoke_list pre_invoke_hooks = {
+ .head = NULL,
+ .tail = &pre_invoke_hooks.head,
+};
+static struct invoke_list post_invoke_hooks = {
+ .head = NULL,
+ .tail = &post_invoke_hooks.head,
+};
+static 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_MUX( "assert", 0, act_assert_feature, assert_feature, &assert_feature_name),
+ 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 ),
+*/
+ ACTION( "help", '?', act_help, usage),
+ ACTION( "version", 0, act_version, printversion),
+
+ { "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 },
+ /* TODO: 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 },
+ { "robot", 0, 0, &f_robot, NULL, NULL, 1 },
+ { "root", 0, 1, NULL, NULL, set_root, 0 },
+ { "abort-after", 0, 1, &errabort, NULL, set_integer, 0 },
+ { "admindir", 0, 1, NULL, NULL, set_admindir, 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 },
+ 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"));
+
+ ret |= cipaction->action(endargs);
+
+ fsys_hash_reset();
+
+ pop_error_context(ehflag_normaltidy);
+ }
+
+ fclose(in);
+
+ return ret;
+}
+
+int main(int argc, const char *const *argv) {
+ char *force_string;
+ 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);
+
+ debug(dbg_general, "root=%s admindir=%s", dpkg_fsys_get_dir(), dpkg_db_get_dir());
+
+ /* 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 && getgid() != 0)
+ if (setgid(0) < 0)
+ ohshite(_("cannot set primary group ID to root"));
+
+ if (!cipaction) badusage(_("need an action option"));
+
+ /* Always set environment, to avoid possible security risks. */
+ if (setenv("DPKG_ADMINDIR", dpkg_db_get_dir(), 1) < 0)
+ ohshite(_("unable to setenv for subprocesses"));
+ if (setenv("DPKG_ROOT", dpkg_fsys_get_dir(), 1) < 0)
+ ohshite(_("unable to setenv for subprocesses"));
+ force_string = get_force_string();
+ if (setenv("DPKG_FORCE", force_string, 1) < 0)
+ ohshite(_("unable to setenv for subprocesses"));
+ free(force_string);
+
+ 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);
+ }
+
+ 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();
+ dpkg_locales_done();
+
+ return reportbroken_retexitstatus(ret);
+}