diff options
Diffstat (limited to 'distribution.c')
-rw-r--r-- | distribution.c | 1233 |
1 files changed, 1233 insertions, 0 deletions
diff --git a/distribution.c b/distribution.c new file mode 100644 index 0000000..8b24365 --- /dev/null +++ b/distribution.c @@ -0,0 +1,1233 @@ +/* This file is part of "reprepro" + * Copyright (C) 2003,2004,2005,2006,2007,2008,2009,2010,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 + */ +#include <config.h> + +#include <errno.h> +#include <limits.h> +#include <assert.h> +#include <stdio.h> +#include <stdarg.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <time.h> +#include "error.h" +#include "mprintf.h" +#include "atoms.h" +#include "sources.h" +#include "dirs.h" +#include "names.h" +#include "release.h" +#include "tracking.h" +#include "override.h" +#include "log.h" +#include "ignore.h" +#include "uploaderslist.h" +#include "configparser.h" +#include "byhandhook.h" +#include "package.h" +#include "distribution.h" + +static retvalue distribution_free(struct distribution *distribution) { + retvalue result, r; + bool needsretrack = false; + + if (distribution != NULL) { + distribution->archive = NULL; + free(distribution->suite); + free(distribution->fakecomponentprefix); + free(distribution->version); + free(distribution->origin); + free(distribution->notautomatic); + free(distribution->butautomaticupgrades); + free(distribution->label); + free(distribution->description); + free(distribution->signed_by); + free(distribution->deb_override); + free(distribution->udeb_override); + free(distribution->dsc_override); + free(distribution->uploaders); + atomlist_done(&distribution->udebcomponents); + atomlist_done(&distribution->architectures); + atomlist_done(&distribution->components); + strlist_done(&distribution->signwith); + strlist_done(&distribution->updates); + strlist_done(&distribution->pulls); + strlist_done(&distribution->alsoaccept); + exportmode_done(&distribution->dsc); + exportmode_done(&distribution->deb); + exportmode_done(&distribution->udeb); + exportmode_done(&distribution->ddeb); + atomlist_done(&distribution->contents_architectures); + atomlist_done(&distribution->contents_components); + atomlist_done(&distribution->contents_dcomponents); + atomlist_done(&distribution->contents_ucomponents); + override_free(distribution->overrides.deb); + override_free(distribution->overrides.udeb); + override_free(distribution->overrides.dsc); + logger_free(distribution->logger); + if (distribution->uploaderslist != NULL) { + uploaders_unlock(distribution->uploaderslist); + } + byhandhooks_free(distribution->byhandhooks); + result = RET_OK; + + while (distribution->targets != NULL) { + struct target *next = distribution->targets->next; + + if (distribution->targets->staletracking) + needsretrack = true; + + r = target_free(distribution->targets); + RET_UPDATE(result, r); + distribution->targets = next; + } + if (distribution->tracking != dt_NONE && needsretrack) { + fprintf(stderr, +"WARNING: Tracking data of '%s' might have become out of date.\n" +"Consider running retrack to avoid getting funny effects.\n", + distribution->codename); + } + free(distribution->codename); + free(distribution); + return result; + } else + return RET_OK; +} + +/* allow premature free'ing of overrides to save some memory */ +void distribution_unloadoverrides(struct distribution *distribution) { + override_free(distribution->overrides.deb); + override_free(distribution->overrides.udeb); + override_free(distribution->overrides.dsc); + distribution->overrides.deb = NULL; + distribution->overrides.udeb = NULL; + distribution->overrides.dsc = NULL; +} + +/* create all contained targets... */ +static retvalue createtargets(struct distribution *distribution) { + retvalue r; + int i, j; + struct target *t; + struct target *last = NULL; + bool has_source = false; + + for (i = 0 ; i < distribution->components.count ; i++) { + component_t c = distribution->components.atoms[i]; + for (j = 0 ; j < distribution->architectures.count ; j++) { + architecture_t a = distribution->architectures.atoms[j]; + + if (a == architecture_source) { + has_source = true; + continue; + } + if (a == architecture_all) { + fprintf(stderr, +"Error: Distribution %s contains an architecture called 'all'.\n", + distribution->codename); + return RET_ERROR; + } + if (strcmp(atoms_architectures[a], "any") == 0) { + fprintf(stderr, +"Error: Distribution %s contains an architecture called 'any'.\n", + distribution->codename); + return RET_ERROR; + } + + r = target_initialize_binary( + distribution, + c, a, + &distribution->deb, + distribution->readonly, + distribution->exportoptions[deo_noexport], + distribution->fakecomponentprefix, + &t); + if (RET_IS_OK(r)) { + if (last != NULL) { + last->next = t; + } else { + distribution->targets = t; + } + last = t; + } + if (RET_WAS_ERROR(r)) + return r; + if (atomlist_in(&distribution->udebcomponents, c)) { + r = target_initialize_ubinary( + distribution, + c, a, + &distribution->udeb, + distribution->readonly, + distribution->exportoptions + [deo_noexport], + distribution->fakecomponentprefix, + &t); + if (RET_IS_OK(r)) { + if (last != NULL) { + last->next = t; + } else { + distribution->targets = t; + } + last = t; + } + if (RET_WAS_ERROR(r)) + return r; + + } + if (atomlist_in(&distribution->ddebcomponents, c)) { + r = target_initialize_dbinary( + distribution, + c, a, + &distribution->ddeb, + distribution->readonly, + distribution->exportoptions[deo_noexport], + distribution->fakecomponentprefix, + &t); + if (RET_IS_OK(r)) { + if (last != NULL) { + last->next = t; + } else { + distribution->targets = t; + } + last = t; + } + if (RET_WAS_ERROR(r)) + return r; + } + } + /* check if this distribution contains source + * (yes, yes, source is not really an architecture, but + * the .changes files started with this...) */ + if (has_source) { + r = target_initialize_source(distribution, + c, &distribution->dsc, + distribution->readonly, + distribution->exportoptions + [deo_noexport], + distribution->fakecomponentprefix, &t); + if (last != NULL) { + last->next = t; + } else { + distribution->targets = t; + } + last = t; + if (RET_WAS_ERROR(r)) + return r; + } + } + return RET_OK; +} + +struct read_distribution_data { + struct distribution *distributions; +}; + +CFstartparse(distribution) { + CFstartparseVAR(distribution, result_p); + struct distribution *n; + retvalue r; + + n = zNEW(struct distribution); + if (FAILEDTOALLOC(n)) + return RET_ERROR_OOM; + /* set some default value: */ + n->limit = 1; + r = exportmode_init(&n->udeb, true, NULL, "Packages"); + if (RET_WAS_ERROR(r)) { + (void)distribution_free(n); + return r; + } + r = exportmode_init(&n->ddeb, true, "Release", "Packages"); + if (RET_WAS_ERROR(r)) { + (void)distribution_free(n); + return r; + } + r = exportmode_init(&n->deb, true, "Release", "Packages"); + if (RET_WAS_ERROR(r)) { + (void)distribution_free(n); + return r; + } + r = exportmode_init(&n->dsc, false, "Release", "Sources"); + if (RET_WAS_ERROR(r)) { + (void)distribution_free(n); + return r; + } + *result_p = n; + return RET_OK; +} + +static bool notpropersuperset(const struct atomlist *allowed, const char *allowedname, const struct atomlist *check, const char *checkname, const char **atoms, const struct distribution *d) { + atom_t missing; + + if (!atomlist_subset(allowed, check, &missing)) { + fprintf(stderr, +"In distribution description of '%s' (line %u to %u in %s):\n" +"%s contains '%s' not found in %s!\n", + d->codename, + d->firstline, d->lastline, d->filename, + checkname, atoms[missing], allowedname); + return true; + } + return false; +} + +static inline retvalue checkcomponentsequalduetofake(const struct distribution *d) { + size_t l; + int i, j; + + if (d->fakecomponentprefix == NULL) + return RET_OK; + + l = strlen(d->fakecomponentprefix); + + for (i = 0 ; i < d->components.count ; i++) { + const char *c1 = atoms_components[d->components.atoms[i]]; + + if (strncmp(c1, d->fakecomponentprefix, l) != 0) + continue; + if (d->fakecomponentprefix[l] != '/') + continue; + + for (j = 0 ; i < d->components.count ; j++) { + const char *c2; + + if (j == i) + continue; + + c2 = atoms_components[d->components.atoms[j]]; + + if (strcmp(c1 + l + 1, c2) == 0) { + fprintf(stderr, +"ERROR: distribution '%s' has components '%s' and '%s',\n" +"which would be output to the same place due to FakeComponentPrefix '%s'.\n", + d->codename, c1, c2, + d->fakecomponentprefix); + return RET_ERROR; + } + } + } + return RET_OK; +} + +CFfinishparse(distribution) { + CFfinishparseVARS(distribution, n, last_p, mydata); + struct distribution *d; + retvalue r; + + if (!complete) { + distribution_free(n); + return RET_NOTHING; + } + n->filename = config_filename(iter); + n->firstline = config_firstline(iter); + n->lastline = config_line(iter) - 1; + + /* Do some consistency checks */ + for (d = mydata->distributions; d != NULL; d = d->next) { + if (strcmp(d->codename, n->codename) == 0) { + fprintf(stderr, +"Multiple distributions with the common codename: '%s'!\n" +"First was in %s line %u to %u,\n" +"now another in lines %u to %u of %s.\n", + n->codename, d->filename, + d->firstline, d->lastline, + n->firstline, n->lastline, + n->filename); + distribution_free(n); + return RET_ERROR; + } + } + + if (notpropersuperset(&n->architectures, "Architectures", + &n->contents_architectures, "ContentsArchitectures", + atoms_architectures, n) || + notpropersuperset(&n->components, "Components", + &n->contents_components, "ContentsComponents", + atoms_components, n) || + notpropersuperset(&n->ddebcomponents, "DDebComponents", + &n->contents_dcomponents, "ContentsDComponents", + atoms_components, n) || + notpropersuperset(&n->udebcomponents, "UDebComponents", + &n->contents_ucomponents, "ContentsUComponents", + atoms_components, n) || + // TODO: instead of checking here make sure it can have more + // in the rest of the code...: + notpropersuperset(&n->components, "Components", + &n->udebcomponents, "UDebComponents", + atoms_components, n) || + notpropersuperset(&n->components, "Components", + &n->ddebcomponents, "DDebComponents", + atoms_components, n)) { + (void)distribution_free(n); + return RET_ERROR; + } + /* overwrite creation of contents files based on given lists: */ + if (n->contents_components_set) { + if (n->contents_components.count > 0) { + n->contents.flags.enabled = true; + n->contents.flags.nodebs = false; + } else { + n->contents.flags.nodebs = true; + } + } + if (n->contents_ucomponents_set) { + if (n->contents_ucomponents.count > 0) { + n->contents.flags.enabled = true; + n->contents.flags.udebs = true; + } else { + n->contents.flags.udebs = false; + } + } + if (n->contents_dcomponents_set) { + if (n->contents_dcomponents.count > 0) { + n->contents.flags.enabled = true; + n->contents.flags.ddebs = true; + } else { + n->contents.flags.ddebs = false; + } + } + if (n->contents_architectures_set) { + if (n->contents_architectures.count > 0) + n->contents.flags.enabled = true; + else + n->contents.flags.enabled = false; + } + + r = checkcomponentsequalduetofake(n); + if (RET_WAS_ERROR(r)) { + (void)distribution_free(n); + return r; + } + + /* prepare substructures */ + + r = createtargets(n); + if (RET_WAS_ERROR(r)) { + (void)distribution_free(n); + return r; + } + n->status = RET_NOTHING; + n->lookedat = false; + n->selected = false; + + /* put in linked list */ + if (*last_p == NULL) + mydata->distributions = n; + else + (*last_p)->next = n; + *last_p = n; + return RET_OK; +} + +CFallSETPROC(distribution, suite) +CFallSETPROC(distribution, version) +CFallSETPROC(distribution, origin) +CFallSETPROC(distribution, notautomatic) +CFallSETPROC(distribution, butautomaticupgrades) +CFtruthSETPROC2(distribution, readonly, readonly) +CFallSETPROC(distribution, label) +CFallSETPROC(distribution, description) +CFallSETPROC(distribution, signed_by) +CFsignwithSETPROC(distribution, signwith) +CFnumberSETPROC(distribution, -1, LLONG_MAX, limit) +CFfileSETPROC(distribution, deb_override) +CFfileSETPROC(distribution, udeb_override) +CFfileSETPROC(distribution, dsc_override) +CFfileSETPROC(distribution, uploaders) +CFuniqstrlistSETPROC(distribution, alsoaccept) +CFstrlistSETPROC(distribution, updates) +CFstrlistSETPROC(distribution, pulls) +CFinternatomsSETPROC(distribution, components, checkforcomponent, at_component) +CFinternatomsSETPROC(distribution, architectures, checkforarchitecture, at_architecture) +CFatomsublistSETPROC(distribution, contents_architectures, at_architecture, architectures, "Architectures") +CFatomsublistSETPROC(distribution, contents_components, at_component, components, "Components") +CFatomsublistSETPROC(distribution, ddebcomponents, at_component, components, "Components") +CFatomsublistSETPROC(distribution, udebcomponents, at_component, components, "Components") +CFatomsublistSETPROC(distribution, contents_ucomponents, at_component, udebcomponents, "UDebComponents") +CFexportmodeSETPROC(distribution, ddeb) +CFexportmodeSETPROC(distribution, udeb) +CFexportmodeSETPROC(distribution, deb) +CFexportmodeSETPROC(distribution, dsc) +CFcheckvalueSETPROC(distribution, codename, checkforcodename) +CFcheckvalueSETPROC(distribution, fakecomponentprefix, checkfordirectoryandidentifier) +CFtimespanSETPROC(distribution, validfor) + +CFuSETPROC(distribution, archive) { + CFSETPROCVARS(distribution, data, mydata); + char *codename; + retvalue r; + + r = config_getall(iter, &codename); + if (!RET_IS_OK(r)) + return r; + + for (struct distribution *d = mydata->distributions; d != NULL; d = d->next) { + if (strcmp(d->codename, codename) == 0) { + data->archive = d; + free(codename); + return RET_OK; + } + } + + fprintf(stderr, +"Error parsing config file %s, line %u:\n" +"No distribution has '%s' as codename.\n" +"Note: The archive distribution '%s' must be specified before '%s'.\n", + config_filename(iter), config_line(iter), codename, codename, data->codename); + free(codename); + return RET_ERROR_MISSING; +} + +CFUSETPROC(distribution, Contents) { + CFSETPROCVAR(distribution, d); + return contentsoptions_parse(d, iter); +} +CFUSETPROC(distribution, logger) { + CFSETPROCVAR(distribution, d); + return logger_init(iter, &d->logger); +} +CFUSETPROC(distribution, Tracking) { + CFSETPROCVAR(distribution, d); + return tracking_parse(d, iter); +} + +CFUSETPROC(distribution, byhandhooks) { + CFSETPROCVAR(distribution, d); + + return byhandhooks_parse(iter, &d->byhandhooks); +} + +static const struct constant exportnames[deo_COUNT+1] = { + {"noexport", deo_noexport}, + {"keepunknown", deo_keepunknown}, + {NULL, 0} +}; + +CFUSETPROC(distribution, exportoptions) { + CFSETPROCVAR(distribution, d); + return config_getflags(iter, name, exportnames, d->exportoptions, + IGNORABLE(unknownfield), + "(allowed values: noexport, keepunknown)"); +} + +static const struct configfield distributionconfigfields[] = { + CF("AlsoAcceptFor", distribution, alsoaccept), + CFr("Architectures", distribution, architectures), + CF("Archive", distribution, archive), + CF("ByHandHooks", distribution, byhandhooks), + CFr("Codename", distribution, codename), + CFr("Components", distribution, components), + CF("ContentsArchitectures", distribution, contents_architectures), + CF("ContentsComponents", distribution, contents_components), + CF("Contents", distribution, Contents), + CF("ContentsUComponents", distribution, contents_ucomponents), + CF("DDebComponents", distribution, ddebcomponents), + CF("DDebIndices", distribution, ddeb), + CF("DebIndices", distribution, deb), + CF("DebOverride", distribution, deb_override), + CF("Description", distribution, description), + CF("Signed-By", distribution, signed_by), + CF("DscIndices", distribution, dsc), + CF("DscOverride", distribution, dsc_override), + CF("FakeComponentPrefix", distribution, fakecomponentprefix), + CF("Label", distribution, label), + CF("Limit", distribution, limit), + CF("Log", distribution, logger), + CF("NotAutomatic", distribution, notautomatic), + CF("ButAutomaticUpgrades", distribution, butautomaticupgrades), + CF("Origin", distribution, origin), + CF("Pull", distribution, pulls), + CF("ReadOnly", distribution, readonly), + CF("ExportOptions", distribution, exportoptions), + CF("SignWith", distribution, signwith), + CF("Suite", distribution, suite), + CF("Tracking", distribution, Tracking), + CF("UDebComponents", distribution, udebcomponents), + CF("UDebIndices", distribution, udeb), + CF("UDebOverride", distribution, udeb_override), + CF("Update", distribution, updates), + CF("Uploaders", distribution, uploaders), + CF("ValidFor", distribution, validfor), + CF("Version", distribution, version) +}; + +/* read specification of all distributions */ +retvalue distribution_readall(struct distribution **distributions) { + struct read_distribution_data mydata; + retvalue result; + + mydata.distributions = NULL; + + // TODO: readd some way to tell about -b or --confdir here? + /* + result = regularfileexists(fn); + if (RET_WAS_ERROR(result)) { + fprintf(stderr, "Could not find '%s'!\n" +"(Have you forgotten to specify a basedir by -b?\n" +"To only set the conf/ dir use --confdir)\n", fn); + free(mydata.filter.found); + free(fn); + return RET_ERROR_MISSING; + } + */ + + result = configfile_parse("distributions", + IGNORABLE(unknownfield), + startparsedistribution, finishparsedistribution, + "distribution definition", + distributionconfigfields, + ARRAYCOUNT(distributionconfigfields), + &mydata); + if (result == RET_ERROR_UNKNOWNFIELD) + fprintf(stderr, +"Use --ignore=unknownfield to ignore unknown fields\n"); + if (RET_WAS_ERROR(result)) { + distribution_freelist(mydata.distributions); + return result; + } + if (mydata.distributions == NULL) { + fprintf(stderr, +"No distribution definitions found in %s/distributions!\n", + global.confdir); + distribution_freelist(mydata.distributions); + return RET_ERROR_MISSING; + } + *distributions = mydata.distributions; + return RET_OK; +} + +/* call <action> for each package */ +retvalue package_foreach(struct distribution *distribution, const struct atomlist *components, const struct atomlist *architectures, const struct atomlist *packagetypes, action_each_package action, action_each_target target_action, void *data) { + retvalue result, r; + struct target *t; + struct package_cursor iterator; + + result = RET_NOTHING; + for (t = distribution->targets ; t != NULL ; t = t->next) { + if (!target_matches(t, components, architectures, packagetypes)) + continue; + if (target_action != NULL) { + r = target_action(t, data); + if (RET_WAS_ERROR(r)) + return result; + if (r == RET_NOTHING) + continue; + } + r = package_openiterator(t, READONLY, true, &iterator); + RET_UPDATE(result, r); + if (RET_WAS_ERROR(r)) + return result; + while (package_next(&iterator)) { + r = action(&iterator.current, data); + RET_UPDATE(result, r); + if (RET_WAS_ERROR(r)) + break; + } + r = package_closeiterator(&iterator); + RET_ENDUPDATE(result, r); + if (RET_WAS_ERROR(result)) + return result; + } + return result; +} + +retvalue package_foreach_c(struct distribution *distribution, const struct atomlist *components, architecture_t architecture, packagetype_t packagetype, action_each_package action, void *data) { + retvalue result, r; + struct target *t; + struct package_cursor iterator; + + result = RET_NOTHING; + for (t = distribution->targets ; t != NULL ; t = t->next) { + if (components != NULL && + !atomlist_in(components, t->component)) + continue; + if (limitation_missed(architecture, t->architecture)) + continue; + if (limitation_missed(packagetype, t->packagetype)) + continue; + r = package_openiterator(t, READONLY, true, &iterator); + RET_UPDATE(result, r); + if (RET_WAS_ERROR(r)) + return result; + while (package_next(&iterator)) { + r = action(&iterator.current, data); + RET_UPDATE(result, r); + if (RET_WAS_ERROR(r)) + break; + } + r = package_closeiterator(&iterator); + RET_ENDUPDATE(result, r); + if (RET_WAS_ERROR(result)) + return result; + } + return result; +} + +struct target *distribution_gettarget(const struct distribution *distribution, component_t component, architecture_t architecture, packagetype_t packagetype) { + struct target *t = distribution->targets; + + assert (atom_defined(component)); + assert (atom_defined(architecture)); + assert (atom_defined(packagetype)); + + // TODO: think about making read only access and only alowing readwrite when lookedat is set + + while (t != NULL && + (t->component != component || + t->architecture != architecture || + t->packagetype != packagetype)) { + t = t->next; + } + return t; +} + +struct target *distribution_getpart(const struct distribution *distribution, component_t component, architecture_t architecture, packagetype_t packagetype) { + struct target *t = distribution->targets; + + assert (atom_defined(component)); + assert (atom_defined(architecture)); + assert (atom_defined(packagetype)); + + while (t != NULL && + (t->component != component || + t->architecture != architecture || + t->packagetype != packagetype)) { + t = t->next; + } + if (t == NULL) { + fprintf(stderr, +"Internal error in distribution_getpart: Bogus request for c='%s' a='%s' t='%s' in '%s'!\n", + atoms_components[component], + atoms_architectures[architecture], + atoms_packagetypes[packagetype], + distribution->codename); + abort(); + } + return t; +} + +/* mark all distributions matching one of the first argc argv */ +retvalue distribution_match(struct distribution *alldistributions, int argc, const char *argv[], bool lookedat, bool allowreadonly) { + struct distribution *d; + bool found[argc], unusable_as_suite[argc]; + struct distribution *has_suite[argc]; + int i; + + assert (alldistributions != NULL); + + if (argc <= 0) { + for (d = alldistributions ; d != NULL ; d = d->next) { + if (!allowreadonly && d->readonly) + continue; + d->selected = true; + d->lookedat = lookedat; + } + return RET_OK; + } + memset(found, 0, sizeof(found)); + memset(unusable_as_suite, 0, sizeof(unusable_as_suite)); + memset(has_suite, 0, sizeof(has_suite)); + + for (d = alldistributions ; d != NULL ; d = d->next) { + for (i = 0 ; i < argc ; i++) { + if (strcmp(argv[i], d->codename) == 0) { + assert (!found[i]); + found[i] = true; + d->selected = true; + if (lookedat) + d->lookedat = lookedat; + if (!allowreadonly && d->readonly) { + fprintf(stderr, +"Error: %s is readonly, so operation not allowed!\n", + d->codename); + return RET_ERROR; + } + } else if (d->suite != NULL && + strcmp(argv[i], d->suite) == 0) { + if (has_suite[i] != NULL) + unusable_as_suite[i] = true; + has_suite[i] = d; + } + } + } + for (i = 0 ; i < argc ; i++) { + if (!found[i]) { + if (has_suite[i] != NULL && !unusable_as_suite[i]) { + if (!allowreadonly && has_suite[i]->readonly) { + fprintf(stderr, +"Error: %s is readonly, so operation not allowed!\n", + has_suite[i]->codename); + return RET_ERROR; + } + has_suite[i]->selected = true; + if (lookedat) + has_suite[i]->lookedat = lookedat; + continue; + } + fprintf(stderr, +"No distribution definition of '%s' found in '%s/distributions'!\n", + argv[i], global.confdir); + if (unusable_as_suite[i]) + fprintf(stderr, +"(It is not the codename of any distribution and there are multiple\n" +"distributions with this as suite name.)\n"); + return RET_ERROR_MISSING; + } + } + return RET_OK; +} + +retvalue distribution_get(struct distribution *alldistributions, const char *name, bool lookedat, struct distribution **distribution) { + struct distribution *d, *d2; + + d = alldistributions; + while (d != NULL && strcmp(name, d->codename) != 0) + d = d->next; + if (d == NULL) { + for (d2 = alldistributions; d2 != NULL ; d2 = d2->next) { + if (d2->suite == NULL) + continue; + if (strcmp(name, d2->suite) != 0) + continue; + if (d != NULL) { + fprintf(stderr, +"No distribution has '%s' as codename, but multiple as suite name,\n" +"thus it cannot be used to determine a distribution.\n", name); + return RET_ERROR_MISSING; + } + d = d2; + } + } + if (d == NULL) { + fprintf(stderr, +"Cannot find definition of distribution '%s'!\n", + name); + return RET_ERROR_MISSING; + } + d->selected = true; + if (lookedat) + d->lookedat = true; + *distribution = d; + return RET_OK; +} + +retvalue distribution_snapshot(struct distribution *distribution, const char *name) { + struct target *target; + retvalue result, r; + struct release *release; + char *id; + + assert (distribution != NULL); + + r = release_initsnapshot(distribution->codename, name, &release); + if (RET_WAS_ERROR(r)) + return r; + + result = RET_NOTHING; + for (target=distribution->targets; target != NULL ; + target = target->next) { + r = release_mkdir(release, target->relativedirectory); + RET_ENDUPDATE(result, r); + if (RET_WAS_ERROR(r)) + break; + r = target_export(target, false, true, release); + RET_UPDATE(result, r); + if (RET_WAS_ERROR(r)) + break; + if (target->exportmode->release != NULL) { + r = release_directorydescription(release, distribution, + target, target->exportmode->release, + false); + RET_UPDATE(result, r); + if (RET_WAS_ERROR(r)) + break; + } + } + if (!RET_WAS_ERROR(result)) { + result = release_prepare(release, distribution, false); + assert (result != RET_NOTHING); + } + if (RET_WAS_ERROR(result)) { + release_free(release); + return result; + } + result = release_finish(release, distribution); + if (RET_WAS_ERROR(result)) + return r; + id = mprintf("s=%s=%s", distribution->codename, name); + if (FAILEDTOALLOC(id)) + return RET_ERROR_OOM; + r = package_foreach(distribution, + atom_unknown, atom_unknown, atom_unknown, + package_referenceforsnapshot, NULL, id); + free(id); + RET_UPDATE(result, r); + return result; +} + +static retvalue export(struct distribution *distribution, bool onlyneeded) { + struct target *target; + retvalue result, r; + struct release *release; + + if (verbose >= 15) + fprintf(stderr, "trace: export(distribution={codename: %s}, onlyneeded=%s)\n", + distribution->codename, onlyneeded ? "true" : "false"); + assert (distribution != NULL); + + if (distribution->exportoptions[deo_noexport]) + return RET_NOTHING; + + if (distribution->readonly) { + fprintf(stderr, +"Error: trying to re-export read-only distribution %s\n", + distribution->codename); + return RET_ERROR; + } + + r = release_init(&release, distribution->codename, distribution->suite, + distribution->fakecomponentprefix); + if (RET_WAS_ERROR(r)) + return r; + + result = RET_NOTHING; + for (target=distribution->targets; target != NULL ; + target = target->next) { + r = release_mkdir(release, target->relativedirectory); + RET_ENDUPDATE(result, r); + if (RET_WAS_ERROR(r)) + break; + r = target_export(target, onlyneeded, false, release); + RET_UPDATE(result, r); + if (RET_WAS_ERROR(r)) + break; + if (target->exportmode->release != NULL) { + r = release_directorydescription(release, distribution, + target, target->exportmode->release, + onlyneeded); + RET_UPDATE(result, r); + if (RET_WAS_ERROR(r)) + break; + } + } + if (!RET_WAS_ERROR(result) && distribution->contents.flags.enabled) { + r = contents_generate(distribution, release, onlyneeded); + } + if (!RET_WAS_ERROR(result)) { + result = release_prepare(release, distribution, onlyneeded); + if (result == RET_NOTHING) { + release_free(release); + return result; + } + } + if (RET_WAS_ERROR(result)) { + bool workleft = false; + release_free(release); + fprintf(stderr, "ERROR: Could not finish exporting '%s'!\n", + distribution->codename); + for (target=distribution->targets; target != NULL ; + target = target->next) { + workleft |= target->saved_wasmodified; + } + if (workleft) { + (void)fputs( +"This means that from outside your repository will still look like before (and\n" +"should still work if this old state worked), but the changes intended with this\n" +"call will not be visible until you call export directly (via reprepro export)\n" +"Changes will also get visible when something else changes the same file and\n" +"thus creates a new export of that file, but even changes to other parts of the\n" +"same distribution will not!\n", + stderr); + } + } else { + r = release_finish(release, distribution); + RET_UPDATE(result, r); + } + if (RET_IS_OK(result)) + distribution->status = RET_NOTHING; + return result; +} + +retvalue distribution_fullexport(struct distribution *distribution) { + return export(distribution, false); +} + +retvalue distribution_freelist(struct distribution *distributions) { + retvalue result, r; + + result = RET_NOTHING; + while (distributions != NULL) { + struct distribution *d = distributions->next; + r = distribution_free(distributions); + RET_UPDATE(result, r); + distributions = d; + } + return result; +} + +retvalue distribution_exportlist(enum exportwhen when, struct distribution *distributions) { + retvalue result, r; + bool todo = false; + struct distribution *d; + + if (verbose >= 15) + fprintf(stderr, "trace: distribution_exportlist() called.\n"); + if (when == EXPORT_SILENT_NEVER) { + for (d = distributions ; d != NULL ; d = d->next) { + struct target *t; + + for (t = d->targets ; t != NULL ; t = t->next) + t->wasmodified = false; + } + return RET_NOTHING; + } + if (when == EXPORT_NEVER) { + if (verbose > 10) + fprintf(stderr, +"Not exporting anything as --export=never specified\n"); + return RET_NOTHING; + } + + for (d=distributions; d != NULL; d = d->next) { + if (d->omitted || !d->selected || d->exportoptions[deo_noexport]) + continue; + if (d->lookedat && (RET_IS_OK(d->status) || + (d->status == RET_NOTHING && when != EXPORT_CHANGED) || + when == EXPORT_FORCE)) { + todo = true; + } + } + + if (verbose >= 0 && todo) + printf("Exporting indices...\n"); + + result = RET_NOTHING; + for (d=distributions; d != NULL; d = d->next) { + if (verbose >= 20) + fprintf(stderr, " looking at distribution {codename: %s, exportoptions[deo_noexport]: %s, omitted: %s, selected: %s, status: %d}.\n", + d->codename, + d->exportoptions[deo_noexport] ? "true" : "false", + d->omitted ? "true" : "false", + d->selected ? "true" : "false", + d->status); + if (d->exportoptions[deo_noexport]) + continue; + if (d->omitted || !d->selected) + continue; + if (!d->lookedat) { + if (verbose >= 30) + printf( +" Not exporting %s because not looked at.\n", d->codename); + } else if ((RET_WAS_ERROR(d->status)||interrupted()) && + when != EXPORT_FORCE) { + if (verbose >= 10) + fprintf(stderr, +" Not exporting %s because there have been errors and no --export=force.\n", + d->codename); + } else if (d->status==RET_NOTHING && when==EXPORT_CHANGED) { + struct target *t; + + if (verbose >= 10) + printf( +" Not exporting %s because of no recorded changes and --export=changed.\n", + d->codename); + + /* some paranoid check */ + + for (t = d->targets ; t != NULL ; t = t->next) { + if (t->wasmodified) { + fprintf(stderr, +"A paranoid check found distribution %s would not have been exported,\n" +"despite having parts that are marked changed by deeper code.\n" +"Please report this and how you got this message as bugreport. Thanks.\n" +"Doing a export despite --export=changed....\n", + d->codename); + r = export(d, true); + RET_UPDATE(result, r); + break; + } + } + } else { + assert (RET_IS_OK(d->status) || + (d->status == RET_NOTHING && + when != EXPORT_CHANGED) || + when == EXPORT_FORCE); + r = export(d, true); + RET_UPDATE(result, r); + } + } + return result; +} + + +/* get a pointer to the apropiate part of the linked list */ +struct distribution *distribution_find(struct distribution *distributions, const char *name) { + struct distribution *d = distributions, *r; + + while (d != NULL && strcmp(d->codename, name) != 0) + d = d->next; + if (d != NULL) + return d; + d = distributions; + while (d != NULL && !strlist_in(&d->alsoaccept, name)) + d = d->next; + r = d; + if (r != NULL) { + d = d->next; + while (d != NULL && ! strlist_in(&d->alsoaccept, name)) + d = d->next; + if (d == NULL) + return r; + fprintf(stderr, +"No distribution has codename '%s' and multiple have it in AlsoAcceptFor!\n", + name); + return NULL; + } + d = distributions; + while (d != NULL && (d->suite == NULL || strcmp(d->suite, name) != 0)) + d = d->next; + r = d; + if (r == NULL) { + fprintf(stderr, "No distribution named '%s' found!\n", name); + return NULL; + } + d = d->next; + while (d != NULL && (d->suite == NULL || strcmp(d->suite, name) != 0)) + d = d->next; + if (d == NULL) + return r; + fprintf(stderr, +"No distribution has codename '%s' and multiple have it as suite-name!\n", + name); + return NULL; +} + +retvalue distribution_loadalloverrides(struct distribution *distribution) { + retvalue r; + + if (distribution->overrides.deb == NULL) { + r = override_read(distribution->deb_override, + &distribution->overrides.deb, false); + if (RET_WAS_ERROR(r)) { + distribution->overrides.deb = NULL; + return r; + } + } + if (distribution->overrides.udeb == NULL) { + r = override_read(distribution->udeb_override, + &distribution->overrides.udeb, false); + if (RET_WAS_ERROR(r)) { + distribution->overrides.udeb = NULL; + return r; + } + } + if (distribution->overrides.dsc == NULL) { + r = override_read(distribution->dsc_override, + &distribution->overrides.dsc, true); + if (RET_WAS_ERROR(r)) { + distribution->overrides.dsc = NULL; + return r; + } + } + if (distribution->overrides.deb != NULL || + distribution->overrides.udeb != NULL || + distribution->overrides.dsc != NULL) + return RET_OK; + else + return RET_NOTHING; +} + +retvalue distribution_loaduploaders(struct distribution *distribution) { + if (distribution->uploaders != NULL) { + if (distribution->uploaderslist != NULL) + return RET_OK; + return uploaders_get(&distribution->uploaderslist, + distribution->uploaders); + } else { + distribution->uploaderslist = NULL; + return RET_NOTHING; + } +} + +void distribution_unloaduploaders(struct distribution *distribution) { + if (distribution->uploaderslist != NULL) { + uploaders_unlock(distribution->uploaderslist); + distribution->uploaderslist = NULL; + } +} + +retvalue distribution_prepareforwriting(struct distribution *distribution) { + retvalue r; + + if (distribution->readonly) { + fprintf(stderr, +"Error: distribution %s is read-only.\n" +"Current operation not possible because it needs write access.\n", + distribution->codename); + return RET_ERROR; + } + + if (distribution->logger != NULL) { + r = logger_prepare(distribution->logger); + if (RET_WAS_ERROR(r)) + return r; + } + distribution->lookedat = true; + return RET_OK; +} + +/* delete every package decider returns RET_OK for */ +retvalue package_remove_each(struct distribution *distribution, const struct atomlist *components, const struct atomlist *architectures, const struct atomlist *packagetypes, action_each_package decider, struct trackingdata *trackingdata, void *data) { + retvalue result, r; + struct target *t; + struct package_cursor iterator; + + if (distribution->readonly) { + fprintf(stderr, +"Error: trying to delete packages in read-only distribution %s.\n", + distribution->codename); + return RET_ERROR; + } + + result = RET_NOTHING; + for (t = distribution->targets ; t != NULL ; t = t->next) { + if (!target_matches(t, components, architectures, packagetypes)) + continue; + r = package_openiterator(t, READWRITE, true, &iterator); + RET_UPDATE(result, r); + if (RET_WAS_ERROR(r)) + return result; + while (package_next(&iterator)) { + r = decider(&iterator.current, data); + RET_UPDATE(result, r); + if (RET_WAS_ERROR(r)) + break; + if (RET_IS_OK(r)) { + r = package_remove_by_cursor(&iterator, + distribution->logger, trackingdata); + RET_UPDATE(result, r); + RET_UPDATE(distribution->status, r); + } + } + r = package_closeiterator(&iterator); + RET_ENDUPDATE(result, r); + if (RET_WAS_ERROR(result)) + return result; + } + return result; +} |