summaryrefslogtreecommitdiffstats
path: root/updates.c
diff options
context:
space:
mode:
Diffstat (limited to 'updates.c')
-rw-r--r--updates.c2702
1 files changed, 2702 insertions, 0 deletions
diff --git a/updates.c b/updates.c
new file mode 100644
index 0000000..8d81ddf
--- /dev/null
+++ b/updates.c
@@ -0,0 +1,2702 @@
+/* This file is part of "reprepro"
+ * Copyright (C) 2003,2004,2005,2006,2007,2008,2009,2016 Bernhard R. Link
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+
+/* This module handles the updating of distributions from remote repositories.
+ * It's using apt's methods (the files in /usr/lib/apt/methods) for the
+ * actuall getting of needed lists and package files.
+ *
+ * It's only task is to request the right actions in the right order,
+ * almost everything is done in other modules:
+ *
+ * aptmethod.c start, feed and take care of the apt methods
+ * downloadcache.c keep track of what is downloaded to avoid duplicates
+ * signature.c verify Release.gpg files, if requested
+ * remoterepository.c cache remote index files and decide which to download
+ * upgradelist.c decide which packages (and version) should be installed
+ *
+ * An update run consists of the following steps, in between done some
+ * downloading, checking and so on:
+ *
+ * Step 1: parsing the conf/updates file with the patterns
+ * Step 2: create rules for some distribution based on those patterns
+ * Step 3: calculate which remote indices are to be retrieved and processed
+ * Step 4: <removed>
+ * Step 5: preperations for actually doing anything
+ * Step 6: queue downloading of list of lists (Release, Release.gpg, ...)
+ * Step 7: queue downloading of lists (Packages.gz, Sources.gz, ...)
+ * Step 8: call possible list hooks allowing them to modify the lists
+ * Step 9: search for missing packages i.e. needing to be added or upgraded
+ * Step 10: enqueue downloading of missing packages
+ * Step 11: install the missing packages
+ * Step 12: remember processed index files as processed
+ *
+ */
+#include <config.h>
+
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include "error.h"
+#include "ignore.h"
+#include "mprintf.h"
+#include "strlist.h"
+#include "atoms.h"
+#include "dirs.h"
+#include "names.h"
+#include "signature.h"
+#include "aptmethod.h"
+#include "downloadcache.h"
+#include "updates.h"
+#include "upgradelist.h"
+#include "distribution.h"
+#include "tracking.h"
+#include "termdecide.h"
+#include "chunks.h"
+#include "filterlist.h"
+#include "log.h"
+#include "donefile.h"
+#include "freespace.h"
+#include "configparser.h"
+#include "filecntl.h"
+#include "remoterepository.h"
+#include "uncompression.h"
+#include "package.h"
+
+/* The data structures of this one: ("u_" is short for "update_")
+
+updates_getpatterns read a list of patterns from <confdir>/updates:
+
+ u_pattern --> u_pattern --> u_pattern --> NULL
+ / \ / \ / \ / \
+ | \ | |
+ \ ----\ | |
+ ------------ | | |
+ \ . | .
+ | |
+updates_getupstreams instances them for a given distribution:
+ | |
+ u_distribution --> u_origin -> u_origin --> NULL
+ | | / \ / \ / \ / \
+ | \ / | | | |
+ | u_target -> u_index -> u_index -> NULL
+ | | | |
+ | \ / | |
+ | u_target -> u_index -> u_index -> NULL
+ | |
+ | \ /
+ | NULL . .
+ \ / | |
+ u_distribution ---> u_origin -> u_origin -> NULL
+ | | / \ / \
+ | \ / | |
+ | u_target --> u_index ---> u_index -> NULL
+ | |
+ | \ /
+ | NULL omitted in this image:
+ | not every target must have an index in each
+ \ / origin. (Some origin might only support a
+ NULL limited number of architectures or components)
+
+ also omitted are delete rules, i.e. markers
+ that all versions previously found are not to
+ be kept or even installed, unless a later
+ index again adds them.
+*/
+
+/* the data for some upstream part to get updates from, some
+ * some fields can be NULL or empty */
+struct update_pattern {
+ struct update_pattern *next;
+ //e.g. "Name: woody"
+ char *name;
+ /* another pattern to take value from */
+ char *from;
+ /*@dependent@*/struct update_pattern *pattern_from;
+ //e.g. "Method: ftp://ftp.uni-freiburg.de/pub/linux/debian"
+ /*@null@*/ char *method;
+ //e.g. "Fallback: ftp://ftp.debian.org/pub/linux/debian"
+ /*@null@*/ char *fallback; // can be other server or dir, but must be same method
+ //e.g. "Config: Dir=/"
+ struct strlist config;
+ //e.g. "Suite: woody" or "Suite: <asterix>/updates" (NULL means "*")
+ /*@null@*/char *suite_from;
+ //e.g. "VerifyRelease: B629A24C38C6029A" (NULL means not check)
+ /*@null@*/char *verifyrelease;
+ //e.g. "Architectures: i386 sparc mips" (not set means all)
+ struct strlist architectures_from;
+ struct strlist architectures_into;
+ //e.g. "Components: main>main non-free>non-free contrib>contrib"
+ // (empty means all)
+ struct strlist components_from;
+ struct strlist components_into;
+ //e.g. "UDebComponents: main>main"
+ // (empty means all)
+ struct strlist udebcomponents_from;
+ struct strlist udebcomponents_into;
+ // There's no ddeb support here yet, since we don't know what the
+ // Debian archive layout is going to look like.
+
+ // NULL means no condition
+ /*@null@*/term *includecondition;
+ struct filterlist filterlist;
+ struct filterlist filtersrclist;
+ // NULL means nothing to execute after lists are downloaded...
+ /*@null@*/char *listhook;
+ /*@null@*/char *shellhook;
+ /* checksums to not read check in Release file: */
+ bool ignorehashes[cs_hashCOUNT];
+ /* the name of the flat component, causing flat mode if non-NULL*/
+ component_t flat;
+ //e.g. "IgnoreRelease: Yes" for 1 (default is 0)
+ bool ignorerelease;
+ //e.g. "GetInRelease: No" for 0 (default is 1)
+ bool getinrelease;
+ /* the form in which index files are preferably downloaded */
+ struct encoding_preferences downloadlistsas;
+ /* if true ignore sources with Extra-Source-Only */
+ bool omitextrasource;
+ /* if the specific field is there (to destinguish from an empty one) */
+ bool omitextrasource_set;
+ bool ignorehashes_set;
+ bool ignorerelease_set;
+ bool getinrelease_set;
+ bool architectures_set;
+ bool components_set;
+ bool udebcomponents_set;
+ bool includecondition_set;
+ bool config_set;
+ bool downloadlistsas_set;
+ /* to check circular references */
+ bool visited;
+
+ bool used;
+ struct remote_repository *repository;
+};
+
+struct update_origin {
+ struct update_origin *next;
+ /* all following are NULL when this is a delete rule */
+ /*@null@*/const struct update_pattern *pattern;
+ /*@null@*/char *suite_from;
+ /*@null@*/const struct distribution *distribution;
+ /*@null@*/struct remote_distribution *from;
+ /* cache for flat mode */
+ bool flat;
+ /* set when there was a error and it should no longer be used */
+ bool failed;
+};
+
+struct update_index_connector {
+ struct update_index_connector *next;
+
+ /* NULL when this is a delete rule */
+ /*@null@*/ struct remote_index *remote;
+ /*@null@*/ struct update_origin *origin;
+
+ /*@null@*/char *afterhookfilename;
+
+ /* ignore wrong architecture packages (arch1>arch2 or flat) */
+ bool ignorewrongarchitecture;
+ /* if newly downloaded or not in done file */
+ bool new;
+ /* content needed (i.e. listhooks have to be run) */
+ bool needed;
+ /* there was something missed here */
+ bool failed;
+ /* do not generate 'done' file */
+ bool incomplete;
+};
+
+struct update_target {
+ /*@null@*/struct update_target *next;
+ /*@null@*/struct update_index_connector *indices;
+ /*@dependent@*/struct target *target;
+ /*@null@*/struct upgradelist *upgradelist;
+ /* Ignore delete marks (as some lists were missing) */
+ bool ignoredelete;
+ /* don't do anything because of --skipold */
+ bool nothingnew;
+ /* if true do not generate donefiles */
+ bool incomplete;
+};
+
+struct update_distribution {
+ struct update_distribution *next;
+ struct distribution *distribution;
+ struct update_pattern **patterns;
+ struct update_origin *origins;
+ struct update_target *targets;
+};
+
+static void update_pattern_free(/*@only@*/struct update_pattern *update) {
+ if (update == NULL)
+ return;
+ free(update->name);
+ free(update->from);
+ free(update->method);
+ free(update->fallback);
+ free(update->suite_from);
+ free(update->verifyrelease);
+ strlist_done(&update->config);
+ strlist_done(&update->architectures_from);
+ strlist_done(&update->architectures_into);
+ strlist_done(&update->components_from);
+ strlist_done(&update->components_into);
+ strlist_done(&update->udebcomponents_from);
+ strlist_done(&update->udebcomponents_into);
+ term_free(update->includecondition);
+ filterlist_release(&update->filterlist);
+ filterlist_release(&update->filtersrclist);
+ free(update->listhook);
+ free(update->shellhook);
+ remote_repository_free(update->repository);
+ free(update);
+}
+
+void updates_freepatterns(struct update_pattern *p) {
+ while (p != NULL) {
+ struct update_pattern *pattern;
+
+ pattern = p;
+ p = pattern->next;
+ update_pattern_free(pattern);
+ }
+}
+
+static void updates_freeorigins(/*@only@*/struct update_origin *o) {
+ while (o != NULL) {
+ struct update_origin *origin;
+
+ origin = o;
+ o = origin->next;
+ free(origin->suite_from);
+ free(origin);
+ }
+}
+
+static void updates_freetargets(/*@only@*/struct update_target *t) {
+ while (t != NULL) {
+ struct update_target *ut;
+
+ ut = t;
+ t = ut->next;
+ while (ut->indices != NULL) {
+ struct update_index_connector *ui;
+
+ ui = ut->indices;
+ ut->indices = ui->next;
+ free(ui->afterhookfilename);
+ free(ui);
+ }
+ free(ut);
+ }
+}
+
+void updates_freeupdatedistributions(struct update_distribution *d) {
+ while (d != NULL) {
+ struct update_distribution *next;
+
+ next = d->next;
+ free(d->patterns);
+ updates_freetargets(d->targets);
+ updates_freeorigins(d->origins);
+ free(d);
+ d = next;
+ }
+}
+
+static inline retvalue newupdatetarget(struct update_target **ts, /*@dependent@*/struct target *target) {
+ struct update_target *ut;
+
+ ut = malloc(sizeof(struct update_target));
+ if (FAILEDTOALLOC(ut))
+ return RET_ERROR_OOM;
+ ut->target = target;
+ ut->next = *ts;
+ ut->indices = NULL;
+ ut->upgradelist = NULL;
+ ut->ignoredelete = false;
+ ut->nothingnew = false;
+ ut->incomplete = false;
+ *ts = ut;
+ return RET_OK;
+}
+
+/****************************************************************************
+ * Step 1: parsing the conf/updates file with the patterns *
+ ****************************************************************************/
+
+CFlinkedlistinit(update_pattern)
+CFvalueSETPROC(update_pattern, name)
+CFvalueSETPROC(update_pattern, suite_from)
+CFatomSETPROC(update_pattern, flat, at_component)
+CFvalueSETPROC(update_pattern, from)
+CFurlSETPROC(update_pattern, method)
+CFurlSETPROC(update_pattern, fallback)
+/* what here? */
+CFallSETPROC(update_pattern, verifyrelease)
+CFlinelistSETPROC(update_pattern, config)
+CFtruthSETPROC(update_pattern, ignorerelease)
+CFtruthSETPROC(update_pattern, getinrelease)
+CFscriptSETPROC(update_pattern, listhook)
+CFallSETPROC(update_pattern, shellhook)
+CFfilterlistSETPROC(update_pattern, filterlist)
+CFfilterlistSETPROC(update_pattern, filtersrclist)
+CFtermSSETPROC(update_pattern, includecondition)
+CFtruthSETPROC(update_pattern, omitextrasource)
+
+CFUSETPROC(update_pattern, downloadlistsas) {
+ CFSETPROCVAR(update_pattern, this);
+ char *word;
+ const char *u;
+ retvalue r;
+ unsigned int e = 0;
+ enum compression c;
+
+ this->downloadlistsas_set = true;
+ r = config_getword(iter, &word);
+ while (RET_IS_OK(r)) {
+ bool force;
+ if (e >= ARRAYCOUNT(this->downloadlistsas.requested)) {
+ fprintf(stderr,
+"%s:%d:%d: Ignoring all but first %d entries...\n",
+ config_filename(iter),
+ config_markerline(iter),
+ config_markercolumn(iter),
+ (int)(ARRAYCOUNT(
+ this->downloadlistsas.requested)));
+ free(word);
+ break;
+ }
+ if (strncmp(word, "force.", 6) == 0) {
+ u = word + 5;
+ force = true;
+ } else {
+ u = word;
+ force = false;
+ }
+ for (c = 0 ; c < c_COUNT ; c++) {
+ if (strcmp(uncompression_config[c], u) == 0 ||
+ strcmp(uncompression_config[c]+1, u) == 0) {
+ break;
+ }
+ }
+ if (c < c_COUNT) {
+ this->downloadlistsas.requested[e].compression = c;
+ this->downloadlistsas.requested[e].diff = false;
+ this->downloadlistsas.requested[e].force = force;
+ e++;
+ free(word);
+ r = config_getword(iter, &word);
+ continue;
+ }
+ if (strcmp(u, ".diff") == 0 || strcmp(u, "diff") == 0) {
+ this->downloadlistsas.requested[e].compression = c_gzip;
+ this->downloadlistsas.requested[e].diff = true;
+ this->downloadlistsas.requested[e].force = force;
+ e++;
+ free(word);
+ r = config_getword(iter, &word);
+ continue;
+ }
+ fprintf(stderr,
+"%s:%d:%d: Error: unknown list download mode '%s'!\n",
+ config_filename(iter),
+ config_markerline(iter),
+ config_markercolumn(iter),
+ u);
+ free(word);
+ return RET_ERROR;
+ }
+ if (RET_WAS_ERROR(r))
+ return r;
+ this->downloadlistsas.count = e;
+ return RET_OK;
+}
+
+CFUSETPROC(update_pattern, components) {
+ CFSETPROCVAR(update_pattern, this);
+ retvalue r;
+ int i;
+
+ this->components_set = true;
+ r = config_getsplitwords(iter, "Components",
+ &this->components_from,
+ &this->components_into);
+ if (RET_IS_OK(r)) {
+ // TODO: instead of this save numbers directly...
+ for (i = 0 ; i < this->components_into.count ; i++) {
+ component_t c;
+ c = component_find(this->components_into.values[i]);
+ if (c == atom_unknown) {
+ fprintf(stderr,
+"Warning parsing %s, line %u: unknown component '%s' will be ignored!\n",
+ config_filename(iter),
+ config_markerline(iter),
+ this->components_into.values[i]);
+ }
+ }
+ }
+ return r;
+}
+
+CFUSETPROC(update_pattern, udebcomponents) {
+ CFSETPROCVAR(update_pattern, this);
+ retvalue r;
+ int i;
+
+ this->udebcomponents_set = true;
+ r = config_getsplitwords(iter, "UdebComponents",
+ &this->udebcomponents_from,
+ &this->udebcomponents_into);
+ if (RET_IS_OK(r)) {
+ // TODO: instead of this save numbers directly...
+ for (i = 0 ; i < this->udebcomponents_into.count ; i++) {
+ component_t c;
+ c = component_find(this->udebcomponents_into.values[i]);
+ if (c == atom_unknown) {
+ fprintf(stderr,
+"Warning parsing %s, line %u: unknown udeb component '%s' will be ignored!\n",
+ config_filename(iter),
+ config_markerline(iter),
+ this->udebcomponents_into.values[i]);
+ }
+ }
+ }
+ return r;
+}
+
+CFUSETPROC(update_pattern, architectures) {
+ CFSETPROCVAR(update_pattern, this);
+ retvalue r;
+ int i;
+
+ this->architectures_set = true;
+ r = config_getsplitwords(iter, "Architectures",
+ &this->architectures_from,
+ &this->architectures_into);
+ if (r == RET_NOTHING) {
+ strlist_init(&this->architectures_from);
+ strlist_init(&this->architectures_into);
+ fprintf(stderr,
+"Warning parsing %s, line %u: an empty Architectures field\n"
+"causes the whole pattern to do nothing.\n",
+ config_filename(iter),
+ config_markerline(iter));
+ }
+ if (RET_IS_OK(r)) {
+ // TODO: instead of this save numbers directly...
+ for (i = 0 ; i < this->architectures_into.count ; i++) {
+ architecture_t a;
+ a = architecture_find(this->architectures_into.values[i]);
+ if (a == atom_unknown) {
+ fprintf(stderr,
+"Warning parsing %s, line %u: unknown architecture '%s' will be ignored!\n",
+ config_filename(iter),
+ config_markerline(iter),
+ this->architectures_into.values[i]);
+ }
+ }
+ }
+ return r;
+}
+CFhashesSETPROC(update_pattern, ignorehashes);
+
+static const struct configfield updateconfigfields[] = {
+ CFr("Name", update_pattern, name),
+ CF("From", update_pattern, from),
+ CF("Method", update_pattern, method),
+ CF("Fallback", update_pattern, fallback),
+ CF("Config", update_pattern, config),
+ CF("Suite", update_pattern, suite_from),
+ CF("Architectures", update_pattern, architectures),
+ CF("Components", update_pattern, components),
+ CF("Flat", update_pattern, flat),
+ CF("UDebComponents", update_pattern, udebcomponents),
+ CF("GetInRelease", update_pattern, getinrelease),
+ CF("IgnoreRelease", update_pattern, ignorerelease),
+ CF("IgnoreHashes", update_pattern, ignorehashes),
+ CF("VerifyRelease", update_pattern, verifyrelease),
+ CF("ListHook", update_pattern, listhook),
+ CF("ListShellHook", update_pattern, shellhook),
+ CF("FilterFormula", update_pattern, includecondition),
+ CF("OmitExtraSourceOnly", update_pattern, omitextrasource),
+ CF("FilterList", update_pattern, filterlist),
+ CF("FilterSrcList", update_pattern, filtersrclist),
+ CF("DownloadListsAs", update_pattern, downloadlistsas)
+};
+
+CFfinishparse(update_pattern) {
+ CFUfinishparseVARS(update_pattern, n, last_p, mydata);
+
+ if (complete) {
+ if (n->components_set && atom_defined(n->flat)) {
+ fprintf(stderr,
+"%s:%u to %u: Update pattern may not contain Components and Flat fields ad the same time.\n",
+ config_filename(iter), config_firstline(iter),
+ config_line(iter));
+ return RET_ERROR;
+ }
+ if (n->udebcomponents_set && atom_defined(n->flat)) {
+ fprintf(stderr,
+"%s:%u to %u: Update pattern may not contain UDebComponents and Flat fields ad the same time.\n",
+ config_filename(iter), config_firstline(iter),
+ config_line(iter));
+ return RET_ERROR;
+ }
+ if (n->from != NULL && n->method != NULL) {
+ fprintf(stderr,
+"%s:%u to %u: Update pattern may not contain From: and Method: fields ad the same time.\n",
+ config_filename(iter), config_firstline(iter),
+ config_line(iter));
+ return RET_ERROR;
+ }
+ if (n->from == NULL && n->method == NULL) {
+ fprintf(stderr,
+"%s:%u to %u: Update pattern must either contain a Methods: field or reference another one with a From: field.\n",
+ config_filename(iter), config_firstline(iter),
+ config_line(iter));
+ return RET_ERROR;
+ }
+ if (n->from != NULL && n->fallback != NULL) {
+ fprintf(stderr,
+"%s:%u to %u: Update pattern may not contain From: and Fallback: fields ad the same time.\n",
+ config_filename(iter), config_firstline(iter),
+ config_line(iter));
+ return RET_ERROR;
+ }
+ if (n->from != NULL && n->config_set) {
+ fprintf(stderr,
+"%s:%u to %u: Update pattern may not contain From: and Config: fields ad the same time.\n",
+ config_filename(iter), config_firstline(iter),
+ config_line(iter));
+ return RET_ERROR;
+ }
+ if (n->suite_from != NULL && strcmp(n->suite_from, "*") != 0 &&
+ strncmp(n->suite_from, "*/", 2) != 0 &&
+ strchr(n->suite_from, '*') != NULL) {
+ fprintf(stderr,
+"%s:%u to %u: Unsupported suite pattern '%s'\n",
+ config_filename(iter), config_firstline(iter),
+ config_line(iter), n->suite_from);
+ return RET_ERROR;
+ }
+ if (n->listhook != NULL && n->shellhook != NULL) {
+ fprintf(stderr,
+"%s:%u to %u: Only one of ListHook and ListShellHook allowed per update rule\n",
+ config_filename(iter), config_firstline(iter),
+ config_line(iter));
+ return RET_ERROR;
+ }
+ }
+ return linkedlistfinish(privdata_update_pattern,
+ thisdata_update_pattern,
+ lastdata_p_update_pattern, complete, iter);
+}
+
+
+retvalue updates_getpatterns(struct update_pattern **patterns) {
+ struct update_pattern *update = NULL, *u, *v;
+ bool progress;
+ int i;
+ retvalue r;
+
+ r = configfile_parse("updates", IGNORABLE(unknownfield),
+ configparser_update_pattern_init,
+ finishparseupdate_pattern,
+ "update rule",
+ updateconfigfields, ARRAYCOUNT(updateconfigfields),
+ &update);
+ if (RET_IS_OK(r)) {
+ for (u = update ; u != NULL ; u = u->next) {
+ v = update;
+ while (v != NULL &&
+ (v == u || strcmp(v->name, u->name) != 0))
+ v = v->next;
+ if (v != NULL) {
+ // TODO: store line information...
+ fprintf(stderr,
+"%s/updates: Multiple update patterns named '%s'!\n",
+ global.confdir, u->name);
+ updates_freepatterns(update);
+ return RET_ERROR;
+ }
+ if (u->from == NULL)
+ continue;
+ v = update;
+ while (v != NULL && strcmp(v->name, u->from) != 0)
+ v = v->next;
+ if (v == NULL) {
+ fprintf(stderr,
+"%s/updates: Update pattern '%s' references unknown pattern '%s' via From!\n",
+ global.confdir, u->name, u->from);
+ updates_freepatterns(update);
+ return RET_ERROR;
+ }
+ u->pattern_from = v;
+ }
+ /* check for circular references */
+ do {
+ progress = false;
+ for (u = update ; u != NULL ; u = u->next) {
+ if (u->visited)
+ continue;
+ if (u->pattern_from == NULL ||
+ u->pattern_from->visited) {
+ u->visited = true;
+ progress = true;
+ }
+ }
+ } while (progress);
+ u = update;
+ while (u != NULL && u->visited)
+ u = u->next;
+ if (u != NULL) {
+ /* The actual error is more likely found later.
+ * If someone creates a cycle and a chain into that
+ * more than 1000 rules long, having a slightly
+ * misleading error message will be the last of
+ * their problems... */
+ for (i = 0 ; i < 1000 ; i++) {
+ u = u->pattern_from;
+ assert (u != NULL && !u->visited);
+ }
+ fprintf(stderr,
+"Error: Update rule '%s' part of circular From-referencing.\n",
+ u->name);
+ updates_freepatterns(update);
+ return RET_ERROR;
+ }
+ *patterns = update;
+ } else if (r == RET_NOTHING) {
+ assert (update == NULL);
+ *patterns = NULL;
+ r = RET_OK;
+ } else {
+ if (r == RET_ERROR_UNKNOWNFIELD)
+ (void)fputs(
+"To ignore unknown fields use --ignore=unknownfield\n",
+ stderr);
+ updates_freepatterns(update);
+ }
+ return r;
+}
+
+static inline void markfound(int count, struct update_pattern * const *patterns, const struct update_pattern *lookfor, const struct strlist *searched, const struct strlist *have, bool *found, bool (*hasattribute)(const struct update_pattern*)) {
+ int i, j, o;
+
+ for (i = 0 ; i < count ; i++) {
+ const struct update_pattern *p = patterns[i];
+
+ /* check if this uses this attribute */
+ while (p != NULL && !hasattribute(p))
+ p = p->pattern_from;
+ if (p != lookfor)
+ continue;
+
+ for (j = 0 ; j < have->count ; j++) {
+ o = strlist_ofs(searched, have->values[j]);
+ if (o >= 0)
+ found[o] = true;
+ }
+ break;
+ }
+}
+
+static inline bool hasarchitectures(const struct update_pattern *p) {
+ return p->architectures_set;
+}
+static inline bool hascomponents(const struct update_pattern *p) {
+ return p->components_set;
+}
+static inline bool hasudebcomponents(const struct update_pattern *p) {
+ return p->udebcomponents_set;
+}
+
+/****************************************************************************
+ * Step 2: create rules for some distribution based on those patterns *
+ ****************************************************************************/
+
+static retvalue new_deleterule(struct update_origin **origins) {
+
+ struct update_origin *update;
+
+ update = zNEW(struct update_origin);
+ if (FAILEDTOALLOC(update))
+ return RET_ERROR_OOM;
+
+ *origins = update;
+ return RET_OK;
+}
+
+static inline char *translate_suite_pattern(const struct update_pattern *p, const char *codename) {
+ /* look for first specified suite: */
+ while (p != NULL && p->suite_from == NULL)
+ p = p->pattern_from;
+
+ if (p == NULL || strcmp(p->suite_from, "*") == 0)
+ return strdup(codename);
+ if (p->suite_from[0] == '*' && p->suite_from[1] == '/')
+ return calc_dirconcat(codename, p->suite_from + 2);
+ else if (strchr(p->suite_from, '*') == NULL)
+ return strdup(p->suite_from);
+ //TODO: implement this
+ // but already checked in parsing...
+ assert(0);
+ return NULL;
+}
+
+static retvalue instance_pattern(struct update_pattern *pattern, const struct distribution *distribution, struct update_origin **origins) {
+
+ struct update_origin *update;
+ /*@dependant@*/struct update_pattern *declaration, *p, *listscomponents;
+ bool ignorehashes[cs_hashCOUNT], ignorerelease, getinrelease;
+ const char *verifyrelease;
+ retvalue r;
+
+ update = zNEW(struct update_origin);
+ if (FAILEDTOALLOC(update))
+ return RET_ERROR_OOM;
+
+ update->suite_from = translate_suite_pattern(pattern,
+ distribution->codename);
+ if (FAILEDTOALLOC(update->suite_from)) {
+ free(update);
+ return RET_ERROR_OOM;
+ }
+ if (!pattern->used) {
+ declaration = pattern;
+ while (declaration->pattern_from != NULL)
+ declaration = declaration->pattern_from;
+ if (declaration->repository == NULL)
+ declaration->repository = remote_repository_prepare(
+ declaration->name, declaration->method,
+ declaration->fallback,
+ &declaration->config);
+ if (FAILEDTOALLOC(declaration->repository)) {
+ free(update->suite_from);
+ free(update);
+ return RET_ERROR_OOM;
+ }
+ pattern->used = true;
+ } else {
+ declaration = pattern;
+ while (declaration->pattern_from != NULL)
+ declaration = declaration->pattern_from;
+ assert (declaration->repository != NULL);
+ }
+
+ update->distribution = distribution;
+ update->pattern = pattern;
+ update->failed = false;
+
+ p = pattern;
+ while (p != NULL && !p->ignorerelease_set)
+ p = p->pattern_from;
+ if (p == NULL)
+ ignorerelease = false;
+ else
+ ignorerelease = p->ignorerelease;
+ p = pattern;
+ while (p != NULL && !p->getinrelease_set)
+ p = p->pattern_from;
+ if (p == NULL)
+ getinrelease = true;
+ else
+ getinrelease = p->getinrelease;
+ /* find the first set values: */
+ p = pattern;
+ while (p != NULL && p->verifyrelease == NULL)
+ p = p->pattern_from;
+ if (p == NULL)
+ verifyrelease = NULL;
+ else
+ verifyrelease = p->verifyrelease;
+ if (!ignorerelease && verifyrelease == NULL && verbose >= 0) {
+ fprintf(stderr,
+"Warning: No VerifyRelease line in '%s' or any rule it includes via 'From:'.\n"
+"Release.gpg cannot be checked unless you tell which key to check with.\n"
+"(To avoid this warning and not check signatures add 'VerifyRelease: blindtrust').\n",
+ pattern->name);
+
+ }
+ p = pattern;
+ while (p != NULL && !p->ignorehashes_set)
+ p = p->pattern_from;
+ if (p == NULL)
+ memset(ignorehashes, 0, sizeof(ignorehashes));
+ else {
+ assert (sizeof(ignorehashes) == sizeof(p->ignorehashes));
+ memcpy(ignorehashes, p->ignorehashes, sizeof(ignorehashes));
+ }
+
+ listscomponents = NULL;
+ p = pattern;
+ while (p != NULL && !atom_defined(p->flat)) {
+ if (p->components_set || p->udebcomponents_set)
+ listscomponents = p;
+ p = p->pattern_from;
+ }
+ update->flat = p != NULL;
+ if (update->flat && listscomponents != NULL) {
+ fprintf(stderr,
+"WARNING: update pattern '%s' (first encountered via '%s' in '%s')\n"
+"sets components that are always ignored as '%s' sets Flat mode.\n",
+ listscomponents->name, pattern->name,
+ distribution->codename, p->name);
+ }
+ if (p != NULL && !atomlist_in(&distribution->components, p->flat)) {
+ fprintf(stderr,
+"Error: distribution '%s' uses flat update pattern '%s'\n"
+"with target component '%s' which it does not contain!\n",
+ distribution->codename,
+ pattern->name, atoms_components[p->flat]);
+ updates_freeorigins(update);
+ return RET_ERROR;
+ }
+ r = remote_distribution_prepare(declaration->repository,
+ update->suite_from, ignorerelease,
+ getinrelease, verifyrelease, update->flat,
+ ignorehashes, &update->from);
+ if (RET_WAS_ERROR(r)) {
+ updates_freeorigins(update);
+ return r;
+ }
+
+ *origins = update;
+ return RET_OK;
+}
+
+static retvalue findpatterns(struct update_pattern *patterns, const struct distribution *distribution, struct update_pattern ***patterns_p) {
+ int i;
+ struct update_pattern **used_patterns;
+
+ if (distribution->updates.count == 0)
+ return RET_NOTHING;
+
+ used_patterns = nzNEW(distribution->updates.count,
+ struct update_pattern *);
+ if (FAILEDTOALLOC(used_patterns))
+ return RET_ERROR_OOM;
+
+ for (i = 0; i < distribution->updates.count ; i++) {
+ const char *name = distribution->updates.values[i];
+ struct update_pattern *pattern;
+
+ if (strcmp(name, "-") == 0)
+ continue;
+
+ pattern = patterns;
+ while (pattern != NULL && strcmp(name, pattern->name) != 0)
+ pattern = pattern->next;
+ if (pattern == NULL) {
+ fprintf(stderr,
+"Cannot find definition of upgrade-rule '%s' for distribution '%s'!\n",
+ name, distribution->codename);
+ if (distribution->selected) {
+ free(used_patterns);
+ return RET_ERROR;
+ } else
+ fprintf(stderr,
+"This is no error now as '%s' is not used, bug might cause spurious warnings...\n",
+ distribution->codename);
+ }
+ used_patterns[i] = pattern;
+ }
+ *patterns_p = used_patterns;
+ return RET_OK;
+}
+
+static retvalue getorigins(struct update_distribution *d) {
+ const struct distribution *distribution = d->distribution;
+ struct update_origin *updates = NULL;
+ retvalue result;
+ int i;
+
+ assert (d->patterns != NULL);
+
+ result = RET_NOTHING;
+ for (i = 0; i < distribution->updates.count ; i++) {
+ struct update_pattern *pattern = d->patterns[i];
+ struct update_origin *update SETBUTNOTUSED(= NULL);
+ retvalue r;
+
+ if (pattern == NULL) {
+ assert (strcmp(distribution->updates.values[i], "-") == 0);
+ r = new_deleterule(&update);
+ } else {
+ r = instance_pattern(pattern, distribution, &update);
+ }
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ break;
+ if (RET_IS_OK(r)) {
+ assert (update != NULL);
+ update->next = updates;
+ updates = update;
+ }
+ }
+
+ if (RET_WAS_ERROR(result)) {
+ updates_freeorigins(updates);
+ } else {
+ d->origins = updates;
+ }
+ return result;
+}
+
+/****************************************************************************
+ * Step 3: calculate which remote indices are to be retrieved and processed *
+ ****************************************************************************/
+
+static inline bool addremoteindex(struct update_origin *origin, struct target *target, struct update_target *updatetargets, const char *architecture, const char *component) {
+ struct update_index_connector *uindex;
+ const struct update_pattern *p;
+
+ uindex = zNEW(struct update_index_connector);
+ if (FAILEDTOALLOC(uindex))
+ return false;
+
+ p = origin->pattern;
+ while (p != NULL && !p->downloadlistsas_set)
+ p = p->pattern_from;
+
+ uindex->origin = origin;
+ uindex->remote = remote_index(origin->from,
+ architecture, component,
+ target->packagetype,
+ (p == NULL)?NULL:&p->downloadlistsas);
+ if (FAILEDTOALLOC(uindex->remote)) {
+ free(uindex);
+ return false;
+ }
+ assert (!origin->flat);
+ uindex->next = updatetargets->indices;
+ uindex->ignorewrongarchitecture = strcmp(architecture,
+ atoms_architectures[
+ target->architecture]) != 0;
+ updatetargets->indices = uindex;
+ return true;
+}
+
+static retvalue addorigintotarget(struct update_origin *origin, struct target *target, struct distribution *distribution, struct update_target *updatetargets) {
+ const struct update_pattern *p;
+ const struct strlist *c_from = NULL, *c_into = NULL;
+ const struct strlist *a_from = NULL, *a_into = NULL;
+ const char *architecture = atoms_architectures[target->architecture];
+ const char *component = atoms_components[target->component];
+ int ai, ci;
+
+ assert (origin != NULL && origin->pattern != NULL);
+
+ p = origin->pattern;
+ while (p != NULL && !p->architectures_set)
+ p = p->pattern_from;
+ if (p != NULL) {
+ a_from = &p->architectures_from;
+ a_into = &p->architectures_into;
+ }
+ p = origin->pattern;
+ if (target->packagetype == pt_udeb) {
+ while (p != NULL && !p->udebcomponents_set)
+ p = p->pattern_from;
+ if (p != NULL) {
+ c_from = &p->udebcomponents_from;
+ c_into = &p->udebcomponents_into;
+ }
+ } else {
+ while (p != NULL && !p->components_set)
+ p = p->pattern_from;
+ if (p != NULL) {
+ c_from = &p->components_from;
+ c_into = &p->components_into;
+ }
+ }
+
+ if (a_into == NULL) {
+ assert (atomlist_in(&distribution->architectures,
+ target->architecture));
+
+ if (c_into == NULL) {
+ if (!addremoteindex(origin, target, updatetargets,
+ architecture, component))
+ return RET_ERROR_OOM;
+ return RET_OK;
+ }
+ for (ci = 0 ; ci < c_into->count ; ci++) {
+ if (strcmp(c_into->values[ci], component) != 0)
+ continue;
+
+ if (!addremoteindex(origin, target, updatetargets,
+ architecture, c_from->values[ci]))
+ return RET_ERROR_OOM;
+ }
+ return RET_OK;
+ }
+ for (ai = 0 ; ai < a_into->count ; ai++) {
+ if (strcmp(architecture, a_into->values[ai]) != 0)
+ continue;
+ if (c_into == NULL) {
+ if (!addremoteindex(origin, target, updatetargets,
+ a_from->values[ai], component))
+ return RET_ERROR_OOM;
+ continue;
+ }
+
+ for (ci = 0 ; ci < c_into->count ; ci++) {
+ if (strcmp(component, c_into->values[ci]) != 0)
+ continue;
+
+ if (!addremoteindex(origin, target, updatetargets,
+ a_from->values[ai], c_from->values[ci]))
+ return RET_ERROR_OOM;
+ }
+ }
+ return RET_OK;
+}
+
+static retvalue addflatorigintotarget(struct update_origin *origin, struct target *target, struct update_target *updatetargets) {
+ const struct update_pattern *p;
+ const struct strlist *a_into;
+ const struct encoding_preferences *downloadlistsas;
+ int ai;
+
+ assert (origin != NULL);
+
+ if (target->packagetype == pt_udeb)
+ return RET_NOTHING;
+
+ p = origin->pattern;
+ while (p != NULL && !p->downloadlistsas_set)
+ p = p->pattern_from;
+ if (p == NULL)
+ downloadlistsas = NULL;
+ else
+ downloadlistsas = &p->downloadlistsas;
+
+ p = origin->pattern;
+ while (p != NULL && !atom_defined(p->flat))
+ p = p->pattern_from;
+ assert (p != NULL);
+ if (p->flat != target->component)
+ return RET_NOTHING;
+
+ p = origin->pattern;
+ while (p != NULL && !p->architectures_set)
+ p = p->pattern_from;
+ if (p == NULL) {
+ struct update_index_connector *uindex;
+
+ uindex = zNEW(struct update_index_connector);
+ if (FAILEDTOALLOC(uindex))
+ return RET_ERROR_OOM;
+
+
+ uindex->origin = origin;
+ uindex->remote = remote_flat_index(origin->from,
+ target->packagetype,
+ downloadlistsas);
+ if (FAILEDTOALLOC(uindex->remote)) {
+ free(uindex);
+ return RET_ERROR_OOM;
+ }
+ uindex->next = updatetargets->indices;
+ assert (origin->flat);
+ uindex->ignorewrongarchitecture = true;
+ updatetargets->indices = uindex;
+ return RET_OK;
+ }
+
+ a_into = &p->architectures_into;
+
+ for (ai = 0 ; ai < a_into->count ; ai++) {
+ struct update_index_connector *uindex;
+ const char *a = atoms_architectures[target->architecture];
+
+ if (strcmp(a_into->values[ai], a) != 0)
+ continue;
+
+ uindex = zNEW(struct update_index_connector);
+ if (FAILEDTOALLOC(uindex))
+ return RET_ERROR_OOM;
+
+ uindex->origin = origin;
+ uindex->remote = remote_flat_index(origin->from,
+ target->packagetype,
+ downloadlistsas);
+ if (FAILEDTOALLOC(uindex->remote)) {
+ free(uindex);
+ return RET_ERROR_OOM;
+ }
+ uindex->next = updatetargets->indices;
+ assert (origin->flat);
+ uindex->ignorewrongarchitecture = true;
+ updatetargets->indices = uindex;
+ }
+ return RET_OK;
+}
+
+static retvalue adddeleteruletotarget(struct update_target *updatetargets) {
+ struct update_index_connector *uindex;
+
+ uindex = zNEW(struct update_index_connector);
+ if (FAILEDTOALLOC(uindex))
+ return RET_ERROR_OOM;
+ uindex->next = updatetargets->indices;
+ updatetargets->indices = uindex;
+ return RET_OK;
+}
+
+static retvalue gettargets(struct update_origin *origins, struct distribution *distribution, const struct atomlist *components, const struct atomlist *architectures, const struct atomlist *types, struct update_target **ts) {
+ struct target *target;
+ struct update_origin *origin;
+ struct update_target *updatetargets;
+ retvalue r;
+
+ updatetargets = NULL;
+
+ for (target = distribution->targets ; target != NULL ;
+ target = target->next) {
+ if (!target_matches(target, components, architectures, types))
+ continue;
+ r = newupdatetarget(&updatetargets, target);
+ if (RET_WAS_ERROR(r)) {
+ updates_freetargets(updatetargets);
+ return r;
+ }
+
+ for (origin = origins ; origin != NULL ; origin=origin->next) {
+ if (origin->pattern == NULL)
+ r = adddeleteruletotarget(updatetargets);
+ else if (!origin->flat)
+ r = addorigintotarget(origin, target,
+ distribution, updatetargets);
+ else
+ r = addflatorigintotarget(origin, target,
+ updatetargets);
+ if (RET_WAS_ERROR(r)) {
+ updates_freetargets(updatetargets);
+ return r;
+ }
+ }
+ }
+
+ *ts = updatetargets;
+ return RET_OK;
+}
+
+static inline retvalue findmissingupdate(const struct distribution *distribution, struct update_origin *updates) {
+ retvalue result;
+ struct update_origin *last;
+ int count;
+
+ assert (updates != NULL);
+ last = updates;
+ count = 1;
+ while (last->next != NULL) {
+ last = last->next;
+ count++;
+ }
+
+ result = RET_OK;
+
+ if (count != distribution->updates.count) {
+ int i;
+
+ // TODO: why is this here? can this actually happen?
+
+ for (i=0; i<distribution->updates.count; i++){
+ const char *update = distribution->updates.values[i];
+ struct update_origin *u;
+
+ u = updates;
+ while (u != NULL && strcmp(u->pattern->name, update) != 0)
+ u = u->next;
+ if (u == NULL) {
+ fprintf(stderr,
+"Update '%s' is listed in distribution '%s', but was not found!\n",
+ update, distribution->codename);
+ result = RET_ERROR_MISSING;
+ break;
+ }
+ }
+ if (RET_IS_OK(result)) {
+ fprintf(stderr,
+"Did you write an update two times in the update-line of '%s'?\n",
+ distribution->codename);
+ result = RET_NOTHING;
+ }
+ }
+
+ return result;
+}
+
+retvalue updates_calcindices(struct update_pattern *patterns, struct distribution *distributions, const struct atomlist *components, const struct atomlist *architectures, const struct atomlist *types, struct update_distribution **update_distributions) {
+ struct distribution *distribution;
+ struct update_distribution *u_ds;
+ retvalue result, r;
+
+ u_ds = NULL;
+ result = RET_NOTHING;
+
+ for (distribution = distributions ; distribution != NULL ;
+ distribution = distribution->next) {
+ struct update_distribution *u_d;
+ struct update_pattern **translated_updates;
+
+ if (!distribution->selected)
+ continue;
+
+ r = findpatterns(patterns, distribution, &translated_updates);
+ if (r == RET_NOTHING)
+ continue;
+ if (RET_WAS_ERROR(r)) {
+ result = r;
+ break;
+ }
+
+ u_d = zNEW(struct update_distribution);
+ if (FAILEDTOALLOC(u_d)) {
+ free(translated_updates);
+ result = RET_ERROR_OOM;
+ break;
+ }
+
+ u_d->distribution = distribution;
+ u_d->patterns = translated_updates;
+ u_d->next = u_ds;
+ u_ds = u_d;
+
+ r = getorigins(u_d);
+ if (RET_WAS_ERROR(r)) {
+ result = r;
+ break;
+ }
+ if (RET_IS_OK(r)) {
+ /* Check if we got all: */
+ r = findmissingupdate(distribution, u_d->origins);
+ if (RET_WAS_ERROR(r)) {
+ result = r;
+ break;
+ }
+
+ r = gettargets(u_d->origins, distribution,
+ components, architectures, types,
+ &u_d->targets);
+ if (RET_WAS_ERROR(r)) {
+ result = r;
+ break;
+ }
+ }
+ result = RET_OK;
+ }
+ if (RET_IS_OK(result)) {
+ *update_distributions = u_ds;
+ } else
+ updates_freeupdatedistributions(u_ds);
+ return result;
+}
+
+/****************************************************************************
+ * Step 5: preperations for actually doing anything: *
+ * - printing some warnings *
+ * - prepare distribution for writing *
+ * - rest moved to remote_startup *
+ ****************************************************************************/
+
+static retvalue updates_startup(struct aptmethodrun *run, struct update_distribution *distributions, bool willwrite) {
+ retvalue r;
+ struct update_distribution *d;
+
+ for (d=distributions ; d != NULL ; d=d->next) {
+ if (willwrite) {
+ r = distribution_prepareforwriting(d->distribution);
+ if (RET_WAS_ERROR(r))
+ return r;
+ }
+ r = distribution_loadalloverrides(d->distribution);
+ if (RET_WAS_ERROR(r))
+ return r;
+ }
+ return remote_startup(run);
+}
+
+/****************************************************************************
+ * Step 6: queue downloading of list of lists (Release, Release.gpg, ...) *
+ ****************************************************************************
+ -> moved to remoterepository.c */
+
+/****************************************************************************
+ * Step 7: queue downloading of lists *
+ * (using information from previously downloaded meta-lists) *
+ ****************************************************************************
+ -> moved to remoterepository.c */
+
+/****************************************************************************
+ * Step 8: call possible list hooks allowing them to modify the lists *
+ ****************************************************************************/
+
+static retvalue calllisthook(struct update_target *ut, struct update_index_connector *f, const char *listhook) {
+ struct update_origin *origin = f->origin;
+ const char *oldfilename = remote_index_file(f->remote);
+ const char *oldbasefilename = remote_index_basefile(f->remote);
+ char *newfilename;
+ pid_t child, c;
+ int status;
+
+ /* distribution, component, architecture and pattern specific... */
+ newfilename = genlistsfilename(oldbasefilename, 5, "",
+ ut->target->distribution->codename,
+ atoms_components[ut->target->component],
+ atoms_architectures[ut->target->architecture],
+ origin->pattern->name, ENDOFARGUMENTS);
+ if (FAILEDTOALLOC(newfilename))
+ return RET_ERROR_OOM;
+ child = fork();
+ if (child < 0) {
+ int e = errno;
+ free(newfilename);
+ fprintf(stderr, "Error %d while forking for listhook: %s\n",
+ e, strerror(e));
+ return RET_ERRNO(e);
+ }
+ if (child == 0) {
+ int e;
+ (void)closefrom(3);
+ sethookenvironment(NULL, NULL, NULL, NULL);
+ setenv("REPREPRO_FILTER_CODENAME",
+ ut->target->distribution->codename, true);
+ setenv("REPREPRO_FILTER_PACKAGETYPE",
+ atoms_architectures[ut->target->packagetype],
+ true);
+ setenv("REPREPRO_FILTER_COMPONENT",
+ atoms_components[ut->target->component],
+ true);
+ setenv("REPREPRO_FILTER_ARCHITECTURE",
+ atoms_architectures[ut->target->architecture],
+ true);
+ setenv("REPREPRO_FILTER_PATTERN", origin->pattern->name, true);
+ execl(listhook, listhook, oldfilename, newfilename,
+ ENDOFARGUMENTS);
+ e = errno;
+ fprintf(stderr, "Error %d while executing '%s': %s\n",
+ e, listhook, strerror(e));
+ exit(255);
+ }
+ if (verbose > 5)
+ fprintf(stderr, "Called %s '%s' '%s'\n", listhook,
+ oldfilename, newfilename);
+ f->afterhookfilename = newfilename;
+ do {
+ c = waitpid(child, &status, WUNTRACED);
+ if (c < 0) {
+ int e = errno;
+ fprintf(stderr,
+"Error %d while waiting for hook '%s' to finish: %s\n",
+ e, listhook, strerror(e));
+ return RET_ERRNO(e);
+ }
+ } while (c != child);
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) == 0) {
+ if (verbose > 5)
+ fprintf(stderr,
+"Listhook successfully returned!\n");
+ return RET_OK;
+ } else {
+ fprintf(stderr,
+"Listhook failed with exitcode %d!\n",
+ (int)WEXITSTATUS(status));
+ return RET_ERROR;
+ }
+ } else {
+ fprintf(stderr,
+"Listhook terminated abnormally. (status is %x)!\n",
+ status);
+ return RET_ERROR;
+ }
+ return RET_OK;
+}
+
+static retvalue callshellhook(struct update_target *ut, struct update_index_connector *f, const char *shellhook) {
+ struct update_origin *origin = f->origin;
+ const char *oldfilename = remote_index_file(f->remote);
+ const char *oldbasefilename = remote_index_basefile(f->remote);
+ char *newfilename;
+ pid_t child, c;
+ int status;
+ int infd, outfd;
+
+ /* distribution, component, architecture and pattern specific... */
+ newfilename = genlistsfilename(oldbasefilename, 5, "",
+ ut->target->distribution->codename,
+ atoms_components[ut->target->component],
+ atoms_architectures[ut->target->architecture],
+ origin->pattern->name, ENDOFARGUMENTS);
+ if (FAILEDTOALLOC(newfilename))
+ return RET_ERROR_OOM;
+ infd = open(oldfilename, O_RDONLY|O_NOCTTY|O_NOFOLLOW);
+ if (infd < 0) {
+ int e = errno;
+
+ fprintf(stderr,
+"Error %d opening expected file '%s': %s!\n"
+"Something strange must go on!\n", e, oldfilename, strerror(e));
+ return RET_ERRNO(e);
+ }
+ (void)unlink(newfilename);
+ outfd = open(newfilename,
+ O_WRONLY|O_NOCTTY|O_NOFOLLOW|O_CREAT|O_EXCL, 0666);
+ if (outfd < 0) {
+ int e = errno;
+
+ fprintf(stderr, "Error %d creating '%s': %s!\n", e,
+ newfilename, strerror(e));
+ close(infd);
+ return RET_ERRNO(e);
+ }
+ child = fork();
+ if (child < 0) {
+ int e = errno;
+ free(newfilename);
+ fprintf(stderr, "Error %d while forking for shell hook: %s\n",
+ e, strerror(e));
+ (void)close(infd);
+ (void)close(outfd);
+ (void)unlink(newfilename);
+ return RET_ERRNO(e);
+ }
+ if (child == 0) {
+ int e;
+
+ assert (dup2(infd, 0) == 0);
+ assert (dup2(outfd, 1) == 1);
+ close(infd);
+ close(outfd);
+ (void)closefrom(3);
+ sethookenvironment(NULL, NULL, NULL, NULL);
+ setenv("REPREPRO_FILTER_CODENAME",
+ ut->target->distribution->codename, true);
+ setenv("REPREPRO_FILTER_PACKAGETYPE",
+ atoms_architectures[ut->target->packagetype],
+ true);
+ setenv("REPREPRO_FILTER_COMPONENT",
+ atoms_components[ut->target->component],
+ true);
+ setenv("REPREPRO_FILTER_ARCHITECTURE",
+ atoms_architectures[ut->target->architecture],
+ true);
+ setenv("REPREPRO_FILTER_PATTERN", origin->pattern->name, true);
+ execlp("sh", "sh", "-c", shellhook, ENDOFARGUMENTS);
+ e = errno;
+ fprintf(stderr, "Error %d while executing sh -c '%s': %s\n",
+ e, shellhook, strerror(e));
+ exit(255);
+ }
+ (void)close(infd);
+ (void)close(outfd);
+ if (verbose > 5)
+ fprintf(stderr, "Called sh -c '%s' <'%s' >'%s'\n", shellhook,
+ oldfilename, newfilename);
+ f->afterhookfilename = newfilename;
+ do {
+ c = waitpid(child, &status, WUNTRACED);
+ if (c < 0) {
+ int e = errno;
+ fprintf(stderr,
+"Error %d while waiting for shell hook '%s' to finish: %s\n",
+ e, shellhook, strerror(e));
+ return RET_ERRNO(e);
+ }
+ } while (c != child);
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) == 0) {
+ if (verbose > 5)
+ fprintf(stderr,
+"shell hook successfully returned!\n");
+ return RET_OK;
+ } else {
+ fprintf(stderr,
+"shell hook '%s' failed with exitcode %d!\n",
+ shellhook, (int)WEXITSTATUS(status));
+ return RET_ERROR;
+ }
+ } else {
+ fprintf(stderr,
+"shell hook '%s' terminated abnormally. (status is %x)!\n",
+ shellhook, status);
+ return RET_ERROR;
+ }
+ return RET_OK;
+}
+
+static retvalue calllisthooks(struct update_distribution *d) {
+ retvalue result, r;
+ struct update_target *target;
+ struct update_index_connector *uindex;
+
+ result = RET_NOTHING;
+ for (target = d->targets; target != NULL ; target = target->next) {
+ if (target->nothingnew)
+ continue;
+ /* if anything is new, we will to need to look at
+ * all (in case there are delete rules) */
+ for (uindex = target->indices ; uindex != NULL ;
+ uindex = uindex->next) {
+ const struct update_pattern *p;
+
+ if (uindex->remote == NULL)
+ continue;
+ if (uindex->failed)
+ continue;
+ p = uindex->origin->pattern;
+ while (p != NULL && p->listhook == NULL
+ && p->shellhook == NULL)
+ p = p->pattern_from;
+ if (p == NULL)
+ continue;
+ if (p->listhook != NULL)
+ r = calllisthook(target, uindex, p->listhook);
+ else {
+ assert (p->shellhook != NULL);
+ r = callshellhook(target, uindex, p->shellhook);
+ }
+ if (RET_WAS_ERROR(r)) {
+ uindex->failed = true;
+ return r;
+ }
+ RET_UPDATE(result, r);
+ }
+ }
+ return result;
+}
+
+static retvalue updates_calllisthooks(struct update_distribution *distributions) {
+ retvalue result, r;
+ struct update_distribution *d;
+
+ result = RET_NOTHING;
+ for (d=distributions ; d != NULL ; d=d->next) {
+ r = calllisthooks(d);
+ RET_UPDATE(result, r);
+ }
+ return result;
+}
+
+/****************************************************************************
+ * Step 9: search for missing packages i.e. needing to be added or upgraded *
+ * (all the logic in upgradelist.c, this is only clue code) *
+ ****************************************************************************/
+
+static upgrade_decision ud_decide_by_pattern(void *privdata, struct target *target, struct package *new, /*@null@*/const char *old_version) {
+ const struct update_pattern *pattern = privdata, *p;
+ retvalue r;
+ upgrade_decision decision = UD_UPGRADE;
+ enum filterlisttype listdecision;
+ bool cmdline_still_undecided;
+
+ if (target->packagetype == pt_dsc) {
+ p = pattern;
+ while (p != NULL && !p->filtersrclist.set)
+ p = p->pattern_from;
+ if (p != NULL)
+ listdecision = filterlist_find(new->name, new->version,
+ &p->filtersrclist);
+ else {
+ p = pattern;
+ while (p != NULL && !p->filterlist.set)
+ p = p->pattern_from;
+ if (p == NULL)
+ listdecision = flt_install;
+ else
+ listdecision = filterlist_find(new->name,
+ new->version, &p->filterlist);
+ }
+ } else {
+ p = pattern;
+ while (p != NULL && !p->filterlist.set)
+ p = p->pattern_from;
+ if (p != NULL)
+ listdecision = filterlist_find(new->name, new->version,
+ &p->filterlist);
+ else {
+ p = pattern;
+ while (p != NULL && !p->filtersrclist.set)
+ p = p->pattern_from;
+ if (p == NULL)
+ listdecision = flt_install;
+ else
+ listdecision = filterlist_find(new->source,
+ new->sourceversion,
+ &p->filtersrclist);
+ }
+ }
+
+ switch (listdecision) {
+ case flt_deinstall:
+ case flt_purge:
+ return UD_NO;
+ case flt_warning:
+ return UD_LOUDNO;
+ case flt_supersede:
+ decision = UD_SUPERSEDE;
+ break;
+ case flt_hold:
+ decision = UD_HOLD;
+ break;
+ case flt_error:
+ /* cannot yet be handled! */
+ fprintf(stderr,
+"Package name marked to be unexpected('error'): '%s'!\n", new->name);
+ return UD_ERROR;
+ case flt_upgradeonly:
+ if (old_version == NULL)
+ return UD_NO;
+ break;
+ case flt_install:
+ break;
+ case flt_unchanged:
+ case flt_auto_hold:
+ assert (listdecision != listdecision);
+ }
+
+ cmdline_still_undecided = false;
+ switch (filterlist_find(new->source, new->sourceversion,
+ &cmdline_src_filter)) {
+ case flt_deinstall:
+ case flt_purge:
+ return UD_NO;
+ case flt_warning:
+ return UD_LOUDNO;
+ case flt_auto_hold:
+ cmdline_still_undecided = true;
+ decision = UD_HOLD;
+ break;
+ case flt_hold:
+ decision = UD_HOLD;
+ break;
+ case flt_supersede:
+ decision = UD_SUPERSEDE;
+ break;
+ case flt_error:
+ /* cannot yet be handled! */
+ fprintf(stderr,
+"Package name marked to be unexpected('error'): '%s'!\n", new->name);
+ return UD_ERROR;
+ case flt_upgradeonly:
+ if (old_version == NULL)
+ return UD_NO;
+ break;
+ case flt_install:
+ decision = UD_UPGRADE;
+ break;
+ case flt_unchanged:
+ cmdline_still_undecided = true;
+ break;
+ }
+
+
+ if (target->packagetype != pt_dsc) {
+ switch (filterlist_find(new->name, new->version,
+ &cmdline_bin_filter)) {
+ case flt_deinstall:
+ case flt_purge:
+ return UD_NO;
+ case flt_warning:
+ return UD_LOUDNO;
+ case flt_supersede:
+ decision = UD_SUPERSEDE;
+ break;
+ case flt_hold:
+ decision = UD_HOLD;
+ break;
+ case flt_error:
+ /* cannot yet be handled! */
+ fprintf(stderr,
+"Package name marked to be unexpected('error'): '%s'!\n", new->name);
+ return UD_ERROR;
+ case flt_upgradeonly:
+ if (old_version == NULL)
+ return UD_NO;
+ break;
+ case flt_install:
+ decision = UD_UPGRADE;
+ break;
+ case flt_unchanged:
+ break;
+ case flt_auto_hold:
+ /* hold only if it was not in the src-filter */
+ if (cmdline_still_undecided)
+ decision = UD_HOLD;
+ break;
+ }
+ }
+
+ p = pattern;
+ while (p != NULL && !p->includecondition_set)
+ p = p->pattern_from;
+ if (p != NULL) {
+ r = term_decidepackage(p->includecondition, new, target);
+ if (RET_WAS_ERROR(r))
+ return UD_ERROR;
+ if (r == RET_NOTHING) {
+ return UD_NO;
+ }
+ }
+
+ if (target->packagetype != pt_dsc)
+ return decision;
+
+ p = pattern;
+ while (p != NULL && !p->omitextrasource_set)
+ p = p->pattern_from;
+ /* if unset or set to true, ignore new->source having that field */
+ if (p == NULL || p->omitextrasource == true) {
+ if (chunk_gettruth(new->control, "Extra-Source-Only"))
+ return UD_NO;
+ }
+
+ return decision;
+}
+
+
+static inline retvalue searchformissing(/*@null@*/FILE *out, struct update_target *u) {
+ struct update_index_connector *uindex;
+ retvalue result, r;
+
+ if (u->nothingnew) {
+ if (u->indices == NULL && verbose >= 4 && out != NULL)
+ fprintf(out,
+" nothing to do for '%s'\n",
+ u->target->identifier);
+ else if (u->indices != NULL && verbose >= 0 && out != NULL)
+ fprintf(out,
+" nothing new for '%s' (use --noskipold to process anyway)\n",
+ u->target->identifier);
+ return RET_NOTHING;
+ }
+ if (verbose > 2 && out != NULL)
+ fprintf(out, " processing updates for '%s'\n",
+ u->target->identifier);
+ r = upgradelist_initialize(&u->upgradelist, u->target);
+ if (RET_WAS_ERROR(r))
+ return r;
+
+ result = RET_NOTHING;
+
+ for (uindex = u->indices ; uindex != NULL ; uindex = uindex->next) {
+ const char *filename;
+
+ if (uindex->origin == NULL) {
+ if (verbose > 4 && out != NULL)
+ fprintf(out,
+" marking everything to be deleted\n");
+ r = upgradelist_deleteall(u->upgradelist);
+ if (RET_WAS_ERROR(r))
+ u->incomplete = true;
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ return result;
+ u->ignoredelete = false;
+ continue;
+ }
+
+ if (uindex->afterhookfilename != NULL)
+ filename = uindex->afterhookfilename;
+ else
+ filename = remote_index_file(uindex->remote);
+
+ if (uindex->failed || uindex->origin->failed) {
+ if (verbose >= 1)
+ fprintf(stderr,
+" missing '%s'\n", filename);
+ u->incomplete = true;
+ u->ignoredelete = true;
+ continue;
+ }
+
+ if (verbose > 4 && out != NULL)
+ fprintf(out, " reading '%s'\n", filename);
+ r = upgradelist_update(u->upgradelist, uindex,
+ filename,
+ ud_decide_by_pattern,
+ (void*)uindex->origin->pattern,
+ uindex->ignorewrongarchitecture);
+ if (RET_WAS_ERROR(r)) {
+ u->incomplete = true;
+ u->ignoredelete = true;
+ }
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ return result;
+ }
+
+ return result;
+}
+
+static retvalue updates_readindices(/*@null@*/FILE *out, struct update_distribution *d) {
+ retvalue result, r;
+ struct update_target *u;
+
+ result = RET_NOTHING;
+ for (u=d->targets ; u != NULL ; u=u->next) {
+ r = searchformissing(out, u);
+ if (RET_WAS_ERROR(r))
+ u->incomplete = true;
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ break;
+ }
+ return result;
+}
+
+/****************************************************************************
+ * Step 10: enqueue downloading of missing packages *
+ ****************************************************************************/
+
+static retvalue enqueue_upgrade_package(void *calldata, const struct checksumsarray *origfiles, const struct strlist *filekeys, void *privdata) {
+ struct update_index_connector *uindex = privdata;
+ struct aptmethod *aptmethod;
+ struct downloadcache *cache = calldata;
+
+ assert(privdata != NULL);
+ aptmethod = remote_aptmethod(uindex->origin->from);
+ assert(aptmethod != NULL);
+ return downloadcache_addfiles(cache, aptmethod, origfiles, filekeys);
+}
+
+static retvalue updates_enqueue(struct downloadcache *cache, struct update_distribution *distribution) {
+ retvalue result, r;
+ struct update_target *u;
+
+ result = RET_NOTHING;
+ for (u=distribution->targets ; u != NULL ; u=u->next) {
+ if (u->nothingnew)
+ continue;
+ r = upgradelist_enqueue(u->upgradelist, enqueue_upgrade_package,
+ cache);
+ if (RET_WAS_ERROR(r))
+ u->incomplete = true;
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ break;
+ }
+ return result;
+}
+
+/****************************************************************************
+ * Step 11: install the missing packages *
+ * (missing files should have been downloaded first) *
+ ****************************************************************************/
+static bool isbigdelete(struct update_distribution *d) {
+ struct update_target *u, *v;
+
+ for (u = d->targets ; u != NULL ; u=u->next) {
+ if (u->nothingnew || u->ignoredelete)
+ continue;
+ if (upgradelist_isbigdelete(u->upgradelist)) {
+ d->distribution->omitted = true;
+ for (v = d->targets ; v != NULL ; v = v->next) {
+ upgradelist_free(v->upgradelist);
+ v->upgradelist = NULL;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+static void updates_from_callback(void *privdata, const char **rule_p, const char **from_p) {
+ struct update_index_connector *uindex = privdata;
+
+ *from_p = uindex->origin->suite_from;
+ *rule_p = uindex->origin->pattern->name;
+}
+
+static retvalue updates_install(struct update_distribution *distribution) {
+ retvalue result, r;
+ struct update_target *u;
+ struct distribution *d = distribution->distribution;
+
+ assert (logger_isprepared(d->logger));
+
+ result = RET_NOTHING;
+ for (u=distribution->targets ; u != NULL ; u=u->next) {
+ if (u->nothingnew)
+ continue;
+ r = upgradelist_install(u->upgradelist, d->logger,
+ u->ignoredelete, updates_from_callback);
+ RET_UPDATE(d->status, r);
+ if (RET_WAS_ERROR(r))
+ u->incomplete = true;
+ RET_UPDATE(result, r);
+ upgradelist_free(u->upgradelist);
+ u->upgradelist = NULL;
+ if (RET_WAS_ERROR(r))
+ break;
+ }
+ if (RET_IS_OK(result) && d->tracking != dt_NONE) {
+ r = tracking_retrack(d, false);
+ RET_ENDUPDATE(result, r);
+ }
+ return result;
+}
+
+/****************************************************************************
+ * Step 12: mark index files as processed, so they won't process a second *
+ * time, unless --noskipold is given *
+ ****************************************************************************/
+
+static void markdone(struct update_distribution *d) {
+ struct markdonefile *done;
+ struct update_index_connector *i;
+ struct update_target *t;
+ retvalue r;
+
+ r = markdone_create(d->distribution->codename, &done);
+ if (!RET_IS_OK(r))
+ return;
+
+ for (t = d->targets ; t != NULL ; t = t->next) {
+ if (t->incomplete)
+ continue;
+ markdone_target(done, t->target->identifier);
+ for (i = t->indices ; i != NULL ; i = i->next)
+ if (i->remote == NULL)
+ markdone_cleaner(done);
+ else
+ remote_index_markdone(i->remote, done);
+ }
+ markdone_finish(done);
+}
+
+
+/****************************************************************************
+ * All together now: everything done step after step, in between telling *
+ * the apt methods to actually download what was enqueued. *
+ ****************************************************************************/
+
+static retvalue markold(struct update_distribution *ud) {
+ struct update_target *ut;
+ struct update_index_connector *ui;
+ retvalue r;
+ struct donefile *donefile;
+ const char *identifier;
+
+ r = donefile_open(ud->distribution->codename, &donefile);
+ if (!RET_IS_OK(r))
+ return r;
+
+ while (donefile_nexttarget(donefile, &identifier)) {
+ ut = ud->targets;
+ while (ut != NULL && strcmp(identifier,
+ ut->target->identifier) != 0)
+ ut = ut->next;
+ if (ut == NULL)
+ continue;
+ ut->nothingnew = true;
+ for (ui = ut->indices ; ui != NULL ; ui = ui->next) {
+ /* if the order does not match, it does not matter
+ * if they are new or not, they should be processed
+ * anyway */
+
+ if (ui->remote == NULL) {
+ if (!donefile_iscleaner(donefile)) {
+ ut->nothingnew = false;
+ break;
+ }
+ continue;
+ }
+ if (remote_index_isnew(ui->remote, donefile)) {
+ ut->nothingnew = false;
+ break;
+ }
+ }
+
+ }
+ donefile_close(donefile);
+ return RET_OK;
+}
+
+static retvalue updates_preparelists(struct aptmethodrun *run, struct update_distribution *distributions, bool nolistsdownload, bool skipold, bool *anythingtodo) {
+ struct update_distribution *d;
+ struct update_target *ut;
+ struct update_index_connector *ui;
+ retvalue r;
+
+ r = remote_preparemetalists(run, nolistsdownload);
+ if (RET_WAS_ERROR(r))
+ return r;
+
+ for (d = distributions ; d != NULL ; d = d->next) {
+ /* first check what is old */
+ if (skipold) {
+ r = markold(d);
+ if (RET_WAS_ERROR(r))
+ return r;
+ }
+ /* we need anything that is needed in a target
+ * where something is new (as new might mean
+ * a package is left hiding leftmore packages,
+ * and everything in rightmore packages is needed
+ * to see what in the new takes effect) */
+ for (ut = d->targets; ut != NULL ; ut = ut->next) {
+ if (ut->nothingnew)
+ continue;
+ if (ut->indices == NULL) {
+ ut->nothingnew = true;
+ continue;
+ }
+ for (ui = ut->indices ; ui != NULL ; ui = ui->next) {
+ if (ui->remote == NULL)
+ continue;
+ remote_index_needed(ui->remote);
+ *anythingtodo = true;
+ }
+ }
+ }
+
+ r = remote_preparelists(run, nolistsdownload);
+ if (RET_WAS_ERROR(r))
+ return r;
+ return RET_OK;
+}
+
+static retvalue updates_prepare(struct update_distribution *distributions, bool willwrite, bool nolistsdownload, bool skipold, struct aptmethodrun **run_p) {
+ retvalue result, r;
+ struct aptmethodrun *run;
+ bool anythingtodo = !skipold;
+
+ r = aptmethod_initialize_run(&run);
+ if (RET_WAS_ERROR(r))
+ return r;
+
+ /* preperations */
+ result = updates_startup(run, distributions, willwrite);
+ if (RET_WAS_ERROR(result)) {
+ aptmethod_shutdown(run);
+ return result;
+ }
+
+ r = updates_preparelists(run, distributions, nolistsdownload, skipold,
+ &anythingtodo);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(result)) {
+ aptmethod_shutdown(run);
+ return result;
+ }
+ if (!anythingtodo && skipold) {
+ if (verbose >= 0) {
+ if (willwrite)
+ printf(
+"Nothing to do found. (Use --noskipold to force processing)\n");
+ else
+ fprintf(stderr,
+"Nothing to do found. (Use --noskipold to force processing)\n");
+ }
+
+ aptmethod_shutdown(run);
+ return RET_NOTHING;
+ }
+
+ /* Call ListHooks (if given) on the downloaded index files.
+ * (This is done even when nolistsdownload is given, as otherwise
+ * the filename to look in is not calculated) */
+ r = updates_calllisthooks(distributions);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(result)) {
+ aptmethod_shutdown(run);
+ return result;
+ }
+
+ *run_p = run;
+ return RET_OK;
+}
+
+
+retvalue updates_update(struct update_distribution *distributions, bool nolistsdownload, bool skipold, enum spacecheckmode mode, off_t reserveddb, off_t reservedother) {
+ retvalue result, r;
+ struct update_distribution *d;
+ struct downloadcache *cache;
+ struct aptmethodrun *run;
+ bool todo;
+
+ causingfile = NULL;
+
+ result = updates_prepare(distributions, true, nolistsdownload, skipold,
+ &run);
+ if (!RET_IS_OK(result))
+ return result;
+
+ /* Then get all packages */
+ if (verbose >= 0)
+ printf("Calculating packages to get...\n");
+ r = downloadcache_initialize(mode, reserveddb, reservedother, &cache);
+ if (!RET_IS_OK(r)) {
+ aptmethod_shutdown(run);
+ RET_UPDATE(result, r);
+ return result;
+ }
+
+ todo = false;
+ for (d=distributions ; d != NULL ; d=d->next) {
+ r = updates_readindices(stdout, d);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ break;
+ if (global.onlysmalldeletes) {
+ if (isbigdelete(d))
+ continue;
+ }
+ r = updates_enqueue(cache, d);
+ if (RET_IS_OK(r))
+ todo = true;
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ break;
+ }
+ if (!RET_WAS_ERROR(result)) {
+ r = space_check(cache->devices);
+ RET_ENDUPDATE(result, r);
+ }
+ if (!RET_WAS_ERROR(result) && !todo) {
+ for (d=distributions ; !todo && d != NULL ; d=d->next) {
+ struct update_target *u;
+ if (d->distribution->omitted)
+ continue;
+ for (u = d->targets ; u != NULL ; u = u->next) {
+ if (u->nothingnew || u->ignoredelete)
+ continue;
+ if (upgradelist_woulddelete(u->upgradelist)) {
+ todo = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (RET_WAS_ERROR(result) || !todo) {
+ for (d=distributions ; d != NULL ; d=d->next) {
+ struct update_target *u;
+ if (d->distribution->omitted) {
+ fprintf(stderr,
+"Not processing updates for '%s' because of --onlysmalldeletes!\n",
+ d->distribution->codename);
+ } else if (RET_IS_OK(result))
+ markdone(d);
+ for (u=d->targets ; u != NULL ; u=u->next) {
+ upgradelist_free(u->upgradelist);
+ u->upgradelist = NULL;
+ }
+ }
+ r = downloadcache_free(cache);
+ RET_UPDATE(result, r);
+ aptmethod_shutdown(run);
+ return result;
+ }
+ if (verbose >= 0)
+ printf("Getting packages...\n");
+ r = aptmethod_download(run);
+ RET_UPDATE(result, r);
+ r = downloadcache_free(cache);
+ RET_ENDUPDATE(result, r);
+ if (verbose > 0)
+ printf("Shutting down aptmethods...\n");
+ r = aptmethod_shutdown(run);
+ RET_UPDATE(result, r);
+
+ if (RET_WAS_ERROR(result)) {
+ for (d=distributions ; d != NULL ; d=d->next) {
+ struct update_target *u;
+ for (u=d->targets ; u != NULL ; u=u->next) {
+ upgradelist_free(u->upgradelist);
+ u->upgradelist = NULL;
+ }
+ }
+ return result;
+ }
+ if (verbose >= 0)
+ printf("Installing (and possibly deleting) packages...\n");
+
+ for (d=distributions ; d != NULL ; d=d->next) {
+ if (d->distribution->omitted)
+ continue;
+ r = updates_install(d);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ break;
+ }
+
+ for (d=distributions ; d != NULL ; d=d->next) {
+ if (d->distribution->omitted) {
+ fprintf(stderr,
+"Not processing updates for '%s' because of --onlysmalldeletes!\n",
+ d->distribution->codename);
+ } else
+ markdone(d);
+ }
+ logger_wait();
+
+ return result;
+}
+
+/****************************************************************************
+ * Alternatively, don't download and install, but list what is needed to be *
+ * done. (For the checkupdate command) *
+ ****************************************************************************/
+
+static void upgrade_dumppackage(const char *packagename, /*@null@*/const char *oldversion, /*@null@*/const char *newversion, /*@null@*/const char *bestcandidate, /*@null@*/const struct strlist *newfilekeys, /*@null@*/const char *newcontrol, void *privdata) {
+ struct update_index_connector *uindex = privdata;
+
+ if (newversion == NULL) {
+ if (oldversion != NULL && bestcandidate != NULL) {
+ printf("'%s': '%s' will be deleted"
+ " (best new: '%s')\n",
+ packagename, oldversion, bestcandidate);
+ } else if (oldversion != NULL) {
+ printf("'%s': '%s' will be deleted"
+ " (no longer available or superseded)\n",
+ packagename, oldversion);
+ } else {
+ printf("'%s': will NOT be added as '%s'\n",
+ packagename, bestcandidate);
+ }
+ } else if (newversion == oldversion) {
+ if (bestcandidate != NULL) {
+ if (verbose > 1)
+ printf("'%s': '%s' will be kept"
+ " (best new: '%s')\n",
+ packagename, oldversion,
+ bestcandidate);
+ } else {
+ if (verbose > 0)
+ printf("'%s': '%s' will be kept"
+ " (unavailable for reload)\n",
+ packagename, oldversion);
+ }
+ } else {
+ const char *via = uindex->origin->pattern->name;
+
+ assert (newfilekeys != NULL);
+ assert (newcontrol != NULL);
+ if (oldversion != NULL)
+ (void)printf(
+"'%s': '%s' will be upgraded to '%s' (from '%s'):\n files needed: ",
+ packagename, oldversion,
+ newversion, via);
+ else
+ (void)printf(
+"'%s': newly installed as '%s' (from '%s'):\n files needed: ",
+ packagename, newversion, via);
+ (void)strlist_fprint(stdout, newfilekeys);
+ if (verbose > 2)
+ (void)printf("\n installing as: '%s'\n",
+ newcontrol);
+ else
+ (void)putchar('\n');
+ }
+}
+
+static void updates_dump(struct update_distribution *distribution) {
+ struct update_target *u;
+
+ for (u=distribution->targets ; u != NULL ; u=u->next) {
+ if (u->nothingnew)
+ continue;
+ printf("Updates needed for '%s':\n", u->target->identifier);
+ upgradelist_dump(u->upgradelist, upgrade_dumppackage);
+ upgradelist_free(u->upgradelist);
+ u->upgradelist = NULL;
+ }
+}
+
+static void upgrade_dumplistpackage(const char *packagename, /*@null@*/const char *oldversion, /*@null@*/const char *newversion, /*@null@*/const char *bestcandidate, /*@null@*/const struct strlist *newfilekeys, /*@null@*/const char *newcontrol, void *privdata) {
+ struct update_index_connector *uindex = privdata;
+
+ if (newversion == NULL) {
+ if (oldversion == NULL)
+ return;
+ printf("delete '%s' '%s'\n", packagename, oldversion);
+ } else if (newversion == oldversion) {
+ if (bestcandidate != NULL)
+ printf("keep '%s' '%s' '%s'\n", packagename,
+ oldversion, bestcandidate);
+ else
+ printf("keep '%s' '%s' unavailable\n", packagename,
+ oldversion);
+ } else {
+ const char *via = uindex->origin->pattern->name;
+
+ assert (newfilekeys != NULL);
+ assert (newcontrol != NULL);
+ if (oldversion != NULL)
+ (void)printf("update '%s' '%s' '%s' '%s'\n",
+ packagename, oldversion,
+ newversion, via);
+ else
+ (void)printf("add '%s' - '%s' '%s'\n",
+ packagename, newversion, via);
+ }
+}
+
+static void updates_dumplist(struct update_distribution *distribution) {
+ struct update_target *u;
+
+ for (u=distribution->targets ; u != NULL ; u=u->next) {
+ if (u->nothingnew)
+ continue;
+ printf("Updates needed for '%s':\n", u->target->identifier);
+ upgradelist_dump(u->upgradelist, upgrade_dumplistpackage);
+ upgradelist_free(u->upgradelist);
+ u->upgradelist = NULL;
+ }
+}
+
+retvalue updates_checkupdate(struct update_distribution *distributions, bool nolistsdownload, bool skipold) {
+ struct update_distribution *d;
+ retvalue result, r;
+ struct aptmethodrun *run;
+
+ result = updates_prepare(distributions, false, nolistsdownload, skipold,
+ &run);
+ if (!RET_IS_OK(result))
+ return result;
+
+ if (verbose > 0)
+ fprintf(stderr, "Shutting down aptmethods...\n");
+ r = aptmethod_shutdown(run);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(result)) {
+ return result;
+ }
+
+ /* Then look what packages to get */
+ if (verbose >= 0)
+ fprintf(stderr, "Calculating packages to get...\n");
+
+ for (d=distributions ; d != NULL ; d=d->next) {
+ r = updates_readindices(stderr, d);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ break;
+ updates_dump(d);
+ }
+
+ return result;
+}
+
+retvalue updates_dumpupdate(struct update_distribution *distributions, bool nolistsdownload, bool skipold) {
+ struct update_distribution *d;
+ retvalue result, r;
+ struct aptmethodrun *run;
+
+ result = updates_prepare(distributions, false, nolistsdownload, skipold,
+ &run);
+ if (!RET_IS_OK(result))
+ return result;
+
+ r = aptmethod_shutdown(run);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(result)) {
+ return result;
+ }
+
+ for (d=distributions ; d != NULL ; d=d->next) {
+ r = updates_readindices(NULL, d);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r))
+ break;
+ updates_dumplist(d);
+ }
+
+ return result;
+}
+
+/******************************************************************************
+ * For the predelete command: delete everything a following update run would *
+ * delete. (Assuming no unexpected errors occur, like a file missing upstream.*
+ *****************************************************************************/
+
+retvalue updates_predelete(struct update_distribution *distributions, bool nolistsdownload, bool skipold) {
+ retvalue result, r;
+ struct update_distribution *d;
+ struct aptmethodrun *run;
+
+ causingfile = NULL;
+
+ result = updates_prepare(distributions, true, nolistsdownload, skipold,
+ &run);
+ if (!RET_IS_OK(result))
+ return result;
+
+ if (verbose > 0)
+ printf("Shutting down aptmethods...\n");
+ r = aptmethod_shutdown(run);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(result)) {
+ return result;
+ }
+
+ if (verbose >= 0)
+ printf("Removing obsolete or to be replaced packages...\n");
+ for (d=distributions ; d != NULL ; d=d->next) {
+ struct distribution *dd = d->distribution;
+ struct update_target *u;
+
+ for (u=d->targets ; u != NULL ; u=u->next) {
+ r = searchformissing(stdout, u);
+ RET_UPDATE(result, r);
+ if (RET_WAS_ERROR(r)) {
+ u->incomplete = true;
+ continue;
+ }
+ if (u->nothingnew || u->ignoredelete) {
+ upgradelist_free(u->upgradelist);
+ u->upgradelist = NULL;
+ continue;
+ }
+ r = upgradelist_predelete(u->upgradelist, dd->logger);
+ RET_UPDATE(dd->status, r);
+ if (RET_WAS_ERROR(r))
+ u->incomplete = true;
+ RET_UPDATE(result, r);
+ upgradelist_free(u->upgradelist);
+ u->upgradelist = NULL;
+ if (RET_WAS_ERROR(r))
+ return r;
+ if (RET_IS_OK(result) && dd->tracking != dt_NONE) {
+ r = tracking_retrack(dd, false);
+ RET_ENDUPDATE(result, r);
+ }
+ }
+ }
+ logger_wait();
+ return result;
+}
+
+/******************************************************************************
+ * The cleanlists command has to mark all files that might be scheduled to be *
+ * downloaded again, so that the rest can be deleted *
+ ******************************************************************************/
+
+static void marktargetsneeded(struct cachedlistfile *files, const struct distribution *d, component_t flat, /*@null@*/const struct strlist *a_from, /*@null@*/const struct strlist *a_into, /*@null@*/const struct strlist *c_from, /*@null@*/const struct strlist *uc_from, const char *repository, const char *suite) {
+ struct target *t;
+ int i, ai;
+
+ if (atom_defined(flat)) {
+ bool deb_needed = false, dsc_needed = false;
+
+ for (t = d->targets ; t != NULL ; t = t->next) {
+ if (t->packagetype == pt_udeb)
+ continue;
+ if (flat != t->architecture)
+ continue;
+ if (a_into != NULL &&
+ !strlist_in(a_into,
+ atoms_architectures[
+ t->architecture]))
+ continue;
+ if (t->packagetype == pt_deb)
+ deb_needed = true;
+ else if (t->packagetype == pt_dsc)
+ dsc_needed = true;
+ }
+ if (deb_needed)
+ cachedlistfile_need_flat_index(files,
+ repository, suite, pt_deb);
+ if (dsc_needed)
+ cachedlistfile_need_flat_index(files,
+ repository, suite, pt_dsc);
+ return;
+ }
+ /* .dsc */
+ if ((a_into != NULL && strlist_in(a_into, "source")) ||
+ (a_into == NULL && atomlist_in(&d->architectures,
+ architecture_source))) {
+ if (c_from != NULL)
+ for (i = 0 ; i < c_from->count ; i++)
+ cachedlistfile_need_index(files,
+ repository, suite, "source",
+ c_from->values[i], pt_dsc);
+ else
+ for (i = 0 ; i < d->components.count ; i++)
+ cachedlistfile_need_index(files,
+ repository, suite, "source",
+ atoms_components[
+ d->components.atoms[i]],
+ pt_dsc);
+ }
+ /* .deb and .udeb */
+ if (a_into != NULL) {
+ for (ai = 0 ; ai < a_into->count ; ai++) {
+ const char *a = a_from->values[ai];
+
+ if (strcmp(a_into->values[ai], "source") == 0)
+ continue;
+ if (c_from != NULL)
+ for (i = 0 ; i < c_from->count ; i++)
+ cachedlistfile_need_index(files,
+ repository, suite, a,
+ c_from->values[i],
+ pt_deb);
+ else
+ for (i = 0 ; i < d->components.count ; i++)
+ cachedlistfile_need_index(files,
+ repository, suite, a,
+ atoms_components[
+ d->components.atoms[i]],
+ pt_deb);
+ if (uc_from != NULL)
+ for (i = 0 ; i < uc_from->count ; i++)
+ cachedlistfile_need_index(files,
+ repository, suite, a,
+ uc_from->values[i],
+ pt_udeb);
+ else
+ for (i = 0 ; i < d->udebcomponents.count ; i++)
+ cachedlistfile_need_index(files,
+ repository, suite, a,
+ atoms_components[
+ d->components.atoms[i]],
+ pt_udeb);
+ }
+ } else {
+ for (ai = 0 ; ai < d->architectures.count ; ai++) {
+ const char *a = atoms_architectures[
+ d->architectures.atoms[ai]];
+
+ if (d->architectures.atoms[ai] == architecture_source)
+ continue;
+ if (c_from != NULL)
+ for (i = 0 ; i < c_from->count ; i++)
+ cachedlistfile_need_index(files,
+ repository, suite, a,
+ c_from->values[i],
+ pt_deb);
+ else
+ for (i = 0 ; i < d->components.count ; i++)
+ cachedlistfile_need_index(files,
+ repository, suite, a,
+ atoms_components[
+ d->components.atoms[i]],
+ pt_deb);
+ if (uc_from != NULL)
+ for (i = 0 ; i < uc_from->count ; i++)
+ cachedlistfile_need_index(files,
+ repository, suite, a,
+ uc_from->values[i],
+ pt_udeb);
+ else
+ for (i = 0 ; i < d->udebcomponents.count ; i++)
+ cachedlistfile_need_index(files,
+ repository, suite, a,
+ atoms_components[
+ d->components.atoms[i]],
+ pt_udeb);
+ }
+ }
+}
+
+retvalue updates_cleanlists(const struct distribution *distributions, const struct update_pattern *patterns) {
+ retvalue result;
+ const struct distribution *d;
+ const struct update_pattern *p, *q;
+ struct cachedlistfile *files;
+ int i;
+ bool isflat;
+ const struct strlist *uc_from = NULL;
+ const struct strlist *c_from = NULL;
+ const struct strlist *a_from = NULL, *a_into = NULL;
+ const char *repository;
+ char *suite;
+
+ result = cachedlists_scandir(&files);
+ if (!RET_IS_OK(result))
+ return result;
+
+ result = RET_OK;
+ for (d = distributions ; d != NULL ; d = d->next) {
+ if (d->updates.count == 0)
+ continue;
+ cachedlistfile_need(files, "lastseen", 2, "", d->codename, NULL);
+ for (i = 0; i < d->updates.count ; i++) {
+ const char *name = d->updates.values[i];
+
+ if (strcmp(name, "-") == 0)
+ continue;
+
+ p = patterns;
+ while (p != NULL && strcmp(name, p->name) != 0)
+ p = p->next;
+ if (p == NULL) {
+ fprintf(stderr,
+"Cannot find definition of upgrade-rule '%s' for distribution '%s'!\n",
+ name, d->codename);
+ result = RET_ERROR;
+ continue;
+ }
+ q = p;
+ while (q != NULL && q->pattern_from != NULL)
+ q = q->pattern_from;
+ repository = q->name;
+ q = p;
+ while (q != NULL && !atom_defined(q->flat))
+ q = q->pattern_from;
+ isflat = q != NULL;
+ q = p;
+ while (q != NULL && !q->architectures_set)
+ q = q->pattern_from;
+ if (q != NULL) {
+ a_from = &q->architectures_from;
+ a_into = &q->architectures_into;
+ }
+ q = p;
+ while (q != NULL && !q->components_set)
+ q = q->pattern_from;
+ if (q != NULL)
+ c_from = &q->components_from;
+ q = p;
+ while (q != NULL && !q->udebcomponents_set)
+ q = q->pattern_from;
+ if (q != NULL)
+ uc_from = &q->udebcomponents_from;
+ suite = translate_suite_pattern(p, d->codename);
+ if (FAILEDTOALLOC(suite)) {
+ cachedlistfile_freelist(files);
+ return RET_ERROR_OOM;
+ }
+ /* Only index files are intresting, everything else
+ * Release, Release.gpg, compressed files, hook processed
+ * files is deleted */
+ marktargetsneeded(files, d, isflat, a_from, a_into,
+ c_from, uc_from, repository, suite);
+ free(suite);
+
+ }
+ }
+ cachedlistfile_deleteunneeded(files);
+ cachedlistfile_freelist(files);
+ return RET_OK;
+}