/* This file is part of "reprepro" * Copyright (C) 2003,2004,2005,2006,2007,2008,2009 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "error.h" #define DEFINE_IGNORE_VARIABLES #include "ignore.h" #include "mprintf.h" #include "strlist.h" #include "atoms.h" #include "dirs.h" #include "names.h" #include "filecntl.h" #include "files.h" #include "filelist.h" #include "database_p.h" #include "target.h" #include "reference.h" #include "binaries.h" #include "sources.h" #include "release.h" #include "aptmethod.h" #include "updates.h" #include "pull.h" #include "upgradelist.h" #include "signature.h" #include "debfile.h" #include "checkindeb.h" #include "checkindsc.h" #include "checkin.h" #include "downloadcache.h" #include "termdecide.h" #include "tracking.h" #include "optionsfile.h" #include "dpkgversions.h" #include "incoming.h" #include "override.h" #include "log.h" #include "copypackages.h" #include "uncompression.h" #include "sourceextraction.h" #include "pool.h" #include "printlistformat.h" #include "globmatch.h" #include "needbuild.h" #include "archallflood.h" #ifndef STD_BASE_DIR #define STD_BASE_DIR "." #endif #ifndef STD_METHOD_DIR #define STD_METHOD_DIR "/usr/lib/apt/methods" #endif #ifndef LLONG_MAX #define LLONG_MAX __LONG_LONG_MAX__ #endif /* global options available to the rest */ struct global_config global; /* global options */ static char /*@only@*/ /*@notnull@*/ // *g* *x_basedir = NULL, *x_outdir = NULL, *x_distdir = NULL, *x_dbdir = NULL, *x_listdir = NULL, *x_confdir = NULL, *x_logdir = NULL, *x_morguedir = NULL, *x_methoddir = NULL; static char /*@only@*/ /*@null@*/ *x_section = NULL, *x_priority = NULL, *x_component = NULL, *x_architecture = NULL, *x_packagetype = NULL; static char /*@only@*/ /*@null@*/ *listformat = NULL; static char /*@only@*/ *gunzip = NULL, *bunzip2 = NULL, *unlzma = NULL, *unxz = NULL, *gnupghome = NULL; static int listmax = -1; static int listskip = 0; static int delete = D_COPY; static bool nothingiserror = false; static bool nolistsdownload = false; static bool keepunreferenced = false; static bool keepunusednew = false; static bool askforpassphrase = false; static bool guessgpgtty = true; static bool skipold = true; static size_t waitforlock = 0; static enum exportwhen export = EXPORT_CHANGED; int verbose = 0; static bool fast = false; static bool verbosedatabase = false; static enum spacecheckmode spacecheckmode = scm_FULL; /* default: 100 MB for database to grow */ static off_t reserveddbspace = 1024*1024*100 /* 1MB safety margin for other fileystems */; static off_t reservedotherspace = 1024*1024; /* define for each config value an owner, and only higher owners are allowed * to change something owned by lower owners. */ enum config_option_owner config_state, #define O(x) owner_ ## x = CONFIG_OWNER_DEFAULT O(fast), O(x_morguedir), O(x_outdir), O(x_basedir), O(x_distdir), O(x_dbdir), O(x_listdir), O(x_confdir), O(x_logdir), O(x_methoddir), O(x_section), O(x_priority), O(x_component), O(x_architecture), O(x_packagetype), O(nothingiserror), O(nolistsdownload), O(keepunusednew), O(keepunreferenced), O(keeptemporaries), O(keepdirectories), O(askforpassphrase), O(skipold), O(export), O(waitforlock), O(spacecheckmode), O(reserveddbspace), O(reservedotherspace), O(guessgpgtty), O(verbosedatabase), O(gunzip), O(bunzip2), O(unlzma), O(unxz), O(gnupghome), O(listformat), O(listmax), O(listskip); #undef O #define CONFIGSET(variable,value) if(owner_ ## variable <= config_state) { \ owner_ ## variable = config_state; \ variable = value; } #define CONFIGGSET(variable,value) if(owner_ ## variable <= config_state) { \ owner_ ## variable = config_state; \ global.variable = value; } #define CONFIGDUP(variable,value) if(owner_ ## variable <= config_state) { \ owner_ ## variable = config_state; \ free(variable); \ variable = strdup(value); \ if( variable == NULL ) { \ (void)fputs("Out of Memory!",stderr); \ exit(EXIT_FAILURE); \ } } #define y(type,name) type name #define n(type,name) UNUSED(type dummy_ ## name) #define ACTION_N(act,sp,args,name) static retvalue action_n_ ## act ## _ ## sp ## _ ## name ( \ UNUSED(struct distribution *dummy2), \ UNUSED(struct database *dummy), \ sp(const char *, section), \ sp(const char *, priority), \ act(const struct atomlist *, architectures), \ act(const struct atomlist *, components), \ act(const struct atomlist *, packagetypes), \ int argc, args(const char *,argv[])) #define ACTION_C(act,sp,name) static retvalue action_c_ ## act ## _ ## sp ## _ ## name ( \ struct distribution *alldistributions, \ UNUSED(struct database *dummy), \ sp(const char *, section), \ sp(const char *, priority), \ act(const struct atomlist *, architectures), \ act(const struct atomlist *, components), \ act(const struct atomlist *, packagetypes), \ int argc,const char *argv[]) #define ACTION_B(act,sp,u,name) static retvalue action_b_ ## act ## _ ## sp ## _ ## name ( \ u(struct distribution *,alldistributions), \ struct database *database, \ sp(const char *, section), \ sp(const char *, priority), \ act(const struct atomlist *, architectures), \ act(const struct atomlist *, components), \ act(const struct atomlist *, packagetypes), \ int argc, const char *argv[]) #define ACTION_L(act,sp,u,args,name) static retvalue action_l_ ## act ## _ ## sp ## _ ## name ( \ struct distribution *alldistributions, \ u(struct database *,database), \ sp(const char *, section), \ sp(const char *, priority), \ act(const struct atomlist *, architectures), \ act(const struct atomlist *, components), \ act(const struct atomlist *, packagetypes), \ int argc, args(const char *,argv[])) #define ACTION_R(act,sp,d,a,name) static retvalue action_r_ ## act ## _ ## sp ## _ ## name ( \ d(struct distribution *, alldistributions), \ struct database *database, \ sp(const char *, section), \ sp(const char *, priority), \ act(const struct atomlist *, architectures), \ act(const struct atomlist *, components), \ act(const struct atomlist *, packagetypes), \ a(int, argc), a(const char *, argv[])) #define ACTION_T(act,sp,name) static retvalue action_t_ ## act ## _ ## sp ## _ ## name ( \ UNUSED(struct distribution *ddummy), \ struct database *database, \ sp(const char *, section), \ sp(const char *, priority), \ act(const struct atomlist *, architectures), \ act(const struct atomlist *, components), \ act(const struct atomlist *, packagetypes), \ UNUSED(int argc), UNUSED(const char *dummy4[])) #define ACTION_F(act,sp,d,a,name) static retvalue action_f_ ## act ## _ ## sp ## _ ## name ( \ d(struct distribution *,alldistributions), \ struct database *database, \ sp(const char *, section), \ sp(const char *, priority), \ act(const struct atomlist *, architectures), \ act(const struct atomlist *, components), \ act(const struct atomlist *, packagetypes), \ a(int, argc), a(const char *, argv[])) #define ACTION_RF(act,sp,u,name) static retvalue action_rf_ ## act ## _ ## sp ## _ ## name ( \ u(struct distribution *, alldistributions), \ struct database *database, \ sp(const char *, section), \ sp(const char *, priority), \ act(const struct atomlist *, architectures), \ act(const struct atomlist *, components), \ act(const struct atomlist *, packagetypes), \ u(int, argc), u(const char *, argv[])) #define ACTION_D(act,sp,u,name) static retvalue action_d_ ## act ## _ ## sp ## _ ## name ( \ struct distribution *alldistributions, \ struct database *database, \ sp(const char *, section), \ sp(const char *, priority), \ act(const struct atomlist *, architectures), \ act(const struct atomlist *, components), \ act(const struct atomlist *, packagetypes), \ u(int,argc), u(const char *,argv[])) ACTION_N(n, n, y, printargs) { int i; fprintf(stderr,"argc: %d\n",argc); for( i=0 ; i < argc ; i++ ) { fprintf(stderr,"%s\n",argv[i]); } return RET_OK; } ACTION_N(n, n, n, dumpuncompressors) { enum compression c; assert( argc == 1 ); for( c = 0 ; c < c_COUNT ; c++ ) { if( c == c_none ) continue; printf("%s: ", uncompression_suffix[c]); if( uncompression_builtin(c) ) { if( extern_uncompressors[c] != NULL ) printf("built-in + '%s'\n", extern_uncompressors[c]); else printf("built-in\n"); } else if( extern_uncompressors[c] != NULL ) printf("'%s'\n", extern_uncompressors[c]); else switch( c ) { case c_bzip2: printf("not supported (install bzip2 or use --bunzip2 to tell where bunzip2 is).\n"); break; case c_lzma: printf("not supported (install lzma or use --unlzma to tell where unlzma is).\n"); break; case c_xz: printf("not supported (install xz-utils or use --unxz to tell where unxz is).\n"); break; default: printf("not supported\n"); } } return RET_OK; } ACTION_N(n, n, y, uncompress) { enum compression c; assert( argc == 4 ); c = c_none + 1; while( c < c_COUNT && strcmp(argv[1], uncompression_suffix[c]) != 0 ) c++; if( c >= c_COUNT ) { fprintf(stderr, "Unknown compression format '%s'\n", argv[1]); return RET_ERROR; } if( !uncompression_supported(c) ) { fprintf(stderr, "Cannot uncompress format '%s'\nCheck __dumpuncompressors for more details.\n", argv[1]); return RET_ERROR; } return uncompress_file(argv[2], argv[3], c); } ACTION_N(n, n, y, extractcontrol) { retvalue result; char *control; assert( argc == 2 ); result = extractcontrol(&control,argv[1]); if( RET_IS_OK(result) ) { puts(control); free(control); } return result; } ACTION_N(n, n, y, extractfilelist) { retvalue result; char *filelist; size_t fls, len; size_t lengths[256]; const unsigned char *dirs[256]; int depth = 0, i, j; assert( argc == 2 ); result = getfilelist(&filelist, &fls, argv[1]); if( RET_IS_OK(result) ) { const unsigned char *p = (unsigned char*)filelist; while( *p != '\0' ) { unsigned char c = *(p++); if( c > 2 ) { if( depth >= c ) depth -= c; else depth = 0; } else if( c == 2 ) { len = 0; while( *p == 255 ) { len +=255; p++; } len += *(p++); lengths[depth] = len; dirs[depth++] = p; p += len; } else { len = 0; while( *p == 255 ) { len +=255; p++; } len += *(p++); (void)putchar('/'); for( i = 0 ; i < depth ; i++ ) { const unsigned char *n = dirs[i]; j = lengths[i]; while( j-- > 0 ) (void)putchar(*(n++)); (void)putchar('/'); } while( len-- > 0 ) (void)putchar(*(p++)); (void)putchar('\n'); } } free(filelist); } return result; } ACTION_N(n, n, y, extractsourcesection) { struct dsc_headers dsc; struct sourceextraction *extraction; char *section = NULL, *priority = NULL, *directory, *filename; retvalue result, r; bool broken; int i; assert( argc == 2 ); r = sources_readdsc(&dsc, argv[1], argv[1], &broken); if( !RET_IS_OK(r) ) return r; if( broken && !IGNORING_(brokensignatures, "'%s' contains only broken signatures.\n" "This most likely means the file was damaged or edited improperly\n", argv[1]) ) return RET_ERROR; r = dirs_getdirectory(argv[1], &directory); if( RET_WAS_ERROR(r) ) { sources_done(&dsc); return r; } assert( RET_IS_OK(r) ); extraction = sourceextraction_init(§ion, &priority); if( FAILEDTOALLOC(extraction) ) { sources_done(&dsc); return RET_ERROR_OOM; } for( i = 0 ; i < dsc.files.names.count ; i ++ ) sourceextraction_setpart(extraction, i, dsc.files.names.values[i]); result = RET_OK; while( sourceextraction_needs(extraction, &i) ) { filename = calc_dirconcat(directory, dsc.files.names.values[i]); if( FAILEDTOALLOC(filename) ) { result = RET_ERROR_OOM; break; } r = sourceextraction_analyse(extraction, filename); free(filename); if( RET_WAS_ERROR(r) ) { result = r; break; } } free(directory); if( RET_WAS_ERROR(result) ) { sourceextraction_abort(extraction); } else { r = sourceextraction_finish(extraction); RET_UPDATE(result, r); } if( RET_IS_OK(result) ) { if( section != NULL ) printf("Section: %s\n", section); if( priority != NULL ) printf("Priority: %s\n", priority); } sources_done(&dsc); free(section); free(priority); return result; } ACTION_F(n, n, n, y, fakeemptyfilelist) { assert( argc == 2 ); return fakefilelist(database, argv[1]); } ACTION_F(n, n, n, y, generatefilelists) { assert( argc == 2 || argc == 3 ); if( argc == 2 ) return files_regenerate_filelist(database, false); if( strcmp(argv[1], "reread") == 0 ) return files_regenerate_filelist(database, true); fprintf(stderr,"Error: Unrecognized second argument '%s'\n" "Syntax: reprepro generatefilelists [reread]\n", argv[1]); return RET_ERROR; } ACTION_T(n, n, translatefilelists) { return database_translate_filelists(database); } ACTION_N(n, n, n, translatelegacychecksums) { assert( argc == 1); return database_translate_legacy_checksums( verbosedatabase || verbose > 10); } ACTION_F(n, n, n, n, addmd5sums) { char buffer[2000],*c,*m; retvalue result,r; result = RET_NOTHING; while( fgets(buffer,1999,stdin) != NULL ) { struct checksums *checksums; c = strchr(buffer,'\n'); if( c == NULL ) { fprintf(stderr,"Line too long\n"); return RET_ERROR; } *c = '\0'; m = strchr(buffer,' '); if( m == NULL ) { fprintf(stderr,"Malformed line\n"); return RET_ERROR; } *m = '\0'; m++; if( *m == '\0' ) { fprintf(stderr,"Malformed line\n"); return RET_ERROR; } r = checksums_setall(&checksums, m, strlen(m), NULL); if( RET_WAS_ERROR(r) ) return r; r = files_add_checksums(database, buffer, checksums); RET_UPDATE(result,r); checksums_free(checksums); } return result; } ACTION_R(n, n, n, y, removereferences) { assert( argc == 2 ); return references_remove(database, argv[1]); } ACTION_R(n, n, n, n, dumpreferences) { struct cursor *cursor; retvalue result, r; const char *found_to, *found_by; r = table_newglobalcursor(database->references, &cursor); if( !RET_IS_OK(r) ) return r; result = RET_OK; while( cursor_nexttemp(database->references, cursor, &found_to, &found_by) ) { if( fputs(found_by, stdout) == EOF || putchar(' ') == EOF || puts(found_to) == EOF ) { result = RET_ERROR; break; } result = RET_OK; if( interrupted() ) { result = RET_ERROR_INTERRUPTED; break; } } r = cursor_close(database->references, cursor); RET_ENDUPDATE(result, r); return result; } static retvalue checkifreferenced(void *data, const char *filekey) { struct database *database = data; retvalue r; r = references_isused(database, filekey); if( r == RET_NOTHING ) { printf("%s\n",filekey); return RET_OK; } else if( RET_IS_OK(r) ) { return RET_NOTHING; } else return r; } ACTION_RF(n, n, n, dumpunreferenced) { retvalue result; result = files_foreach(database, checkifreferenced, database); return result; } static retvalue deleteifunreferenced(void *data, const char *filekey) { struct database *database = data; retvalue r; r = references_isused(database,filekey); if( r == RET_NOTHING ) { r = pool_delete(database, filekey); return r; } else if( RET_IS_OK(r) ) { return RET_NOTHING; } else return r; } ACTION_RF(n, n, n, deleteunreferenced) { retvalue result; if( keepunreferenced ) { if( owner_keepunreferenced == CONFIG_OWNER_CMDLINE ) fprintf(stderr,"Calling deleteunreferenced with --keepunreferencedfiles does not really make sense, does it?\n"); else fprintf(stderr, "Error: deleteunreferenced called with option\n" "'keepunreferencedfiles' activated. Please run\n" "'reprepro --nokeepunreferencedfiles deleteunreferenced',\n" "if you are sure you want to delete those files.\n"); return RET_ERROR; } result = files_foreach(database, deleteifunreferenced, database); return result; } ACTION_R(n, n, n, y, addreference) { assert( argc == 2 || argc == 3 ); return references_increment(database, argv[1], argv[2]); } static retvalue remove_from_target(struct database *db, struct distribution *distribution, struct trackingdata *trackingdata, struct target *target, int count, const char * const *names, int *todo, bool *gotremoved) { retvalue result,r; int i; result = RET_NOTHING; for( i = 0 ; i < count ; i++ ){ r = target_removepackage(target, distribution->logger, db, names[i], trackingdata); RET_UPDATE(distribution->status, r); if( RET_IS_OK(r) ) { if( !gotremoved[i] ) (*todo)--; gotremoved[i] = true; } RET_UPDATE(result,r); } return result; } ACTION_D(y, n, y, remove) { retvalue result,r; struct distribution *distribution; struct target *t; bool *gotremoved; int todo; trackingdb tracks; struct trackingdata trackingdata; r = distribution_get(alldistributions, argv[1], true, &distribution); assert( r != RET_NOTHING ); if( RET_WAS_ERROR(r) ) return r; if( distribution->readonly ) { fprintf(stderr, "Cannot remove packages from read-only distribution '%s'\n", distribution->codename); return RET_ERROR; } r = distribution_prepareforwriting(distribution); if( RET_WAS_ERROR(r) ) return r; if( distribution->tracking != dt_NONE ) { r = tracking_initialize(&tracks, database, distribution, false); if( RET_WAS_ERROR(r) ) { return r; } r = trackingdata_new(tracks,&trackingdata); if( RET_WAS_ERROR(r) ) { (void)tracking_done(tracks); return r; } } todo = argc-2; gotremoved = calloc(argc-2, sizeof(*gotremoved)); result = RET_NOTHING; if( FAILEDTOALLOC(gotremoved) ) result = RET_ERROR_OOM; else for( t = distribution->targets ; t != NULL ; t = t->next ) { if( !target_matches(t, components, architectures, packagetypes) ) continue; r = target_initpackagesdb(t, database, READWRITE); RET_UPDATE(result, r); if( RET_WAS_ERROR(r) ) break; r = remove_from_target(database, distribution, (distribution->tracking != dt_NONE)?&trackingdata:NULL, t, argc-2, argv+2, &todo, gotremoved); RET_UPDATE(result, r); r = target_closepackagesdb(t); RET_UPDATE(distribution->status, r); RET_UPDATE(result, r); if( RET_WAS_ERROR(result) ) break; } logger_wait(); r = distribution_export(export, distribution, database); RET_ENDUPDATE(result,r); if( distribution->tracking != dt_NONE ) { if( RET_WAS_ERROR(result) ) trackingdata_done(&trackingdata); else trackingdata_finish(tracks, &trackingdata, database); r = tracking_done(tracks); RET_ENDUPDATE(result,r); } if( verbose >= 0 && !RET_WAS_ERROR(result) && todo > 0 ) { int i = argc - 2; (void)fputs("Not removed as not found: ", stderr); while( i > 0 ) { i--; assert(gotremoved != NULL); if( !gotremoved[i] ) { (void)fputs(argv[2 + i], stderr); todo--; if( todo > 0 ) (void)fputs(", ", stderr); } } (void)fputc('\n', stderr); } free(gotremoved); return result; } struct removesrcdata { const char *sourcename; const char /*@null@*/*sourceversion; }; static retvalue package_source_fits(UNUSED(struct database *da), UNUSED(struct distribution *di), struct target *target, const char *packagename, const char *control, void *data) { struct removesrcdata *d = data; char *sourcename, *sourceversion; retvalue r; r = target->getsourceandversion(control, packagename, &sourcename, &sourceversion); if( !RET_IS_OK(r) ) return r; if( strcmp(sourcename, d->sourcename) != 0 ) { free(sourcename); free(sourceversion); return RET_NOTHING; } if( d->sourceversion == NULL ) { free(sourcename); free(sourceversion); return RET_OK; } if( strcmp(sourceversion, d->sourceversion) != 0 ) { free(sourcename); free(sourceversion); return RET_NOTHING; } free(sourcename); free(sourceversion); return RET_OK; } ACTION_D(n, n, y, removesrc) { retvalue result, r; struct distribution *distribution; trackingdb tracks; struct removesrcdata data; r = distribution_get(alldistributions, argv[1], true, &distribution); assert( r != RET_NOTHING ); if( RET_WAS_ERROR(r) ) return r; if( distribution->readonly ) { fprintf(stderr, "Error: Cannot remove packages from read-only distribution '%s'\n", distribution->codename); return RET_ERROR; } r = distribution_prepareforwriting(distribution); if( RET_WAS_ERROR(r) ) return r; if( distribution->tracking != dt_NONE ) { r = tracking_initialize(&tracks, database, distribution, false); if( RET_WAS_ERROR(r) ) { return r; } if( r == RET_NOTHING ) tracks = NULL; } else tracks = NULL; result = RET_NOTHING; if( tracks != NULL ) { result = tracking_removepackages(tracks, database, distribution, argv[2], (argc <= 3)?NULL:argv[3]); if( RET_WAS_ERROR(r) ) { r = tracking_done(tracks); RET_ENDUPDATE(result,r); return result; } if( result == RET_NOTHING ) { if( verbose >= -2 ) { if( argc == 3 ) fprintf(stderr, "Nothing about source package '%s' found in the tracking data of '%s'!\n" "This either means nothing from this source in this version is there,\n" "or the tracking information might be out of date.\n", argv[2], distribution->codename); else fprintf(stderr, "Nothing about '%s' version '%s' found in the tracking data of '%s'!\n" "This either means nothing from this source in this version is there,\n" "or the tracking information might be out of date.\n", argv[2], argv[3], distribution->codename); } } else { r = distribution_export(export, distribution, database); RET_ENDUPDATE(result,r); } r = tracking_done(tracks); RET_ENDUPDATE(result,r); return result; } data.sourcename = argv[2]; if( argc <= 3 ) data.sourceversion = NULL; else data.sourceversion = argv[3]; result = distribution_remove_packages(distribution, database, // TODO: why not arch comp pt here? atom_unknown, atom_unknown, atom_unknown, package_source_fits, NULL, &data); r = distribution_export(export, distribution, database); RET_ENDUPDATE(result, r); return result; } static retvalue package_matches_condition(UNUSED(struct database *da), UNUSED(struct distribution *di), struct target *target, UNUSED(const char *pa), const char *control, void *data) { term *condition = data; return term_decidechunktarget(condition, control, target); } ACTION_D(y, n, y, removefilter) { retvalue result, r; struct distribution *distribution; trackingdb tracks; struct trackingdata trackingdata; term *condition; assert( argc == 3 ); r = distribution_get(alldistributions, argv[1], true, &distribution); assert( r != RET_NOTHING ); if( RET_WAS_ERROR(r) ) return r; if( distribution->readonly ) { fprintf(stderr, "Error: Cannot remove packages from read-only distribution '%s'\n", distribution->codename); return RET_ERROR; } result = term_compilefortargetdecision(&condition, argv[2]); if( RET_WAS_ERROR(result) ) return result; r = distribution_prepareforwriting(distribution); if( RET_WAS_ERROR(r) ) { term_free(condition); return r; } if( distribution->tracking != dt_NONE ) { r = tracking_initialize(&tracks, database, distribution, false); if( RET_WAS_ERROR(r) ) { term_free(condition); return r; } if( r == RET_NOTHING ) tracks = NULL; else { r = trackingdata_new(tracks, &trackingdata); if( RET_WAS_ERROR(r) ) { (void)tracking_done(tracks); term_free(condition); return r; } } } else tracks = NULL; result = distribution_remove_packages(distribution, database, components, architectures, packagetypes, package_matches_condition, (tracks != NULL)?&trackingdata:NULL, condition); r = distribution_export(export, distribution, database); RET_ENDUPDATE(result, r); if( tracks != NULL ) { trackingdata_finish(tracks, &trackingdata, database); r = tracking_done(tracks); RET_ENDUPDATE(result,r); } term_free(condition); return result; } static retvalue package_matches_glob(UNUSED(struct database *da), UNUSED(struct distribution *di), UNUSED(struct target *ta), const char *packagename, UNUSED(const char *control), void *data) { if( globmatch(packagename, data) ) return RET_OK; else return RET_NOTHING; } ACTION_D(y, n, y, removematched) { retvalue result, r; struct distribution *distribution; trackingdb tracks; struct trackingdata trackingdata; assert( argc == 3 ); r = distribution_get(alldistributions, argv[1], true, &distribution); assert( r != RET_NOTHING ); if( RET_WAS_ERROR(r) ) return r; if( distribution->readonly ) { fprintf(stderr, "Error: Cannot remove packages from read-only distribution '%s'\n", distribution->codename); return RET_ERROR; } r = distribution_prepareforwriting(distribution); if( RET_WAS_ERROR(r) ) return r; if( distribution->tracking != dt_NONE ) { r = tracking_initialize(&tracks, database, distribution, false); if( RET_WAS_ERROR(r) ) return r; if( r == RET_NOTHING ) tracks = NULL; else { r = trackingdata_new(tracks, &trackingdata); if( RET_WAS_ERROR(r) ) { (void)tracking_done(tracks); return r; } } } else tracks = NULL; result = distribution_remove_packages(distribution, database, components, architectures, packagetypes, package_matches_glob, (tracks != NULL)?&trackingdata:NULL, (void*)argv[2]); r = distribution_export(export, distribution, database); RET_ENDUPDATE(result, r); if( tracks != NULL ) { trackingdata_finish(tracks, &trackingdata, database); r = tracking_done(tracks); RET_ENDUPDATE(result,r); } return result; } ACTION_B(y, n, y, buildneeded) { retvalue r; struct distribution *distribution; const char *glob; architecture_t arch; if( architectures != NULL ) { fprintf(stderr, "Error: build-needing cannot be used with --architecture!\n"); return RET_ERROR; } if( packagetypes != NULL ) { fprintf(stderr, "Error: build-needing cannot be used with --packagetype!\n"); return RET_ERROR; } if( argc == 4 ) glob = argv[3]; else glob = NULL; arch = architecture_find(argv[2]); if( !atom_defined(arch) ) { fprintf(stderr, "Error: Architecture '%s' is not known!\n", argv[2]); return RET_ERROR; } if( arch == architecture_source || arch == architecture_all ) { fprintf(stderr, "Error: Architecture '%s' makes no sense for build-needing!\n", argv[2]); return RET_ERROR; } r = distribution_get(alldistributions, argv[1], false, &distribution); assert( r != RET_NOTHING ); if( RET_WAS_ERROR(r) ) return r; if( !atomlist_in(&distribution->architectures, architecture_source) ) { fprintf(stderr, "Error: Architecture '%s' does not contain sources. build-needing cannot be used!\n", distribution->codename); return RET_ERROR; } if( !atomlist_in(&distribution->architectures, arch) ) { fprintf(stderr, "Error: Architecture '%s' not found in distribution '%s'!\n", argv[2], distribution->codename); return RET_ERROR; } return find_needs_build(database, distribution, arch, components, glob); } static retvalue list_in_target(struct database *database, struct target *target, const char *packagename) { retvalue r,result; char *control; if( listmax == 0 ) return RET_NOTHING; r = target_initpackagesdb(target, database, READONLY); if( !RET_IS_OK(r) ) return r; result = table_getrecord(target->packages, packagename, &control); if( RET_IS_OK(result) ) { if( listskip <= 0 ) { r = listformat_print(listformat, target, packagename, control); RET_UPDATE(result, r); if( listmax > 0 ) listmax--; } else listskip--; free(control); } r = target_closepackagesdb(target); RET_ENDUPDATE(result, r); return result; } static retvalue list_package(UNUSED(struct database *dummy1), UNUSED(struct distribution *dummy2), struct target *target, const char *package, const char *control, UNUSED(void *dummy3)) { if( listmax == 0 ) return RET_NOTHING; if( listskip <= 0 ) { if( listmax > 0 ) listmax--; return listformat_print(listformat, target, package, control); } else { listskip--; return RET_NOTHING; } } ACTION_B(y, n, y, list) { retvalue result = RET_NOTHING, r; struct distribution *distribution; struct target *t; assert( argc >= 2 ); r = distribution_get(alldistributions, argv[1], false, &distribution); assert( r != RET_NOTHING ); if( RET_WAS_ERROR(r) ) return r; if( argc == 2 ) return distribution_foreach_package(distribution, database, components, architectures, packagetypes, list_package, NULL, NULL); else for( t = distribution->targets ; t != NULL ; t = t->next ) { if( !target_matches(t, components, architectures, packagetypes) ) continue; r = list_in_target(database, t, argv[2]); if( RET_WAS_ERROR(r) ) return r; RET_UPDATE(result, r); } return result; } struct lsversion { /*@null@*/struct lsversion *next; char *version; struct atomlist architectures; }; static retvalue newlsversion(struct lsversion **versions_p, /*@only@*/char *version, architecture_t architecture) { struct lsversion *v, **v_p; for( v_p = versions_p ; (v = *v_p) != NULL ; v_p = &v->next ) { if( strcmp(v->version, version) != 0 ) continue; free(version); return atomlist_add_uniq(&v->architectures, architecture); } v = calloc(1, sizeof(struct lsversion)); if( FAILEDTOALLOC(v) ) return RET_ERROR_OOM; *v_p = v; v->version = version; return atomlist_add(&v->architectures, architecture); } static retvalue ls_in_target(struct database *database, struct target *target, const char *packagename, struct lsversion **versions_p) { retvalue r,result; char *control,*version; r = target_initpackagesdb(target, database, READONLY); if( !RET_IS_OK(r) ) return r; result = table_getrecord(target->packages, packagename, &control); if( RET_IS_OK(result) ) { r = target->getversion(control, &version); if( RET_IS_OK(r) ) r = newlsversion(versions_p, version, target->architecture_atom); free(control); RET_UPDATE(result, r); } r = target_closepackagesdb(target); RET_ENDUPDATE(result, r); return result; } ACTION_B(y, n, y, ls) { retvalue r; struct distribution *d; struct target *t; size_t maxcodenamelen; assert( argc == 2 ); maxcodenamelen = 1; for( d = alldistributions ; d != NULL ; d = d->next ) { size_t l = strlen(d->codename); if( l > maxcodenamelen ) maxcodenamelen = l; } for( d = alldistributions ; d != NULL ; d = d->next ) { struct lsversion *versions = NULL, *v; size_t maxversionlen; int i; for( t = d->targets ; t != NULL ; t = t->next ) { if( !target_matches(t, components, architectures, packagetypes) ) continue; r = ls_in_target(database, t, argv[1], &versions); if( RET_WAS_ERROR(r) ) return r; } maxversionlen = 1; for( v = versions ; v != NULL ; v = v->next ) { size_t l = strlen(v->version); if( l > maxversionlen ) maxversionlen = l; } while( versions != NULL ) { architecture_t a; v = versions; versions = v->next; printf("%s | %*s | %*s | ", argv[1], (int)maxversionlen, v->version, (int)maxcodenamelen, d->codename); for( i = 0 ; i + 1 < v->architectures.count ; i++ ) { a = v->architectures.atoms[i]; printf("%s, ", atoms_architectures[a]); } a = v->architectures.atoms[i]; puts(atoms_architectures[a]); free(v->version); atomlist_done(&v->architectures); free(v); } } return RET_OK; } static retvalue listfilterprint(UNUSED(struct database *da), UNUSED(struct distribution *di), struct target *target, const char *packagename, const char *control, void *data) { term *condition = data; retvalue r; if( listmax == 0 ) return RET_NOTHING; r = term_decidechunktarget(condition, control, target); if( RET_IS_OK(r) ) { if( listskip <= 0 ) { if( listmax > 0 ) listmax--; r = listformat_print(listformat, target, packagename, control); } else { listskip--; r = RET_NOTHING; } } return r; } ACTION_B(y, n, y, listfilter) { retvalue r,result; struct distribution *distribution; term *condition; assert( argc == 3 ); r = distribution_get(alldistributions, argv[1], false, &distribution); assert( r != RET_NOTHING); if( RET_WAS_ERROR(r) ) { return r; } result = term_compilefortargetdecision(&condition, argv[2]); if( RET_WAS_ERROR(result) ) { return result; } result = distribution_foreach_package(distribution, database, components, architectures, packagetypes, listfilterprint, NULL, condition); term_free(condition); return result; } static retvalue listmatchprint(UNUSED(struct database *da), UNUSED(struct distribution *di), struct target *target, const char *packagename, const char *control, void *data) { const char *glob = data; if( listmax == 0 ) return RET_NOTHING; if( globmatch(packagename, glob) ) { if( listskip <= 0 ) { if( listmax > 0 ) listmax--; return listformat_print(listformat, target, packagename, control); } else { listskip--; return RET_NOTHING; } } else return RET_NOTHING; } ACTION_B(y, n, y, listmatched) { retvalue r,result; struct distribution *distribution; assert( argc == 3 ); r = distribution_get(alldistributions, argv[1], false, &distribution); assert( r != RET_NOTHING); if( RET_WAS_ERROR(r) ) { return r; } result = distribution_foreach_package(distribution, database, components, architectures, packagetypes, listmatchprint, NULL, (void*)argv[2]); return result; } ACTION_F(n, n, n, y, detect) { char buffer[5000],*nl; int i; retvalue r,ret; ret = RET_NOTHING; if( argc > 1 ) { for( i = 1 ; i < argc ; i++ ) { r = files_detect(database,argv[i]); RET_UPDATE(ret,r); } } else while( fgets(buffer,4999,stdin) != NULL ) { nl = strchr(buffer,'\n'); if( nl == NULL ) { return RET_ERROR; } *nl = '\0'; r = files_detect(database,buffer); RET_UPDATE(ret,r); } return ret; } ACTION_F(n, n, n, y, forget) { char buffer[5000],*nl; int i; retvalue r,ret; ret = RET_NOTHING; if( argc > 1 ) { for( i = 1 ; i < argc ; i++ ) { r = files_remove(database, argv[i]); RET_UPDATE(ret,r); } } else while( fgets(buffer,4999,stdin) != NULL ) { nl = strchr(buffer,'\n'); if( nl == NULL ) { return RET_ERROR; } *nl = '\0'; r = files_remove(database, buffer); RET_UPDATE(ret,r); } return ret; } ACTION_F(n, n, n, n, listmd5sums) { return files_printmd5sums(database); } ACTION_F(n, n, n, n, listchecksums) { return files_printchecksums(database); } ACTION_B(n, n, n, dumpcontents) { retvalue result,r; struct table *packages; const char *package, *chunk; struct cursor *cursor; assert( argc == 2 ); result = database_openpackages(database, argv[1], true, &packages); if( RET_WAS_ERROR(result) ) return result; r = table_newglobalcursor(packages, &cursor); assert( r != RET_NOTHING ); if( RET_WAS_ERROR(r) ) { (void)table_close(packages); return r; } result = RET_NOTHING; while( cursor_nexttemp(packages, cursor, &package, &chunk) ) { printf("'%s' -> '%s'\n", package, chunk); result = RET_OK; } r = cursor_close(packages, cursor); RET_ENDUPDATE(result,r); r = table_close(packages); RET_ENDUPDATE(result,r); return result; } ACTION_F(n, n, y, y, export) { retvalue result,r; struct distribution *d; if( export == EXPORT_NEVER ) { fprintf(stderr, "Error: reprepro export incompatible with --export=never\n"); return RET_ERROR; } result = distribution_match(alldistributions, argc-1, argv+1, true, READWRITE); assert( result != RET_NOTHING); if( RET_WAS_ERROR(result) ) return result; result = RET_NOTHING; for( d = alldistributions ; d != NULL ; d = d->next ) { if( !d->selected ) continue; if( verbose > 0 ) { printf("Exporting %s...\n",d->codename); } r = distribution_fullexport(d, database); RET_UPDATE(result,r); if( RET_WAS_ERROR(r) && export != EXPORT_FORCE) { return r; } } return result; } /***********************update********************************/ ACTION_D(n, n, y, update) { retvalue result,r; struct update_pattern *patterns; struct update_distribution *u_distributions; result = dirs_make_recursive(global.listdir); if( RET_WAS_ERROR(result) ) { return result; } result = distribution_match(alldistributions, argc-1, argv+1, true, READWRITE); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; result = updates_getpatterns(&patterns); if( RET_WAS_ERROR(result) ) return result; assert( RET_IS_OK(result) ); result = updates_calcindices(patterns, alldistributions, &u_distributions); if( !RET_IS_OK(result) ) { if( result == RET_NOTHING ) { if( argc == 1 ) fputs("Nothing to do, because no distribution has an Update: field.\n", stderr); else fputs("Nothing to do, because none of the selected distributions has an Update: field.\n", stderr); } updates_freepatterns(patterns); return result; } assert( RET_IS_OK(result) ); if( !RET_WAS_ERROR(result) ) result = updates_update(database, u_distributions, nolistsdownload, skipold, spacecheckmode, reserveddbspace, reservedotherspace); updates_freeupdatedistributions(u_distributions); updates_freepatterns(patterns); r = distribution_exportlist(export, alldistributions, database); RET_ENDUPDATE(result,r); return result; } ACTION_D(n, n, y, predelete) { retvalue result,r; struct update_pattern *patterns; struct update_distribution *u_distributions; result = dirs_make_recursive(global.listdir); if( RET_WAS_ERROR(result) ) { return result; } result = distribution_match(alldistributions, argc-1, argv+1, true, READWRITE); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; result = updates_getpatterns(&patterns); if( RET_WAS_ERROR(result) ) { return result; } assert( RET_IS_OK(result) ); result = updates_calcindices(patterns, alldistributions, &u_distributions); if( !RET_IS_OK(result) ) { if( result == RET_NOTHING ) { if( argc == 1 ) fputs("Nothing to do, because no distribution has an Update: field.\n", stderr); else fputs("Nothing to do, because none of the selected distributions has an Update: field.\n", stderr); } updates_freepatterns(patterns); return result; } assert( RET_IS_OK(result) ); if( !RET_WAS_ERROR(result) ) result = updates_predelete(database, u_distributions, nolistsdownload, skipold); updates_freeupdatedistributions(u_distributions); updates_freepatterns(patterns); r = distribution_exportlist(export, alldistributions, database); RET_ENDUPDATE(result, r); return result; } ACTION_B(n, n, y, checkupdate) { retvalue result; struct update_pattern *patterns; struct update_distribution *u_distributions; result = dirs_make_recursive(global.listdir); if( RET_WAS_ERROR(result) ) { return result; } result = distribution_match(alldistributions, argc-1, argv+1, false, READONLY); assert( result != RET_NOTHING); if( RET_WAS_ERROR(result) ) return result; result = updates_getpatterns(&patterns); if( RET_WAS_ERROR(result) ) { return result; } result = updates_calcindices(patterns, alldistributions, &u_distributions); if( !RET_IS_OK(result) ) { if( result == RET_NOTHING ) { if( argc == 1 ) fputs("Nothing to do, because no distribution has an Updates: field.\n", stderr); else fputs("Nothing to do, because none of the selected distributions has an Update: field.\n", stderr); } updates_freepatterns(patterns); return result; } result = updates_checkupdate(database, u_distributions, nolistsdownload, skipold); updates_freeupdatedistributions(u_distributions); updates_freepatterns(patterns); return result; } ACTION_B(n, n, y, dumpupdate) { retvalue result; struct update_pattern *patterns; struct update_distribution *u_distributions; result = dirs_make_recursive(global.listdir); if( RET_WAS_ERROR(result) ) { return result; } result = distribution_match(alldistributions, argc-1, argv+1, false, READONLY); assert( result != RET_NOTHING); if( RET_WAS_ERROR(result) ) return result; result = updates_getpatterns(&patterns); if( RET_WAS_ERROR(result) ) { return result; } result = updates_calcindices(patterns, alldistributions, &u_distributions); if( !RET_IS_OK(result) ) { if( result == RET_NOTHING ) { if( argc == 1 ) fputs("Nothing to do, because no distribution has an Updates: field.\n", stderr); else fputs("Nothing to do, because none of the selected distributions has an Update: field.\n", stderr); } updates_freepatterns(patterns); return result; } result = updates_dumpupdate(database, u_distributions, nolistsdownload, skipold); updates_freeupdatedistributions(u_distributions); updates_freepatterns(patterns); return result; } ACTION_L(n, n, n, n, cleanlists) { retvalue result; struct update_pattern *patterns; assert( argc == 1 ); if( !isdirectory(global.listdir) ) return RET_NOTHING; result = updates_getpatterns(&patterns); if( RET_WAS_ERROR(result) ) return result; result = updates_cleanlists(alldistributions, patterns); updates_freepatterns(patterns); return result; } /***********************migrate*******************************/ ACTION_D(n, n, y, pull) { retvalue result,r; struct pull_rule *rules; struct pull_distribution *p; result = distribution_match(alldistributions, argc-1, argv+1, true, READWRITE); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; result = pull_getrules(&rules); if( RET_WAS_ERROR(result) ) { return result; } assert( RET_IS_OK(result) ); result = pull_prepare(alldistributions, rules, fast, &p); if( RET_WAS_ERROR(result) ) { pull_freerules(rules); return result; } result = pull_update(database, p); pull_freerules(rules); pull_freedistributions(p); r = distribution_exportlist(export, alldistributions, database); RET_ENDUPDATE(result,r); return result; } ACTION_B(n, n, y, checkpull) { retvalue result; struct pull_rule *rules; struct pull_distribution *p; result = distribution_match(alldistributions, argc-1, argv+1, false, READONLY); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; result = pull_getrules(&rules); if( RET_WAS_ERROR(result) ) { return result; } assert( RET_IS_OK(result) ); result = pull_prepare(alldistributions, rules, fast, &p); if( RET_WAS_ERROR(result) ) { pull_freerules(rules); return result; } result = pull_checkupdate(database, p); pull_freerules(rules); pull_freedistributions(p); return result; } ACTION_B(n, n, y, dumppull) { retvalue result; struct pull_rule *rules; struct pull_distribution *p; result = distribution_match(alldistributions, argc-1, argv+1, false, READONLY); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; result = pull_getrules(&rules); if( RET_WAS_ERROR(result) ) { return result; } assert( RET_IS_OK(result) ); result = pull_prepare(alldistributions, rules, fast, &p); if( RET_WAS_ERROR(result) ) { pull_freerules(rules); return result; } result = pull_dumpupdate(database, p); pull_freerules(rules); pull_freedistributions(p); return result; } ACTION_D(y, n, y, copy) { struct distribution *destination, *source; retvalue result, r; result = distribution_get(alldistributions, argv[1], true, &destination); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; result = distribution_get(alldistributions, argv[2], false, &source); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; if( destination->readonly ) { fprintf(stderr, "Cannot copy packages to read-only distribution '%s'.\n", destination->codename); return RET_ERROR; } result = distribution_prepareforwriting(destination); if( RET_WAS_ERROR(result) ) return result; r = copy_by_name(database, destination, source, argc-3, argv+3, components, architectures, packagetypes); RET_ENDUPDATE(result,r); logger_wait(); r = distribution_export(export, destination, database); RET_ENDUPDATE(result,r); return result; } ACTION_D(y, n, y, copysrc) { struct distribution *destination, *source; retvalue result, r; result = distribution_get(alldistributions, argv[1], true, &destination); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; result = distribution_get(alldistributions, argv[2], false, &source); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; if( destination->readonly ) { fprintf(stderr, "Cannot copy packages to read-only distribution '%s'.\n", destination->codename); return RET_ERROR; } result = distribution_prepareforwriting(destination); if( RET_WAS_ERROR(result) ) return result; r = copy_by_source(database, destination, source, argc-3, argv+3, components, architectures, packagetypes); RET_ENDUPDATE(result,r); logger_wait(); r = distribution_export(export, destination, database); RET_ENDUPDATE(result,r); return result; } ACTION_D(y, n, y, copyfilter) { struct distribution *destination, *source; retvalue result, r; assert( argc == 4 ); result = distribution_get(alldistributions, argv[1], true, &destination); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; result = distribution_get(alldistributions, argv[2], false, &source); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; if( destination->readonly ) { fprintf(stderr, "Cannot copy packages to read-only distribution '%s'.\n", destination->codename); return RET_ERROR; } result = distribution_prepareforwriting(destination); if( RET_WAS_ERROR(result) ) return result; r = copy_by_formula(database, destination, source, argv[3], components, architectures, packagetypes); RET_ENDUPDATE(result,r); logger_wait(); r = distribution_export(export, destination, database); RET_ENDUPDATE(result,r); return result; } ACTION_D(y, n, y, copymatched) { struct distribution *destination, *source; retvalue result, r; assert( argc == 4 ); result = distribution_get(alldistributions, argv[1], true, &destination); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; result = distribution_get(alldistributions, argv[2], false, &source); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; if( destination->readonly ) { fprintf(stderr, "Cannot copy packages to read-only distribution '%s'.\n", destination->codename); return RET_ERROR; } result = distribution_prepareforwriting(destination); if( RET_WAS_ERROR(result) ) return result; r = copy_by_glob(database, destination, source, argv[3], components, architectures, packagetypes); RET_ENDUPDATE(result,r); logger_wait(); r = distribution_export(export, destination, database); RET_ENDUPDATE(result,r); return result; } ACTION_D(y, n, y, restore) { struct distribution *destination; retvalue result, r; result = distribution_get(alldistributions, argv[1], true, &destination); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; if( destination->readonly ) { fprintf(stderr, "Cannot copy packages to read-only distribution '%s'.\n", destination->codename); return RET_ERROR; } result = distribution_prepareforwriting(destination); if( RET_WAS_ERROR(result) ) return result; r = restore_by_name(database, destination, components, architectures, packagetypes, argv[2], argc-3, argv+3); RET_ENDUPDATE(result,r); logger_wait(); r = distribution_export(export, destination, database); RET_ENDUPDATE(result,r); return result; } ACTION_D(y, n, y, restoresrc) { struct distribution *destination; retvalue result, r; result = distribution_get(alldistributions, argv[1], true, &destination); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; if( destination->readonly ) { fprintf(stderr, "Cannot copy packages to read-only distribution '%s'.\n", destination->codename); return RET_ERROR; } result = distribution_prepareforwriting(destination); if( RET_WAS_ERROR(result) ) return result; r = restore_by_source(database, destination, components, architectures, packagetypes, argv[2], argc-3, argv+3); RET_ENDUPDATE(result,r); logger_wait(); r = distribution_export(export, destination, database); RET_ENDUPDATE(result,r); return result; } ACTION_D(y, n, y, restorematched) { struct distribution *destination; retvalue result, r; assert( argc == 4 ); result = distribution_get(alldistributions, argv[1], true, &destination); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; if( destination->readonly ) { fprintf(stderr, "Cannot copy packages to read-only distribution '%s'.\n", destination->codename); return RET_ERROR; } result = distribution_prepareforwriting(destination); if( RET_WAS_ERROR(result) ) return result; r = restore_by_glob(database, destination, components, architectures, packagetypes, argv[2], argv[3]); RET_ENDUPDATE(result,r); logger_wait(); r = distribution_export(export, destination, database); RET_ENDUPDATE(result,r); return result; } ACTION_D(y, n, y, restorefilter) { struct distribution *destination; retvalue result, r; assert( argc == 4 ); result = distribution_get(alldistributions, argv[1], true, &destination); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; if( destination->readonly ) { fprintf(stderr, "Cannot copy packages to read-only distribution '%s'.\n", destination->codename); return RET_ERROR; } result = distribution_prepareforwriting(destination); if( RET_WAS_ERROR(result) ) return result; r = restore_by_formula(database, destination, components, architectures, packagetypes, argv[2], argv[3]); RET_ENDUPDATE(result,r); logger_wait(); r = distribution_export(export, destination, database); RET_ENDUPDATE(result,r); return result; } ACTION_D(y, n, y, addpackage) { struct distribution *destination; retvalue result, r; architecture_t architecture = atom_unknown; component_t component = atom_unknown; packagetype_t packagetype = atom_unknown; if( packagetypes != NULL ) { if( packagetypes->count > 1 ) { fprintf(stderr, "_addpackage can only cope with one packagetype at a time!\n"); return RET_ERROR; } packagetype = packagetypes->atoms[0]; } if( architectures != NULL ) { if( architectures->count > 1 ) { fprintf(stderr, "_addpackage can only cope with one architecture at a time!\n"); return RET_ERROR; } architecture = architectures->atoms[0]; } if( components != NULL ) { if( components->count > 1 ) { fprintf(stderr, "_addpackage can only cope with one component at a time!\n"); return RET_ERROR; } component = components->atoms[0]; } if( !atom_defined(packagetype) && atom_defined(architecture) && architecture == architecture_source ) packagetype = pt_dsc; if( atom_defined(packagetype) && !atom_defined(architecture) && packagetype == pt_dsc ) architecture = architecture_source; // TODO: some more guesses based on components and udebcomponents if( !atom_defined(architecture) || !atom_defined(component) || !atom_defined(packagetype) ) { fprintf(stderr, "_addpackage needs -C and -A and -T set!\n"); return RET_ERROR; } result = distribution_get(alldistributions, argv[1], true, &destination); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; if( destination->readonly ) { fprintf(stderr, "Cannot add packages to read-only distribution '%s'.\n", destination->codename); return RET_ERROR; } result = distribution_prepareforwriting(destination); if( RET_WAS_ERROR(result) ) return result; result = copy_from_file(database, destination, component, architecture, packagetype, argv[2], argc-3, argv+3); logger_wait(); r = distribution_export(export, destination, database); RET_ENDUPDATE(result,r); return result; } /***********************rereferencing*************************/ ACTION_R(n, n, y, y, rereference) { retvalue result, r; struct distribution *d; struct target *t; result = distribution_match(alldistributions, argc-1, argv+1, true, READONLY); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) { return result; } result = RET_NOTHING; for( d = alldistributions ; d != NULL ; d = d->next ) { if( !d->selected ) continue; if( verbose > 0 ) { printf("Referencing %s...\n",d->codename); } for( t = d->targets ; t != NULL ; t = t->next ) { r = target_rereference(t, database); RET_UPDATE(result, r); } r = tracking_rereference(database, d); RET_UPDATE(result, r); if( RET_WAS_ERROR(r) ) break; } return result; } /***************************retrack****************************/ ACTION_D(n, n, y, retrack) { retvalue result,r; struct distribution *d; result = distribution_match(alldistributions, argc-1, argv+1, true, READONLY); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) { return result; } result = RET_NOTHING; for( d = alldistributions ; d != NULL ; d = d->next ) { if( !d->selected ) continue; if( d->tracking == dt_NONE ) { if( argc > 1 ) { fprintf(stderr, "Cannot retrack %s: Tracking not activated for this distribution!\n", d->codename); RET_UPDATE(result, RET_ERROR); } continue; } r = tracking_retrack(database, d, true); RET_ENDUPDATE(result,r); if( RET_WAS_ERROR(result) ) break; } return result; } ACTION_D(n, n, y, removetrack) { retvalue result,r; struct distribution *distribution; trackingdb tracks; assert( argc == 4 ); result = distribution_get(alldistributions, argv[1], true, &distribution); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; r = tracking_initialize(&tracks, database, distribution, false); if( RET_WAS_ERROR(r) ) { return r; } result = tracking_remove(tracks, argv[2], argv[3], database); r = tracking_done(tracks); RET_ENDUPDATE(result,r); return result; } ACTION_D(n, n, y, removealltracks) { retvalue result, r; struct distribution *d; const char *codename; int i; if( delete <= 0 ) for( i = 1 ; i < argc ; i ++ ) { codename = argv[i]; d = alldistributions; while( d != NULL && strcmp(codename, d->codename) != 0 ) d = d->next; if( d != NULL && d->tracking != dt_NONE ) { fprintf(stderr, "Error: Requested removing of all tracks of distribution '%s',\n" "which still has tracking enabled. Use --delete to delete anyway.\n", codename); return RET_ERROR; } } result = RET_NOTHING; for( i = 1 ; i < argc ; i ++ ) { codename = argv[i]; if( verbose >= 0 ) { printf("Deleting all tracks for %s...\n", codename); } r = tracking_drop(database, codename); RET_UPDATE(result,r); if( RET_WAS_ERROR(result) ) break; if( r == RET_NOTHING ) { d = alldistributions; while( d != NULL && strcmp(codename, d->codename) != 0 ) d = d->next; if( d == NULL ) { fprintf(stderr, "Warning: There was no tracking information to delete for '%s',\n" "which is also not found in conf/distributions. Either this was already\n" "deleted earlier, or you might have mistyped.\n", codename); } } } return result; } ACTION_D(n, n, y, tidytracks) { retvalue result,r; struct distribution *d; result = distribution_match(alldistributions, argc-1, argv+1, true, READONLY); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) { return result; } result = RET_NOTHING; for( d = alldistributions ; d != NULL ; d = d->next ) { trackingdb tracks; if( !d->selected ) continue; if( d->tracking == dt_NONE ) { r = tracking_drop(database, d->codename); RET_UPDATE(result,r); if( RET_WAS_ERROR(r) ) break; continue; } if( verbose >= 0 ) { printf("Looking for old tracks in %s...\n",d->codename); } r = tracking_initialize(&tracks, database, d, false); if( RET_WAS_ERROR(r) ) { RET_UPDATE(result,r); if( RET_WAS_ERROR(r) ) break; continue; } r = tracking_tidyall(tracks, database); RET_UPDATE(result,r); r = tracking_done(tracks); RET_ENDUPDATE(result,r); if( RET_WAS_ERROR(result) ) break; } return result; } ACTION_B(n, n, y, dumptracks) { retvalue result,r; struct distribution *d; result = distribution_match(alldistributions, argc-1, argv+1, false, READONLY); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) { return result; } result = RET_NOTHING; for( d = alldistributions ; d != NULL ; d = d->next ) { trackingdb tracks; if( !d->selected ) continue; r = tracking_initialize(&tracks, database, d, true); if( RET_WAS_ERROR(r) ) { RET_UPDATE(result,r); if( RET_WAS_ERROR(r) ) break; continue; } if( r == RET_NOTHING ) continue; r = tracking_printall(tracks); RET_UPDATE(result,r); r = tracking_done(tracks); RET_ENDUPDATE(result,r); if( RET_WAS_ERROR(result) ) break; } return result; } /***********************checking*************************/ ACTION_RF(y, n, y, check) { retvalue result,r; struct distribution *d; result = distribution_match(alldistributions, argc-1, argv+1, false, READONLY); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) { return result; } result = RET_NOTHING; for( d = alldistributions ; d != NULL ; d = d->next ) { if( !d->selected ) continue; if( verbose > 0 ) { printf("Checking %s...\n",d->codename); } r = distribution_foreach_package(d, database, components, architectures, packagetypes, package_check, NULL, NULL); RET_UPDATE(result,r); if( RET_WAS_ERROR(r) ) break; } return result; } ACTION_F(n, n, n, y, checkpool) { if( argc == 2 && strcmp(argv[1],"fast") != 0 ) { fprintf(stderr,"Error: Unrecognized second argument '%s'\n" "Syntax: reprepro checkpool [fast]\n", argv[1]); return RET_ERROR; } return files_checkpool(database, argc == 2); } /* Update checksums of existing files */ ACTION_F(n, n, n, n, collectnewchecksums) { return files_collectnewchecksums(database); } /*****************reapplying override info***************/ ACTION_F(y, n, y, y, reoverride) { retvalue result,r; struct distribution *d; result = distribution_match(alldistributions, argc-1, argv+1, true, READWRITE); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) { return result; } result = RET_NOTHING; for( d = alldistributions ; d != NULL ; d = d->next ) { if( !d->selected ) continue; if( verbose > 0 ) { fprintf(stderr,"Reapplying override to %s...\n",d->codename); } r = distribution_loadalloverrides(d); if( RET_IS_OK(r) ) { struct target *t; for( t = d->targets ; t != NULL ; t = t->next ) { if( !target_matches(t, components, architectures, packagetypes) ) continue; r = target_reoverride(t, d, database); RET_UPDATE(result, r); // TODO: how to seperate this in those affecting d // and those that do not? RET_UPDATE(d->status, r); } distribution_unloadoverrides(d); } else if( r == RET_NOTHING ) { fprintf(stderr,"No override files, thus nothing to do for %s.\n",d->codename); } else { RET_UPDATE(result, r); } if( RET_WAS_ERROR(result) ) break; } r = distribution_exportlist(export, alldistributions, database); RET_ENDUPDATE(result,r); return result; } /***********************include******************************************/ ACTION_D(y, y, y, includedeb) { retvalue result,r; struct distribution *distribution; bool isudeb; trackingdb tracks; int i = 0; component_t component = atom_unknown; if( components != NULL ) { if( components->count > 1 ) { fprintf(stderr, "Error: Only one component is allowed with %s!\n",argv[0]); return RET_ERROR; } assert(components->count > 0 ); component = components->atoms[0]; } if( architectures != NULL ) if( !atomlist_hasexcept(architectures, architecture_source) ) { fprintf(stderr, "Error: -A source is not possible with includedeb!\n"); return RET_ERROR; } if( strcmp(argv[0],"includeudeb") == 0 ) { isudeb = true; if( limitations_missed(packagetypes, pt_udeb) ) { fprintf(stderr, "Calling includeudeb with a -T not containing udeb makes no sense!\n"); return RET_ERROR; } } else if( strcmp(argv[0],"includedeb") == 0 ) { isudeb = false; if( limitations_missed(packagetypes, pt_deb) ) { fprintf(stderr, "Calling includedeb with a -T not containing deb makes no sense!\n"); return RET_ERROR; } } else { fprintf(stderr,"Internal error while parding command!\n"); return RET_ERROR; } for( i = 2 ; i < argc ; i++ ) { const char *filename = argv[i]; if( isudeb ) { if( !endswith(filename,".udeb") && !IGNORING_(extension, "includeudeb called with file '%s' not ending with '.udeb'\n", filename) ) return RET_ERROR; } else { if( !endswith(filename,".deb") && !IGNORING_(extension, "includedeb called with file '%s' not ending with '.deb'\n", filename) ) return RET_ERROR; } } result = distribution_get(alldistributions, argv[1], true, &distribution); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) { return result; } if( distribution->readonly ) { fprintf(stderr, "Cannot add packages to read-only distribution '%s'.\n", distribution->codename); return RET_ERROR; } if( isudeb ) result = override_read( distribution->udeb_override, &distribution->overrides.udeb); else result = override_read( distribution->deb_override, &distribution->overrides.deb); if( RET_WAS_ERROR(result) ) { return result; } // TODO: same for component? (depending on type?) if( architectures != NULL ) { architecture_t missing = atom_unknown; if( !atomlist_subset(&distribution->architectures, architectures, &missing) ){ fprintf(stderr, "Cannot force into the architecture '%s' not available in '%s'!\n", atoms_architectures[missing], distribution->codename); return RET_ERROR; } } r = distribution_prepareforwriting(distribution); if( RET_WAS_ERROR(r) ) { return RET_ERROR; } if( distribution->tracking != dt_NONE ) { result = tracking_initialize(&tracks, database, distribution, false); if( RET_WAS_ERROR(result) ) { return result; } } else { tracks = NULL; } result = RET_NOTHING; for( i = 2 ; i < argc ; i++ ) { const char *filename = argv[i]; r = deb_add(database, component, architectures, section, priority, isudeb?pt_udeb:pt_deb, distribution, filename, delete, tracks); RET_UPDATE(result, r); } distribution_unloadoverrides(distribution); r = tracking_done(tracks); RET_ENDUPDATE(result,r); logger_wait(); r = distribution_export(export, distribution, database); RET_ENDUPDATE(result,r); return result; } ACTION_D(y, y, y, includedsc) { retvalue result,r; struct distribution *distribution; trackingdb tracks; component_t component = atom_unknown; if( components != NULL ) { if( components->count > 1 ) { fprintf(stderr, "Error: Only one component is allowed with %s!\n",argv[0]); return RET_ERROR; } assert(components->count > 0 ); component = components->atoms[0]; } assert( argc == 3 ); if( limitations_missed(architectures, architecture_source) ) { fprintf(stderr, "Cannot put a source package anywhere else than in architecture 'source'!\n"); return RET_ERROR; } if( limitations_missed(packagetypes, pt_dsc) ) { fprintf(stderr, "Cannot put a source package anywhere else than in type 'dsc'!\n"); return RET_ERROR; } if( !endswith(argv[2],".dsc") && !IGNORING_(extension, "includedsc called with a file not ending with '.dsc'\n") ) return RET_ERROR; result = distribution_get(alldistributions, argv[1], true, &distribution); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; if( distribution->readonly ) { fprintf(stderr, "Cannot add packages to read-only distribution '%s'.\n", distribution->codename); return RET_ERROR; } result = override_read(distribution->dsc_override, &distribution->overrides.dsc); if( RET_WAS_ERROR(result) ) { return result; } result = distribution_prepareforwriting(distribution); if( RET_WAS_ERROR(result) ) { return result; } if( distribution->tracking != dt_NONE ) { result = tracking_initialize(&tracks, database, distribution, false); if( RET_WAS_ERROR(result) ) { return result; } } else { tracks = NULL; } result = dsc_add(database, component, section, priority, distribution, argv[2], delete, tracks); logger_wait(); distribution_unloadoverrides(distribution); r = tracking_done(tracks); RET_ENDUPDATE(result,r); r = distribution_export(export, distribution, database); RET_ENDUPDATE(result,r); return result; } ACTION_D(y, y, y, include) { retvalue result,r; struct distribution *distribution; trackingdb tracks; component_t component = atom_unknown; if( components != NULL ) { if( components->count > 1 ) { fprintf(stderr, "Error: Only one component is allowed with %s!\n", argv[0]); return RET_ERROR; } assert(components->count > 0 ); component = components->atoms[0]; } assert( argc == 3 ); if( !endswith(argv[2],".changes") && !IGNORING_(extension, "include called with a file not ending with '.changes'\n" "(Did you mean includedeb or includedsc?)\n") ) return RET_ERROR; result = distribution_get(alldistributions, argv[1], true, &distribution); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; if( distribution->readonly ) { fprintf(stderr, "Cannot add packages to read-only distribution '%s'.\n", distribution->codename); return RET_ERROR; } result = distribution_loadalloverrides(distribution); if( RET_WAS_ERROR(result) ) { return result; } if( distribution->tracking != dt_NONE ) { result = tracking_initialize(&tracks, database, distribution, false); if( RET_WAS_ERROR(result) ) { return result; } } else { tracks = NULL; } result = distribution_loaduploaders(distribution); if( RET_WAS_ERROR(result) ) { r = tracking_done(tracks); RET_ENDUPDATE(result,r); return result; } result = changes_add(database, tracks, packagetypes, component, architectures, section, priority, distribution, argv[2], delete); if( RET_WAS_ERROR(result) ) RET_UPDATE(distribution->status, result); distribution_unloadoverrides(distribution); distribution_unloaduploaders(distribution); r = tracking_done(tracks); RET_ENDUPDATE(result,r); r = distribution_export(export, distribution, database); RET_ENDUPDATE(result,r); return result; } /***********************createsymlinks***********************************/ static bool mayaliasas(const struct distribution *alldistributions, const char *part, const char *cnpart) { const struct distribution *d; /* here it is only checked whether there is something that could * cause this link to exist. No tests whether this really will * cause it to be created (or already existing). */ for( d = alldistributions ; d != NULL ; d = d->next ) { if( d->suite == NULL ) continue; if( strcmp(d->suite, part) == 0 && strcmp(d->codename, cnpart) == 0) return true; if( strcmp(d->codename, part) == 0 && strcmp(d->suite, cnpart) == 0) return true; } return false; } ACTION_C(n, n, createsymlinks) { retvalue result,r; struct distribution *d,*d2; bool warned_slash = false; r = dirs_make_recursive(global.distdir); if( RET_WAS_ERROR(r) ) return r; result = distribution_match(alldistributions, argc-1, argv+1, false, READONLY); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) { return result; } result = RET_NOTHING; for( d = alldistributions ; d != NULL ; d = d->next ) { char *linkname,*buffer; size_t bufsize; int ret; const char *separator_in_suite; if( !d->selected ) continue; if( d->suite == NULL || strcmp(d->suite, d->codename) == 0 ) continue; r = RET_NOTHING; for( d2 = alldistributions ; d2 != NULL ; d2 = d2->next ) { if( !d2->selected ) continue; if( d!=d2 && d2->suite!=NULL &&strcmp(d->suite,d2->suite)==0) { fprintf(stderr, "Not linking %s->%s due to conflict with %s->%s\n", d->suite,d->codename, d2->suite,d2->codename); r = RET_ERROR; } else if( strcmp(d->suite,d2->codename)==0) { fprintf(stderr, "Not linking %s->%s due to conflict with %s\n", d->suite,d->codename,d2->codename); r = RET_ERROR; } } if( RET_WAS_ERROR(r) ) { RET_UPDATE(result,r); continue; } separator_in_suite = strchr(d->suite, '/'); if( separator_in_suite != NULL ) { /* things with / in it are tricky: * relative symbolic links are hard, * perhaps something else already moved * the earlier ones, ... */ const char *separator_in_codename; size_t ofs_in_suite = separator_in_suite - d->suite; char *part = strndup(d->suite, ofs_in_suite); if( FAILEDTOALLOC(part) ) return RET_ERROR_OOM; /* check if this is some case we do not want to warn about: */ separator_in_codename = strchr(d->codename, '/'); if( separator_in_codename != NULL && strcmp(separator_in_codename, separator_in_suite) == 0 ) { /* all but the first is common: */ size_t cnofs = separator_in_codename - d->codename; char *cnpart = strndup(d->codename, cnofs); if( FAILEDTOALLOC(cnpart) ) { free(part); return RET_ERROR_OOM; } if( mayaliasas(alldistributions, part, cnpart) ) { if( verbose > 1 ) fprintf(stderr, "Not creating '%s' -> '%s' because of the '/' in it.\n" "Hopefully something else will link '%s' -> '%s' then this is not needed.\n", d->suite, d->codename, part, cnpart); free(part); free(cnpart); continue; } free(cnpart); } free(part); if( verbose >= 0 && !warned_slash ) { fprintf(stderr, "Creating symlinks with '/' in them is not yet supported:\n"); warned_slash = true; } if( verbose >= 0 ) fprintf(stderr, "Not creating '%s' -> '%s' because of '/'.\n", d->suite, d->codename); continue; } linkname = calc_dirconcat(global.distdir, d->suite); bufsize = strlen(d->codename)+10; buffer = calloc(1,bufsize); if( linkname == NULL || buffer == NULL ) { free(linkname);free(buffer); (void)fputs("Out of Memory!\n",stderr); return RET_ERROR_OOM; } ret = readlink(linkname,buffer,bufsize-4); if( ret < 0 && errno == ENOENT ) { ret = symlink(d->codename,linkname); if( ret != 0 ) { int e = errno; r = RET_ERRNO(e); fprintf(stderr, "Error %d creating symlink %s->%s: %s\n", e, linkname, d->codename, strerror(e)); RET_UPDATE(result,r); } else { if( verbose > 0 ) { printf("Created %s->%s\n",linkname,d->codename); } RET_UPDATE(result,RET_OK); } } else if( ret >= 0 ) { buffer[ret] = '\0'; if( ret >= ((int)bufsize)-4 ) { buffer[bufsize-4]='.'; buffer[bufsize-3]='.'; buffer[bufsize-2]='.'; buffer[bufsize-1]='\0'; } if( strcmp(buffer,d->codename) == 0 ) { if( verbose > 2 ) { printf("Already ok: %s->%s\n",linkname,d->codename); } RET_UPDATE(result,RET_OK); } else { if( delete <= 0 ) { fprintf(stderr,"Cannot create %s as already pointing to %s instead of %s,\n use --delete to delete the old link before creating an new one.\n",linkname,buffer,d->codename); RET_UPDATE(result,RET_ERROR); } else { unlink(linkname); ret = symlink(d->codename,linkname); if( ret != 0 ) { int e = errno; r = RET_ERRNO(e); fprintf(stderr, "Error %d creating symlink %s->%s: %s\n", e, linkname, d->codename, strerror(e)); RET_UPDATE(result,r); } else { if( verbose > 0 ) { printf("Replaced %s->%s\n",linkname,d->codename); } RET_UPDATE(result,RET_OK); } } } } else { int e = errno; r = RET_ERRNO(e); fprintf(stderr, "Error %d checking %s, perhaps not a symlink?: %s\n", e, linkname, strerror(e)); RET_UPDATE(result,r); } free(linkname);free(buffer); RET_UPDATE(result,r); } return result; } /***********************clearvanished***********************************/ ACTION_D(n, n, n, clearvanished) { retvalue result,r; struct distribution *d; struct strlist identifiers, codenames; bool *inuse; int i; result = database_listpackages(database, &identifiers); if( !RET_IS_OK(result) ) { return result; } inuse = calloc(identifiers.count, sizeof(bool)); if( inuse == NULL ) { strlist_done(&identifiers); return RET_ERROR_OOM; } for( d = alldistributions; d != NULL ; d = d->next ) { struct target *t; for( t = d->targets; t != NULL ; t = t->next ) { int ofs = strlist_ofs(&identifiers, t->identifier); if( ofs >= 0 ) { inuse[ofs] = true; if( verbose > 6 ) printf( "Marking '%s' as used.\n", t->identifier); } else if( verbose > 3 && database->capabilities.createnewtables){ fprintf(stderr, "Strange, '%s' does not appear in packages.db yet.\n", t->identifier); } } } for( i = 0 ; i < identifiers.count ; i ++ ) { const char *identifier = identifiers.values[i]; const char *p, *q; if( inuse[i] ) continue; if( interrupted() ) return RET_ERROR_INTERRUPTED; if( delete <= 0 ) { struct table *packages; r = database_openpackages(database, identifier, true, &packages); if( RET_IS_OK(r) ) { if( !table_isempty(packages) ) { fprintf(stderr, "There are still packages in '%s', not removing (give --delete to do so)!\n", identifier); (void)table_close(packages); continue; } r = table_close(packages); } } if( interrupted() ) return RET_ERROR_INTERRUPTED; // TODO: if delete, check what is removed, so that tracking // information can be updated. printf( "Deleting vanished identifier '%s'.\n", identifier); /* intern component and architectures, so parsing * has no problems (actually only need component now) */ p = identifier; if( strncmp(p, "u|", 2) == 0 ) p += 2; p = strchr(p, '|'); if( p != NULL ) { p++; q = strchr(p, '|'); if( q != NULL ) { atom_t dummy; char *component = strndup(p, q-p); q++; char *architecture = strdup(q); if( FAILEDTOALLOC(component) || FAILEDTOALLOC(architecture) ) { free(component); free(architecture); return RET_ERROR_OOM; } r = architecture_intern(architecture, &dummy); free(architecture); if( RET_WAS_ERROR(r) ) { free(component); return r; } r = component_intern(component, &dummy); free(component); if( RET_WAS_ERROR(r) ) return r; } } /* derference anything left */ references_remove(database, identifier); /* remove the database */ database_droppackages(database, identifier); } free(inuse); strlist_done(&identifiers); if( interrupted() ) return RET_ERROR_INTERRUPTED; r = tracking_listdistributions(database, &codenames); RET_UPDATE(result, r); if( RET_IS_OK(r) ) { for( d = alldistributions; d != NULL ; d = d->next ) { strlist_remove(&codenames, d->codename); } for( i = 0 ; i < codenames.count ; i ++ ) { printf("Deleting tracking data for vanished distribution '%s'.\n", codenames.values[i]); r = tracking_drop(database, codenames.values[i]); RET_UPDATE(result, r); } strlist_done(&codenames); } return result; } ACTION_B(n, n, y, listdbidentifiers) { retvalue result; struct strlist identifiers; const struct distribution *d; int i; result = database_listpackages(database, &identifiers); if( !RET_IS_OK(result) ) { return result; } result = distribution_match(alldistributions, argc-1, argv+1, false, READONLY); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; result = RET_NOTHING; for( i = 0 ; i < identifiers.count ; i++ ) { const char *p, *q, *identifier = identifiers.values[i]; if( argc <= 1 ) { puts(identifier); result = RET_OK; continue; } p = identifier; if( strncmp(p, "u|", 2) == 0 ) p += 2; q = strchr(p, '|'); if( q == NULL ) q = strchr(p, '\0'); for( d = alldistributions ; d != NULL ; d = d->next ) { if( !d->selected ) continue; if( strncmp(p, d->codename, q-p) == 0 && d->codename[q-p] == '\0' ) { puts(identifier); result = RET_OK; break; } } } strlist_done(&identifiers); return result; } ACTION_C(n, n, listconfidentifiers) { struct target *t; const struct distribution *d; retvalue result; result = distribution_match(alldistributions, argc-1, argv+1, false, READONLY); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; result = RET_NOTHING; for( d = alldistributions ; d != NULL ; d = d->next ) { if( !d->selected ) continue; for( t = d->targets; t != NULL ; t = t->next ) { puts(t->identifier); result = RET_OK; } } return result; } ACTION_N(n, n, y, versioncompare) { retvalue r; int i; assert( argc == 3 ); r = properversion(argv[1]); if( RET_WAS_ERROR(r) ) fprintf(stderr, "'%s' is not a proper version!\n", argv[1]); r = properversion(argv[2]); if( RET_WAS_ERROR(r) ) fprintf(stderr, "'%s' is not a proper version!\n", argv[2]); r = dpkgversions_cmp(argv[1],argv[2],&i); if( RET_IS_OK(r) ) { if( i < 0 ) { printf("'%s' is smaller than '%s'.\n", argv[1], argv[2]); } else if( i > 0 ) { printf("'%s' is larger than '%s'.\n", argv[1], argv[2]); } else printf("'%s' is the same as '%s'.\n", argv[1], argv[2]); } return r; } /***********************processincoming********************************/ ACTION_D(n, n, y, processincoming) { retvalue result,r; struct distribution *d; for( d = alldistributions ; d != NULL ; d = d->next ) d->selected = true; result = process_incoming(database, alldistributions, argv[1], (argc==3)?argv[2]:NULL); logger_wait(); r = distribution_exportlist(export, alldistributions, database); RET_ENDUPDATE(result,r); return result; } /***********************gensnapshot********************************/ ACTION_R(n, n, y, y, gensnapshot) { retvalue result; struct distribution *distribution; assert( argc == 3 ); result = distribution_get(alldistributions, argv[1], true, &distribution); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; return distribution_snapshot(distribution, database, argv[2]); } /***********************rerunnotifiers********************************/ static retvalue rerunnotifiersintarget(UNUSED(struct database *da), struct distribution *d, struct target *target, UNUSED(void *dummy)) { if( !logger_rerun_needs_target(d->logger, target) ) return RET_NOTHING; return RET_OK; } ACTION_B(y, n, y, rerunnotifiers) { retvalue result,r; struct distribution *d; result = distribution_match(alldistributions, argc-1, argv+1, false, READONLY); assert( result != RET_NOTHING); if( RET_WAS_ERROR(result) ) return result; result = RET_NOTHING; for( d = alldistributions ; d != NULL ; d = d->next ) { if( !d->selected ) continue; if( d->logger == NULL ) continue; if( verbose > 0 ) { printf("Processing %s...\n",d->codename); } r = logger_prepare(d->logger); RET_UPDATE(result,r); if( RET_WAS_ERROR(r) ) break; r = distribution_foreach_package(d, database, components, architectures, packagetypes, package_rerunnotifiers, rerunnotifiersintarget, NULL); logger_wait(); RET_UPDATE(result,r); if( RET_WAS_ERROR(r) ) break; } return result; } /*********************** flood ****************************/ ACTION_D(y, n, y, flood) { retvalue result,r; struct distribution *distribution; trackingdb tracks; component_t architecture = atom_unknown; result = distribution_get(alldistributions, argv[1], true, &distribution); assert( result != RET_NOTHING ); if( RET_WAS_ERROR(result) ) return result; if( distribution->readonly ) { fprintf(stderr, "Cannot add packages to read-only distribution '%s'.\n", distribution->codename); return RET_ERROR; } if( argc == 3 ) { architecture = architecture_find(argv[2]); if( !atom_defined(architecture) ) { fprintf(stderr, "Error: Unknown architecture '%s'!\n", argv[2]); return RET_ERROR; } if( architecture == architecture_source ) { fprintf(stderr, "Error: Architecture 'source' does not make sense with 'flood'!\n"); return RET_ERROR; } if( !atomlist_in(&distribution->architectures, architecture) ) { fprintf(stderr, "Error: Architecture '%s' not part of '%s'!\n", argv[2], distribution->codename); return RET_ERROR; } } result = distribution_prepareforwriting(distribution); if( RET_WAS_ERROR(result) ) { return result; } if( distribution->tracking != dt_NONE ) { result = tracking_initialize(&tracks, database, distribution, false); if( RET_WAS_ERROR(result) ) { return result; } } else tracks = NULL; result = flood(distribution, components, architectures, packagetypes, architecture, database, tracks); logger_wait(); if( RET_WAS_ERROR(result) ) RET_UPDATE(distribution->status, result); if( tracks != NULL ) { r = tracking_done(tracks); RET_ENDUPDATE(result, r); } r = distribution_export(export, distribution, database); RET_ENDUPDATE(result, r); return result; } /*********************/ /* argument handling */ /*********************/ // TODO: this has become an utter mess and needs some serious cleaning... #define NEED_REFERENCES 1 /* FILESDB now includes REFERENCED... */ #define NEED_FILESDB 2 #define NEED_DEREF 4 #define NEED_DATABASE 8 #define NEED_CONFIG 16 #define NEED_NO_PACKAGES 32 #define IS_RO 64 #define MAY_UNUSED 128 #define NEED_ACT 256 #define NEED_SP 512 #define NEED_DELNEW 1024 #define A_N(w) action_n_n_n_ ## w, 0 #define A_C(w) action_c_n_n_ ## w, NEED_CONFIG #define A_ROB(w) action_b_n_n_ ## w, NEED_DATABASE|IS_RO #define A_ROBact(w) action_b_y_n_ ## w, NEED_ACT|NEED_DATABASE|IS_RO #define A_L(w) action_l_n_n_ ## w, NEED_DATABASE #define A_B(w) action_b_n_n_ ## w, NEED_DATABASE #define A_Bact(w) action_b_y_n_ ## w, NEED_ACT|NEED_DATABASE #define A_F(w) action_f_n_n_ ## w, NEED_DATABASE|NEED_FILESDB #define A_Fact(w) action_f_y_n_ ## w, NEED_ACT|NEED_DATABASE|NEED_FILESDB #define A_R(w) action_r_n_n_ ## w, NEED_DATABASE|NEED_REFERENCES #define A__F(w) action_f_n_n_ ## w, NEED_DATABASE|NEED_FILESDB|NEED_NO_PACKAGES #define A__R(w) action_r_n_n_ ## w, NEED_DATABASE|NEED_REFERENCES|NEED_NO_PACKAGES #define A__T(w) action_t_n_n_ ## w, NEED_DATABASE|NEED_NO_PACKAGES|MAY_UNUSED #define A_RF(w) action_rf_n_n_ ## w, NEED_DATABASE|NEED_FILESDB|NEED_REFERENCES #define A_RFact(w) action_rf_y_n_ ## w, NEED_ACT|NEED_DATABASE|NEED_FILESDB|NEED_REFERENCES /* to dereference files, one needs files and references database: */ #define A_D(w) action_d_n_n_ ## w, NEED_DATABASE|NEED_FILESDB|NEED_REFERENCES|NEED_DEREF #define A_Dact(w) action_d_y_n_ ## w, NEED_ACT|NEED_DATABASE|NEED_FILESDB|NEED_REFERENCES|NEED_DEREF #define A_Dactsp(w) action_d_y_y_ ## w, NEED_ACT|NEED_SP|NEED_DATABASE|NEED_FILESDB|NEED_REFERENCES|NEED_DEREF static const struct action { const char *name; retvalue (*start)( /*@null@*/struct distribution *, /*@null@*/struct database*, /*@null@*/const char *priority, /*@null@*/const char *section, /*@null@*/const struct atomlist *, /*@null@*/const struct atomlist *, /*@null@*/const struct atomlist *, int argc,const char *argv[]); int needs; int minargs, maxargs; const char *wrongargmessage; } all_actions[] = { {"__d", A_N(printargs), -1, -1, NULL}, {"__dumpuncompressors", A_N(dumpuncompressors), 0, 0, "__dumpuncompressors"}, {"__uncompress", A_N(uncompress), 3, 3, "__uncompress .gz|.bz2|.lzma|.xz "}, {"__extractsourcesection", A_N(extractsourcesection), 1, 1, "__extractsourcesection <.dsc-file>"}, {"__extractcontrol", A_N(extractcontrol), 1, 1, "__extractcontrol <.deb-file>"}, {"__extractfilelist", A_N(extractfilelist), 1, 1, "__extractfilelist <.deb-file>"}, {"_versioncompare", A_N(versioncompare), 2, 2, "_versioncompare "}, {"_detect", A__F(detect), -1, -1, NULL}, {"_forget", A__F(forget), -1, -1, NULL}, {"_listmd5sums", A__F(listmd5sums), 0, 0, "_listmd5sums"}, {"_listchecksums", A__F(listchecksums), 0, 0, "_listchecksums"}, {"_addchecksums", A__F(addmd5sums), 0, 0, "_addchecksums < data"}, {"_addmd5sums", A__F(addmd5sums), 0, 0, "_addmd5sums < data"}, {"_dumpcontents", A_ROB(dumpcontents)|MAY_UNUSED, 1, 1, "_dumpcontents "}, {"_removereferences", A__R(removereferences), 1, 1, "_removereferences "}, {"_addreference", A__R(addreference), 2, 2, "_addreference "}, {"_fakeemptyfilelist", A__F(fakeemptyfilelist), 1, 1, "_fakeemptyfilelist "}, {"_addpackage", A_Dact(addpackage), 3, -1, "-C -A -T _addpackage "}, {"remove", A_Dact(remove), 2, -1, "[-C ] [-A ] [-T ] remove "}, {"removesrc", A_D(removesrc), 2, 3, "removesrc []"}, {"ls", A_ROBact(ls), 1, 1, "[-C ] [-A ] [-T ] ls "}, {"list", A_ROBact(list), 1, 2, "[-C ] [-A ] [-T ] list []"}, {"listfilter", A_ROBact(listfilter), 2, 2, "[-C ] [-A ] [-T ] listfilter "}, {"removefilter", A_Dact(removefilter), 2, 2, "[-C ] [-A ] [-T ] removefilter "}, {"listmatched", A_ROBact(listmatched), 2, 2, "[-C ] [-A ] [-T ] listmatched "}, {"removematched", A_Dact(removematched), 2, 2, "[-C ] [-A ] [-T ] removematched "}, {"createsymlinks", A_C(createsymlinks), 0, -1, "createsymlinks []"}, {"export", A_F(export), 0, -1, "export []"}, {"check", A_RFact(check), 0, -1, "check []"}, {"reoverride", A_Fact(reoverride), 0, -1, "[-T ...] [-C ...] [-A ...] reoverride []"}, {"collectnewchecksums", A_F(collectnewchecksums), 0, 0, "collectnewchecksums"}, {"checkpool", A_F(checkpool), 0, 1, "checkpool [fast]"}, {"rereference", A_R(rereference), 0, -1, "rereference []"}, {"dumpreferences", A_R(dumpreferences)|MAY_UNUSED, 0, 0, "dumpreferences", }, {"dumpunreferenced", A_RF(dumpunreferenced), 0, 0, "dumpunreferenced", }, {"deleteunreferenced", A_RF(deleteunreferenced), 0, 0, "deleteunreferenced", }, {"retrack", A_D(retrack), 0, -1, "retrack []"}, {"dumptracks", A_ROB(dumptracks)|MAY_UNUSED, 0, -1, "dumptracks []"}, {"removealltracks", A_D(removealltracks)|MAY_UNUSED, 1, -1, "removealltracks "}, {"tidytracks", A_D(tidytracks), 0, -1, "tidytracks []"}, {"removetrack", A_D(removetrack), 3, 3, "removetrack "}, {"update", A_D(update), 0, -1, "update []"}, {"checkupdate", A_B(checkupdate), 0, -1, "checkupdate []"}, {"dumpupdate", A_B(dumpupdate), 0, -1, "dumpupdate []"}, {"predelete", A_D(predelete), 0, -1, "predelete []"}, {"pull", A_D(pull), 0, -1, "pull []"}, {"copy", A_Dact(copy), 3, -1, "[-C ] [-A ] [-T ] copy "}, {"copysrc", A_Dact(copysrc), 3, -1, "[-C ] [-A ] [-T ] copysrc []"}, {"copymatched", A_Dact(copymatched), 3, 3, "[-C ] [-A ] [-T ] copymatched "}, {"copyfilter", A_Dact(copyfilter), 3, 3, "[-C ] [-A ] [-T ] copyfilter "}, {"restore", A_Dact(restore), 3, -1, "[-C ] [-A ] [-T ] restore "}, {"restoresrc", A_Dact(restoresrc), 3, -1, "[-C ] [-A ] [-T ] restoresrc []"}, {"restorematched", A_Dact(restorematched), 3, 3, "[-C ] [-A ] [-T ] restorematched "}, {"restorefilter", A_Dact(restorefilter), 3, 3, "[-C ] [-A ] [-T ] restorefilter "}, {"dumppull", A_B(dumppull), 0, -1, "dumppull []"}, {"checkpull", A_B(checkpull), 0, -1, "checkpull []"}, {"includedeb", A_Dactsp(includedeb)|NEED_DELNEW, 2, -1, "[--delete] includedeb <.deb-file>"}, {"includeudeb", A_Dactsp(includedeb)|NEED_DELNEW, 2, -1, "[--delete] includeudeb <.udeb-file>"}, {"includedsc", A_Dactsp(includedsc)|NEED_DELNEW, 2, 2, "[--delete] includedsc "}, {"include", A_Dactsp(include)|NEED_DELNEW, 2, 2, "[--delete] include <.changes-file>"}, {"generatefilelists", A_F(generatefilelists), 0, 1, "generatefilelists [reread]"}, {"translatefilelists", A__T(translatefilelists), 0, 0, "translatefilelists"}, {"translatelegacychecksums", A_N(translatelegacychecksums), 0, 0, "translatelegacychecksums"}, {"_listconfidentifiers", A_C(listconfidentifiers), 0, -1, "_listconfidentifiers"}, {"_listdbidentifiers", A_ROB(listdbidentifiers)|MAY_UNUSED, 0, -1, "_listdbidentifiers"}, {"clearvanished", A_D(clearvanished)|MAY_UNUSED, 0, 0, "[--delete] clearvanished"}, {"processincoming", A_D(processincoming)|NEED_DELNEW, 1, 2, "processincoming [<.changes file>]"}, {"gensnapshot", A_R(gensnapshot), 2, 2, "gensnapshot "}, {"rerunnotifiers", A_Bact(rerunnotifiers), 0, -1, "rerunnotifiers []"}, {"cleanlists", A_L(cleanlists), 0, 0, "cleanlists"}, {"build-needing", A_ROBact(buildneeded), 2, 3, "[-C ] build-needing []"}, {"flood", A_Dact(flood)|MAY_UNUSED, 1, 2, "[-C ] [-A ] [-T ] flood []"}, {NULL,NULL,0,0,0,NULL} }; #undef A_N #undef A_B #undef A_ROB #undef A_C #undef A_F #undef A_R #undef A_RF #undef A_F #undef A__T static retvalue callaction(command_t command, const struct action *action, int argc, const char *argv[]) { retvalue result, r; struct database *database; struct distribution *alldistributions = NULL; bool deletederef, deletenew; int needs; struct atomlist as, *architectures; struct atomlist cs, *components; struct atomlist ps, *packagetypes; assert(action != NULL); causingcommand_atom = command; if( action->minargs >= 0 && argc < 1 + action->minargs ) { fprintf(stderr, "Error: Too few arguments for command '%s'!\nSyntax: reprepro %s\n", argv[0], action->wrongargmessage); return RET_ERROR; } if( action->maxargs >= 0 && argc > 1 + action->maxargs ) { fprintf(stderr, "Error: Too many arguments for command '%s'!\nSyntax: reprepro %s\n", argv[0], action->wrongargmessage); return RET_ERROR; } needs = action->needs; if( !ISSET(needs, NEED_ACT) && ( x_architecture != NULL ) ) { if( !IGNORING_(unusedoption, "Action '%s' cannot be restricted to an architecture!\n" "neither --archiecture nor -A make sense here.\n", action->name) ) return RET_ERROR; } if( !ISSET(needs, NEED_ACT) && ( x_component != NULL ) ) { if( !IGNORING_(unusedoption, "Action '%s' cannot be restricted to a component!\n" "neither --component nor -C make sense here.\n", action->name) ) return RET_ERROR; } if( !ISSET(needs, NEED_ACT) && ( x_packagetype != NULL ) ) { if( !IGNORING_(unusedoption, "Action '%s' cannot be restricted to a packagetype!\n" "neither --packagetype nor -T make sense here.\n", action->name) ) return RET_ERROR; } if( !ISSET(needs, NEED_SP) && ( x_section != NULL ) ) { if( !IGNORING_(unusedoption, "Action '%s' cannot take a section option!\n" "neither --section nor -S make sense here.\n", action->name) ) return RET_ERROR; } if( !ISSET(needs, NEED_SP) && ( x_priority != NULL ) ) { if( !IGNORING_(unusedoption, "Action '%s' cannot take a priority option!\n" "neither --priority nor -P make sense here.\n", action->name) ) return RET_ERROR; } if( ISSET(needs, NEED_DATABASE)) needs |= NEED_CONFIG; if( ISSET(needs, NEED_CONFIG) ) { r = distribution_readall(&alldistributions); if( RET_WAS_ERROR(r) ) return r; } if( !ISSET(needs, NEED_DATABASE) ) { assert( (needs & ~NEED_CONFIG) == 0); result = action->start(alldistributions, NULL, x_section, x_priority, atom_unknown, atom_unknown, atom_unknown, argc, argv); r = distribution_freelist(alldistributions); RET_ENDUPDATE(result,r); return result; } if( ISSET(needs, NEED_ACT) ) { const char *unknownitem; if( x_architecture != NULL ) { r = atomlist_filllist(at_architecture, &as, x_architecture, &unknownitem); if( r == RET_NOTHING ) { fprintf(stderr, "Error: Architecture '%s' as given to --architecture is not know.\n" "(it does not appear as architecture in %s/distributions (did you mistype?))\n", unknownitem, global.confdir); r = RET_ERROR; } if( RET_WAS_ERROR(r) ) { (void)distribution_freelist(alldistributions); return r; } architectures = &as; } else { atomlist_init(&as); architectures = NULL; } if( x_component != NULL ) { r = atomlist_filllist(at_component, &cs, x_component, &unknownitem); if( r == RET_NOTHING ) { fprintf(stderr, "Error: Component '%s' as given to --component is not know.\n" "(it does not appear as component in %s/distributions (did you mistype?))\n", unknownitem, global.confdir); r = RET_ERROR; } if( RET_WAS_ERROR(r) ) { (void)distribution_freelist(alldistributions); return r; } components = &cs; } else { atomlist_init(&cs); components = NULL; } if( x_packagetype != NULL ) { r = atomlist_filllist(at_packagetype, &ps, x_packagetype, &unknownitem); if( r == RET_NOTHING ) { fprintf(stderr, "Error: Packagetype '%s' as given to --packagetype is not know.\n" "(only dsc, deb, udeb and combinations of those are allowed)\n", unknownitem); r = RET_ERROR; } if( RET_WAS_ERROR(r) ) { (void)distribution_freelist(alldistributions); return r; } packagetypes = &ps; } else { atomlist_init(&ps); packagetypes = NULL; } if( ps.count == 1 && ps.atoms[0] == pt_dsc && limitations_missed(architectures, architecture_source) ) { fprintf(stderr, "Error: -T dsc is not possible with -A not including source!\n"); return RET_ERROR; } if( as.count == 1 && as.atoms[0] == architecture_source && limitations_missed(packagetypes, pt_dsc) ) { fprintf(stderr, "Error: -A source is not possible with -T not including dsc!\n"); return RET_ERROR; } } deletederef = ISSET(needs,NEED_DEREF) && !keepunreferenced; deletenew = ISSET(needs,NEED_DELNEW) && !keepunusednew; result = database_create(&database, alldistributions, fast, ISSET(needs, NEED_NO_PACKAGES), ISSET(needs, MAY_UNUSED), ISSET(needs, IS_RO), waitforlock, verbosedatabase || (verbose >= 30)); if( !RET_IS_OK(result) ) { (void)distribution_freelist(alldistributions); return result; } /* adding files may check references to see if they were added */ if( ISSET(needs,NEED_FILESDB) ) needs |= NEED_REFERENCES; if( ISSET(needs,NEED_REFERENCES) ) result = database_openreferences(database); assert( result != RET_NOTHING ); if( RET_IS_OK(result) ) { if( ISSET(needs,NEED_FILESDB) ) result = database_openfiles(database); assert( result != RET_NOTHING ); if( RET_IS_OK(result) ) { if( deletederef ) { assert( ISSET(needs,NEED_REFERENCES) ); } if( !interrupted() ) { result = action->start(alldistributions, database, x_section, x_priority, architectures, components, packagetypes, argc, argv); /* wait for package specific loggers */ logger_wait(); /* remove files added but not used */ pool_tidyadded(database, deletenew); // TODO: tell hook script the added files // TODO: tell hook scripts the modified distributions /* delete files losing references, or * tell how many lost their references */ // TODO: instead check if any distribution that // was not exported lost files // (and in a far future do not remove references // before the index is written) if( deletederef && RET_WAS_ERROR(result) ) { deletederef = false; if( pool_havedereferenced ) { fprintf(stderr, "Not deleting possibly left over files due to previous errors.\n" "(To keep the files in the still existing index files from vanishing)\n" "Use dumpunreferenced/deleteunreferenced to show/delete files without references.\n"); } } r = pool_removeunreferenced(database, deletederef); RET_ENDUPDATE(result, r); // TODO: tell hook scripts the deleted files } } } if( !interrupted() ) { logger_wait(); } if( ISSET(needs, NEED_ACT) ) { atomlist_done(&as); atomlist_done(&cs); atomlist_done(&ps); } logger_warn_waiting(); r = database_close(database); RET_ENDUPDATE(result, r); r = distribution_freelist(alldistributions); RET_ENDUPDATE(result,r); database = NULL; return result; } enum { LO_DELETE=1, LO_KEEPUNREFERENCED, LO_KEEPUNUSEDNEW, LO_KEEPUNNEEDEDLISTS, LO_NOTHINGISERROR, LO_NOLISTDOWNLOAD, LO_ASKPASSPHRASE, LO_KEEPDIRECTORIES, LO_KEEPTEMPORARIES, LO_FAST, LO_SKIPOLD, LO_GUESSGPGTTY, LO_NODELETE, LO_NOKEEPUNREFERENCED, LO_NOKEEPUNUSEDNEW, LO_NOKEEPUNNEEDEDLISTS, LO_NONOTHINGISERROR, LO_LISTDOWNLOAD, LO_NOASKPASSPHRASE, LO_NOKEEPDIRECTORIES, LO_NOKEEPTEMPORARIES, LO_NOFAST, LO_NOSKIPOLD, LO_NOGUESSGPGTTY, LO_VERBOSEDB, LO_NOVERBOSEDB, LO_EXPORT, LO_OUTDIR, LO_DISTDIR, LO_DBDIR, LO_LOGDIR, LO_LISTDIR, LO_OVERRIDEDIR, LO_CONFDIR, LO_METHODDIR, LO_VERSION, LO_WAITFORLOCK, LO_SPACECHECK, LO_SAFETYMARGIN, LO_DBSAFETYMARGIN, LO_GUNZIP, LO_BUNZIP2, LO_UNLZMA, LO_UNXZ, LO_GNUPGHOME, LO_LISTFORMAT, LO_LISTSKIP, LO_LISTMAX, LO_MORGUEDIR, LO_SHOWPERCENT, LO_UNIGNORE}; static int longoption = 0; const char *programname; static void setexport(const char *argument) { if( strcasecmp(argument, "never") == 0 ) { CONFIGSET(export, EXPORT_NEVER); return; } if( strcasecmp(argument, "changed") == 0 ) { CONFIGSET(export, EXPORT_CHANGED); return; } if( strcasecmp(argument, "normal") == 0 ) { CONFIGSET(export, EXPORT_NORMAL); return; } if( strcasecmp(argument, "lookedat") == 0 ) { CONFIGSET(export, EXPORT_NORMAL); return; } if( strcasecmp(argument, "force") == 0 ) { CONFIGSET(export, EXPORT_FORCE); return; } fprintf(stderr, "Error: --export needs an argument of 'never', 'normal' or 'force', but got '%s'\n", argument); exit(EXIT_FAILURE); } static unsigned long long parse_number(const char *name, const char *argument, long long max) { long long l; char *p; l = strtoll(argument, &p, 10); if( p==NULL || *p != '\0' || l < 0 ) { fprintf(stderr, "Invalid argument to %s: '%s'\n", name, argument); exit(EXIT_FAILURE); } if( l == LLONG_MAX || l > max ) { fprintf(stderr, "Too large argument for to %s: '%s'\n", name, argument); exit(EXIT_FAILURE); } return l; } static void handle_option(int c, const char *argument) { retvalue r; int i; switch( c ) { case 'h': printf( "reprepro - Produce and Manage a Debian package repository\n\n" "options:\n" " -h, --help: Show this help\n" " -i --ignore : Ignore errors of type .\n" " --keepunreferencedfiles: Do not delete files no longer needed.\n" " --delete: Delete included files if reasonable.\n" " -b, --basedir : Base directory\n" " --outdir : Set pool and dists base directory\n" " --distdir : Override dists directory.\n" " --dbdir : Directory to place the database in.\n" " --listdir : Directory to place downloaded lists in.\n" " --confdir : Directory to search configuration in.\n" " --logdir : Directory to put requeted log files in.\n" " --methodir : Use instead of /usr/lib/apt/methods/\n" " -S, --section
: Force include* to set section.\n" " -P, --priority : Force include* to set priority.\n" " -C, --component : Add,list or delete only in component.\n" " -A, --architecture : Add,list or delete only to architecture.\n" " -T, --type : Add,list or delete only type (dsc,deb,udeb).\n" "\n" "actions (selection, for more see manpage):\n" " dumpreferences: Print all saved references\n" " dumpunreferenced: Print registered files without reference\n" " deleteunreferenced: Delete and forget all unreferenced files\n" " checkpool: Check if all files in the pool are still in proper shape.\n" " check []\n" " Check for all needed files to be registered properly.\n" " export []\n" " Force (re)generation of Packages.gz/Packages/Sources.gz/Release\n" " update []\n" " Update the given distributions from the configured sources.\n" " remove \n" " Remove the given package from the specified distribution.\n" " include <.changes-file>\n" " Include the given upload.\n" " includedeb <.deb-file>\n" " Include the given binary package.\n" " includeudeb <.udeb-file>\n" " Include the given installer binary package.\n" " includedsc <.dsc-file>\n" " Include the given source package.\n" " list \n" " List all packages by the given name occurring in the given distribution.\n" " listfilter \n" " List all packages in the given distribution matching the condition.\n" " clearvanished\n" " Remove everything no longer referenced in the distributions config file.\n" "\n"); exit(EXIT_SUCCESS); case '\0': switch( longoption ) { case LO_UNIGNORE: r = set_ignore(argument, false, config_state); if( RET_WAS_ERROR(r) ) { exit(EXIT_FAILURE); } break; case LO_SHOWPERCENT: global.showdownloadpercent++; break; case LO_DELETE: delete++; break; case LO_NODELETE: delete--; break; case LO_KEEPUNREFERENCED: CONFIGSET(keepunreferenced, true); break; case LO_NOKEEPUNREFERENCED: CONFIGSET(keepunreferenced, false); break; case LO_KEEPUNUSEDNEW: CONFIGSET(keepunusednew, true); break; case LO_NOKEEPUNUSEDNEW: CONFIGSET(keepunusednew, false); break; case LO_KEEPUNNEEDEDLISTS: /* this is the only option now and ignored * for compatibility reasond */ break; case LO_NOKEEPUNNEEDEDLISTS: fprintf(stderr, "Warning: --nokeepuneededlists no longer exists.\n" "Use cleanlists to clean manually.\n"); break; case LO_KEEPTEMPORARIES: CONFIGGSET(keeptemporaries, true); break; case LO_NOKEEPTEMPORARIES: CONFIGGSET(keeptemporaries, false); break; case LO_KEEPDIRECTORIES: CONFIGGSET(keepdirectories, true); break; case LO_NOKEEPDIRECTORIES: CONFIGGSET(keepdirectories, false); break; case LO_NOTHINGISERROR: CONFIGSET(nothingiserror, true); break; case LO_NONOTHINGISERROR: CONFIGSET(nothingiserror, false); break; case LO_NOLISTDOWNLOAD: CONFIGSET(nolistsdownload, true); break; case LO_LISTDOWNLOAD: CONFIGSET(nolistsdownload, false); break; case LO_ASKPASSPHRASE: CONFIGSET(askforpassphrase, true); break; case LO_NOASKPASSPHRASE: CONFIGSET(askforpassphrase, false); break; case LO_GUESSGPGTTY: CONFIGSET(guessgpgtty, true); break; case LO_NOGUESSGPGTTY: CONFIGSET(guessgpgtty, false); break; case LO_SKIPOLD: CONFIGSET(skipold, true); break; case LO_NOSKIPOLD: CONFIGSET(skipold, false); break; case LO_FAST: CONFIGSET(fast, true); break; case LO_NOFAST: CONFIGSET(fast, false); break; case LO_VERBOSEDB: CONFIGSET(verbosedatabase, true); break; case LO_NOVERBOSEDB: CONFIGSET(verbosedatabase, false); break; case LO_EXPORT: setexport(argument); break; case LO_OUTDIR: CONFIGDUP(x_outdir, argument); break; case LO_DISTDIR: CONFIGDUP(x_distdir, argument); break; case LO_DBDIR: CONFIGDUP(x_dbdir, argument); break; case LO_LISTDIR: CONFIGDUP(x_listdir, argument); break; case LO_CONFDIR: CONFIGDUP(x_confdir, argument); break; case LO_LOGDIR: CONFIGDUP(x_logdir, argument); break; case LO_METHODDIR: CONFIGDUP(x_methoddir, argument); break; case LO_MORGUEDIR: CONFIGDUP(x_morguedir, argument); break; case LO_VERSION: fprintf(stderr,"%s: This is " PACKAGE " version " VERSION "\n",programname); exit(EXIT_SUCCESS); case LO_WAITFORLOCK: CONFIGSET(waitforlock, parse_number( "--waitforlock", argument, LONG_MAX)); break; case LO_SPACECHECK: if( strcasecmp(argument, "none") == 0 ) { CONFIGSET(spacecheckmode, scm_NONE); } else if( strcasecmp(argument, "full") == 0 ) { CONFIGSET(spacecheckmode, scm_FULL); } else { fprintf(stderr, "Unknown --spacecheck argument: '%s'!\n", argument); exit(EXIT_FAILURE); } break; case LO_SAFETYMARGIN: CONFIGSET(reservedotherspace, parse_number( "--safetymargin", argument, LONG_MAX)); break; case LO_DBSAFETYMARGIN: CONFIGSET(reserveddbspace, parse_number( "--dbsafetymargin", argument, LONG_MAX)); break; case LO_GUNZIP: CONFIGDUP(gunzip, argument); break; case LO_BUNZIP2: CONFIGDUP(bunzip2, argument); break; case LO_UNLZMA: CONFIGDUP(unlzma, argument); break; case LO_UNXZ: CONFIGDUP(unxz, argument); break; case LO_GNUPGHOME: CONFIGDUP(gnupghome, argument); break; case LO_LISTMAX: i = parse_number("--list-max", argument, INT_MAX); if( i == 0 ) i = -1; CONFIGSET(listmax, i); break; case LO_LISTSKIP: i = parse_number("--list-skip", argument, INT_MAX); CONFIGSET(listskip, i); break; case LO_LISTFORMAT: if( strcmp(argument, "NONE") == 0 ) { CONFIGSET(listformat, NULL); } else CONFIGDUP(listformat, argument); break; default: fprintf (stderr,"Error parsing arguments!\n"); exit(EXIT_FAILURE); } longoption = 0; break; case 's': verbose--; break; case 'v': verbose++; break; case 'V': verbose+=5; break; case 'f': fprintf(stderr, "Ignoring no longer existing option -f/--force!\n"); break; case 'b': CONFIGDUP(x_basedir, argument); break; case 'i': r = set_ignore(argument, true, config_state); if( RET_WAS_ERROR(r) ) { exit(EXIT_FAILURE); } break; case 'C': if( x_component != NULL && strcmp(x_component, argument) != 0) { fprintf(stderr,"Multiple '-C' are not supported!\n"); exit(EXIT_FAILURE); } CONFIGDUP(x_component, argument); break; case 'A': if( x_architecture != NULL && strcmp(x_architecture, argument) != 0) { fprintf(stderr,"Multiple '-A's are not supported!\n"); exit(EXIT_FAILURE); } CONFIGDUP(x_architecture, argument); break; case 'T': if( x_packagetype != NULL && strcmp(x_packagetype, argument) != 0) { fprintf(stderr,"Multiple '-T's are not supported!\n"); exit(EXIT_FAILURE); } CONFIGDUP(x_packagetype, argument); break; case 'S': if( x_section != NULL && strcmp(x_section, argument) != 0) { fprintf(stderr,"Multiple '-S' are not supported!\n"); exit(EXIT_FAILURE); } CONFIGDUP(x_section, argument); break; case 'P': if( x_priority != NULL && strcmp(x_priority, argument) != 0) { fprintf(stderr,"Multiple '-P's are mpt supported!\n"); exit(EXIT_FAILURE); } CONFIGDUP(x_priority, argument); break; case '?': /* getopt_long should have already given an error msg */ exit(EXIT_FAILURE); default: fprintf (stderr,"Not supported option '-%c'\n", c); exit(EXIT_FAILURE); } } static volatile bool was_interrupted = false; static bool interruption_printed = false; bool interrupted(void) { if( was_interrupted ) { if( !interruption_printed ) { interruption_printed = true; fprintf(stderr, "\n\nInterruption in progress, interrupt again to force-stop it (and risking database corruption!)\n\n"); } return true; } else return false; } static void interrupt_signaled(int) /*__attribute__((signal))*/; static void interrupt_signaled(UNUSED(int s)) { was_interrupted = true; } static void myexit(int) __attribute__((__noreturn__)); static void myexit(int status) { free(x_dbdir); free(x_distdir); free(x_listdir); free(x_logdir); free(x_confdir); free(x_basedir); free(x_outdir); free(x_methoddir); free(x_component); free(x_architecture); free(x_packagetype); free(x_section); free(x_priority); free(x_morguedir); free(gnupghome); exit(status); } static void disallow_plus_prefix(const char *dir, const char *name, const char *allowed) { if( dir[0] != '+' ) return; if( dir[1] == '\0' || dir[2] != '/' ) { fprintf(stderr, "Error: %s starts with +, but does not continue with '+b/'.\n", name); myexit(EXIT_FAILURE); } if( strchr(allowed, dir[1]) != NULL ) return; fprintf(stderr, "Error: %s is not allowed to start with '+%c/'.\n" "(if your directory is named like that, set it to './+%c/')\n", name, dir[1], dir[1]); myexit(EXIT_FAILURE); } static char *expand_plus_prefix(/*@only@*/char *dir, const char *name, const char *allowed, bool freedir) { const char *fromdir; char *newdir; disallow_plus_prefix(dir, name, allowed); if( dir[0] == '/' || (dir[0] == '.' && dir[1] == '/') ) return dir; if( dir[0] != '+' ) { fprintf(stderr, "Warning: %s '%s' does not start with '/', './', or '+'.\n" "This currently means it is relative to the current working directory,\n" "but that might change in the future or cause an error instead!\n", name, dir); return dir; } if( dir[1] == 'b' ) { fromdir = x_basedir; } else if( dir[1] == 'o' ) { fromdir = x_outdir; } else if( dir[1] == 'c' ) { fromdir = x_confdir; } else { abort(); return dir; } if( dir[3] == '\0' ) newdir = strdup(fromdir); else newdir = calc_dirconcat(fromdir, dir + 3); if( FAILEDTOALLOC(newdir) ) { (void)fputs("Out of Memory!\n",stderr); exit(EXIT_FAILURE); } if( freedir ) free(dir); return newdir; } int main(int argc,char *argv[]) { static struct option longopts[] = { {"delete", no_argument, &longoption,LO_DELETE}, {"nodelete", no_argument, &longoption,LO_NODELETE}, {"basedir", required_argument, NULL, 'b'}, {"ignore", required_argument, NULL, 'i'}, {"unignore", required_argument, &longoption, LO_UNIGNORE}, {"noignore", required_argument, &longoption, LO_UNIGNORE}, {"methoddir", required_argument, &longoption, LO_METHODDIR}, {"outdir", required_argument, &longoption, LO_OUTDIR}, {"distdir", required_argument, &longoption, LO_DISTDIR}, {"dbdir", required_argument, &longoption, LO_DBDIR}, {"listdir", required_argument, &longoption, LO_LISTDIR}, {"confdir", required_argument, &longoption, LO_CONFDIR}, {"logdir", required_argument, &longoption, LO_LOGDIR}, {"section", required_argument, NULL, 'S'}, {"priority", required_argument, NULL, 'P'}, {"component", required_argument, NULL, 'C'}, {"architecture", required_argument, NULL, 'A'}, {"type", required_argument, NULL, 'T'}, {"help", no_argument, NULL, 'h'}, {"verbose", no_argument, NULL, 'v'}, {"silent", no_argument, NULL, 's'}, {"version", no_argument, &longoption, LO_VERSION}, {"nothingiserror", no_argument, &longoption, LO_NOTHINGISERROR}, {"nolistsdownload", no_argument, &longoption, LO_NOLISTDOWNLOAD}, {"keepunreferencedfiles", no_argument, &longoption, LO_KEEPUNREFERENCED}, {"keepunusednewfiles", no_argument, &longoption, LO_KEEPUNUSEDNEW}, {"keepunneededlists", no_argument, &longoption, LO_KEEPUNNEEDEDLISTS}, {"keepdirectories", no_argument, &longoption, LO_KEEPDIRECTORIES}, {"keeptemporaries", no_argument, &longoption, LO_KEEPTEMPORARIES}, {"ask-passphrase", no_argument, &longoption, LO_ASKPASSPHRASE}, {"nonothingiserror", no_argument, &longoption, LO_NONOTHINGISERROR}, {"nonolistsdownload", no_argument, &longoption, LO_LISTDOWNLOAD}, {"listsdownload", no_argument, &longoption, LO_LISTDOWNLOAD}, {"nokeepunreferencedfiles", no_argument, &longoption, LO_NOKEEPUNREFERENCED}, {"nokeepunusednewfiles", no_argument, &longoption, LO_NOKEEPUNUSEDNEW}, {"nokeepunneededlists", no_argument, &longoption, LO_NOKEEPUNNEEDEDLISTS}, {"nokeepdirectories", no_argument, &longoption, LO_NOKEEPDIRECTORIES}, {"nokeeptemporaries", no_argument, &longoption, LO_NOKEEPTEMPORARIES}, {"noask-passphrase", no_argument, &longoption, LO_NOASKPASSPHRASE}, {"guessgpgtty", no_argument, &longoption, LO_GUESSGPGTTY}, {"noguessgpgtty", no_argument, &longoption, LO_NOGUESSGPGTTY}, {"nonoguessgpgtty", no_argument, &longoption, LO_GUESSGPGTTY}, {"fast", no_argument, &longoption, LO_FAST}, {"nofast", no_argument, &longoption, LO_NOFAST}, {"verbosedb", no_argument, &longoption, LO_VERBOSEDB}, {"noverbosedb", no_argument, &longoption, LO_NOVERBOSEDB}, {"verbosedatabase", no_argument, &longoption, LO_VERBOSEDB}, {"noverbosedatabase", no_argument, &longoption, LO_NOVERBOSEDB}, {"skipold", no_argument, &longoption, LO_SKIPOLD}, {"noskipold", no_argument, &longoption, LO_NOSKIPOLD}, {"nonoskipold", no_argument, &longoption, LO_SKIPOLD}, {"force", no_argument, NULL, 'f'}, {"export", required_argument, &longoption, LO_EXPORT}, {"waitforlock", required_argument, &longoption, LO_WAITFORLOCK}, {"checkspace", required_argument, &longoption, LO_SPACECHECK}, {"spacecheck", required_argument, &longoption, LO_SPACECHECK}, {"safetymargin", required_argument, &longoption, LO_SAFETYMARGIN}, {"dbsafetymargin", required_argument, &longoption, LO_DBSAFETYMARGIN}, {"gunzip", required_argument, &longoption, LO_GUNZIP}, {"bunzip2", required_argument, &longoption, LO_BUNZIP2}, {"unlzma", required_argument, &longoption, LO_UNLZMA}, {"unxz", required_argument, &longoption, LO_UNXZ}, {"gnupghome", required_argument, &longoption, LO_GNUPGHOME}, {"list-format", required_argument, &longoption, LO_LISTFORMAT}, {"list-skip", required_argument, &longoption, LO_LISTSKIP}, {"list-max", required_argument, &longoption, LO_LISTMAX}, {"morguedir", required_argument, &longoption, LO_MORGUEDIR}, {"show-percent", no_argument, &longoption, LO_SHOWPERCENT}, {NULL, 0, NULL, 0} }; const struct action *a; retvalue r; int c; struct sigaction sa; char *tempconfdir; sigemptyset(&sa.sa_mask); #if defined(SA_ONESHOT) sa.sa_flags = SA_ONESHOT; #elif defined(SA_RESETHAND) sa.sa_flags = SA_RESETHAND; #elif !defined(SPLINT) # error "missing argument to sigaction!" #endif sa.sa_handler = interrupt_signaled; (void)sigaction(SIGTERM, &sa, NULL); (void)sigaction(SIGABRT, &sa, NULL); (void)sigaction(SIGINT, &sa, NULL); (void)sigaction(SIGQUIT, &sa, NULL); (void)signal(SIGPIPE, SIG_IGN); programname = argv[0]; init_ignores(); config_state = CONFIG_OWNER_DEFAULT; CONFIGDUP(x_basedir, STD_BASE_DIR); CONFIGDUP(x_confdir, "+b/conf"); CONFIGDUP(x_methoddir, STD_METHOD_DIR); CONFIGDUP(x_outdir, "+b/"); CONFIGDUP(x_distdir, "+o/dists"); CONFIGDUP(x_dbdir, "+b/db"); CONFIGDUP(x_logdir, "+b/logs"); CONFIGDUP(x_listdir, "+b/lists"); config_state = CONFIG_OWNER_CMDLINE; if( interrupted() ) exit(EXIT_RET(RET_ERROR_INTERRUPTED)); while( (c = getopt_long(argc,argv,"+fVvshb:P:i:A:C:S:T:",longopts,NULL)) != -1 ) { handle_option(c,optarg); } if( optind >= argc ) { fprintf(stderr,"No action given. (see --help for available options and actions)\n"); exit(EXIT_FAILURE); } if( interrupted() ) exit(EXIT_RET(RET_ERROR_INTERRUPTED)); /* only for this CONFIG_OWNER_ENVIRONMENT is a bit stupid, * but perhaps it gets more... */ config_state = CONFIG_OWNER_ENVIRONMENT; if( getenv("REPREPRO_BASE_DIR") != NULL ) { CONFIGDUP(x_basedir, getenv("REPREPRO_BASE_DIR")); } if( getenv("REPREPRO_CONFIG_DIR") != NULL ) { CONFIGDUP(x_confdir,getenv("REPREPRO_CONFIG_DIR")); } disallow_plus_prefix(x_basedir, "basedir", ""); tempconfdir = expand_plus_prefix(x_confdir, "confdir", "b", false); config_state = CONFIG_OWNER_FILE; optionsfile_parse(tempconfdir, longopts, handle_option); if( tempconfdir != x_confdir ) free(tempconfdir); disallow_plus_prefix(x_basedir, "basedir", ""); disallow_plus_prefix(x_methoddir, "methoddir", ""); x_confdir = expand_plus_prefix(x_confdir, "confdir", "b", true); x_outdir = expand_plus_prefix(x_outdir, "outdir", "bc", true); x_logdir = expand_plus_prefix(x_logdir, "logdir", "boc", true); x_dbdir = expand_plus_prefix(x_dbdir, "dbdir", "boc", true); x_distdir = expand_plus_prefix(x_distdir, "distdir", "boc", true); x_listdir = expand_plus_prefix(x_listdir, "listdir", "boc", true); if( x_morguedir != NULL ) x_morguedir = expand_plus_prefix(x_morguedir, "morguedir", "boc", true); if( guessgpgtty && (getenv("GPG_TTY")==NULL) && isatty(0) ) { static char terminalname[1024]; ssize_t len; len = readlink("/proc/self/fd/0", terminalname, 1023); if( len > 0 && len < 1024 ) { terminalname[len] = '\0'; setenv("GPG_TTY", terminalname, 0); } else if( verbose > 10 ) { fprintf(stderr, "Could not readlink /proc/self/fd/0 (error was %s), not setting GPG_TTY.\n", strerror(errno)); } } if( delete < D_COPY ) delete = D_COPY; if( interrupted() ) exit(EXIT_RET(RET_ERROR_INTERRUPTED)); global.basedir = x_basedir; global.dbdir = x_dbdir; global.outdir = x_outdir; global.confdir = x_confdir; global.distdir = x_distdir; global.logdir = x_logdir; global.methoddir = x_methoddir; global.listdir = x_listdir; global.morguedir = x_morguedir; if( gunzip != NULL && gunzip[0] == '+' ) gunzip = expand_plus_prefix(gunzip, "gunzip", "boc", true); if( bunzip2 != NULL && bunzip2[0] == '+' ) bunzip2 = expand_plus_prefix(bunzip2, "bunzip2", "boc", true); if( unlzma != NULL && unlzma[0] == '+' ) unlzma = expand_plus_prefix(unlzma, "unlzma", "boc", true); if( unxz != NULL && unxz[0] == '+' ) unxz = expand_plus_prefix(unxz, "unxz", "boc", true); uncompressions_check(gunzip, bunzip2, unlzma, unxz); free(gunzip); free(bunzip2); free(unlzma); free(unxz); a = all_actions; while( a->name != NULL ) { a++; } r = atoms_init(a - all_actions); if( r == RET_ERROR_OOM ) (void)fputs("Out of Memory!\n",stderr); if( RET_WAS_ERROR(r) ) exit(EXIT_RET(r)); for( a = all_actions; a->name != NULL ; a++ ) { atoms_commands[1 + (a - all_actions)] = a->name; } if( gnupghome != NULL ) { gnupghome = expand_plus_prefix(gnupghome, "gnupghome", "boc", true); if( setenv("GNUPGHOME", gnupghome, 1) != 0 ) { int e = errno; fprintf(stderr, "Error %d setting GNUPGHOME to '%s': %s\n", e, gnupghome, strerror(e)); myexit(EXIT_FAILURE); } } a = all_actions; while( a->name != NULL ) { if( strcasecmp(a->name,argv[optind]) == 0 ) { signature_init(askforpassphrase); r = callaction(1 + (a - all_actions), a, argc-optind, (const char**)argv+optind); /* yeah, freeing all this stuff before exiting is * stupid, but it makes valgrind logs easier * readable */ signatures_done(); free_known_keys(); if( RET_WAS_ERROR(r) ) { if( r == RET_ERROR_OOM ) (void)fputs("Out of Memory!\n",stderr); else if( verbose >= 0 ) (void)fputs("There have been errors!\n",stderr); } myexit(EXIT_RET(r)); } else a++; } fprintf(stderr,"Unknown action '%s'. (see --help for available options and actions)\n",argv[optind]); signatures_done(); myexit(EXIT_FAILURE); }