rpm 5.2.1

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

rpmrepo.c
Go to the documentation of this file.
1
5 #include "system.h"
6
7 #if defined(WITH_SQLITE)
8 #include <sqlite3.h>
9 #ifdef __LCLINT__
10 /*@-incondefs -redecl @*/
11 extern const char *sqlite3_errmsg(sqlite3 *db)
12 /*@*/;
13 extern int sqlite3_open(
14 const char *filename, /* Database filename (UTF-8) */
15 /*@out@*/ sqlite3 **ppDb /* OUT: SQLite db handle */
16 )
17 /*@modifies *ppDb @*/;
18 extern int sqlite3_exec(
19 sqlite3 *db, /* An open database */
20 const char *sql, /* SQL to be evaluted */
21 int (*callback)(void*,int,char**,char**), /* Callback function */
22 void *, /* 1st argument to callback */
23 /*@out@*/ char **errmsg /* Error msg written here */
24 )
25 /*@modifies db, *errmsg @*/;
26 extern int sqlite3_prepare(
27 sqlite3 *db, /* Database handle */
28 const char *zSql, /* SQL statement, UTF-8 encoded */
29 int nByte, /* Maximum length of zSql in bytes. */
30 /*@out@*/ sqlite3_stmt **ppStmt, /* OUT: Statement handle */
31 /*@out@*/ const char **pzTail /* OUT: Pointer to unused portion of zSql */
32 )
33 /*@modifies *ppStmt, *pzTail @*/;
34 extern int sqlite3_reset(sqlite3_stmt *pStmt)
35 /*@modifies pStmt @*/;
36 extern int sqlite3_step(sqlite3_stmt *pStmt)
37 /*@modifies pStmt @*/;
38 extern int sqlite3_finalize(/*@only@*/ sqlite3_stmt *pStmt)
39 /*@modifies pStmt @*/;
40 extern int sqlite3_close(sqlite3 * db)
41 /*@modifies db @*/;
42 /*@=incondefs =redecl @*/
43 #endif
44 #endif
45
46 #include <rpmio_internal.h> /* XXX fdInitDigest() et al */
47 #include <fts.h>
48 #include <argv.h>
49 #include <mire.h>
50 #include <poptIO.h>
51
52 #include <rpmtypes.h>
53 #include <rpmtag.h>
54 #include <pkgio.h>
55 #include <rpmts.h>
56
57 #include "debug.h"
58
59 /*@access FD_t @*/
60 /*@access miRE @*/
61
62 /*==============================================================*/
63
64 /*@unchecked@*/
65 static int _repo_debug;
66
67 typedef struct rpmrepo_s * rpmrepo;
68 typedef struct rpmrfile_s * rpmrfile;
69
73 struct rpmrfile_s {
74 /*@observer@*/
75 const char * type;
76 /*@observer@*/
77 const char * xml_init;
78 /*@observer@*/ /*@relnull@*/
79 const char * xml_qfmt;
80 /*@observer@*/
81 const char * xml_fini;
82 /*@observer@*/
83 const char ** sql_init;
84 /*@observer@*/
85 const char * sql_qfmt;
86 #ifdef NOTYET /* XXX char **?!? */
87 /*@observer@*/
88 const char ** sql_fini;
89 #endif
90 /*@observer@*/
91 const char * yaml_init;
92 /*@observer@*/
93 const char * yaml_qfmt;
94 /*@observer@*/
95 const char * yaml_fini;
96 /*@observer@*/
97 const char * Packages_init;
98 /*@observer@*/
99 const char * Packages_qfmt;
100 /*@observer@*/
101 const char * Packages_fini;
102 /*@observer@*/
103 const char * Sources_init;
104 /*@observer@*/
105 const char * Sources_qfmt;
106 /*@observer@*/
107 const char * Sources_fini;
108 /*@relnull@*/
109 FD_t fd;
110 #if defined(WITH_SQLITE)
111 sqlite3 * sqldb;
112 #endif
113 /*@null@*/
114 const char * digest;
115 /*@null@*/
116 const char * Zdigest;
117 time_t ctime;
118 };
119
123 struct rpmrepo_s {
124 int quiet;
125 int verbose;
126 int dryrun;
127 /*@null@*/
128 ARGV_t exclude_patterns;
129 /*@relnull@*/
130 miRE excludeMire;
131 int nexcludes;
132 /*@null@*/
133 ARGV_t include_patterns;
134 /*@relnull@*/
135 miRE includeMire;
136 int nincludes;
137 /*@null@*/
138 const char * basedir;
139 /*@null@*/
140 const char * baseurl;
141 #ifdef NOTYET
142 /*@null@*/
143 const char * groupfile;
144 #endif
145 int split;
146 #if defined(WITH_SQLITE)
147 int database;
148 #endif
149 int pretty;
150 int checkts;
151 /*@relnull@*/
152 const char * outputdir;
153
154 int nofollow;
155 /*@null@*/
156 ARGV_t manifests;
157
158 /*@observer@*/ /*@relnull@*/
159 const char * tempdir;
160 /*@observer@*/ /*@relnull@*/
161 const char * finaldir;
162 /*@observer@*/ /*@relnull@*/
163 const char * olddir;
164
165 time_t mdtimestamp;
166
167 int uniquemdfilenames;
168
169 /*@null@*/
170 rpmts ts;
171 /*@null@*/
172 ARGV_t pkglist;
173 unsigned current;
174 unsigned pkgcount;
175
176 /*@null@*/
177 ARGV_t directories;
178 int ftsoptions;
179 uint32_t pkgalgo;
180 uint32_t algo;
181 int compression;
182 /*@observer@*/
183 const char * markup;
184 /*@observer@*/ /*@null@*/
185 const char * suffix;
186 /*@observer@*/
187 const char * wmode;
188
189 struct rpmrfile_s primary;
190 struct rpmrfile_s filelists;
191 struct rpmrfile_s other;
192 struct rpmrfile_s repomd;
193
194 };
195
196 /*@unchecked@*/ /*@observer@*/
197 static const char primary_xml_init[] =
198 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
199 "<metadata xmlns=\"http://linux.duke.edu/metadata/common\" xmlns:rpm=\"http://linux.duke.edu/metadata/rpm\" packages=\"0\">\n";
200 /*@unchecked@*/ /*@observer@*/
201 static const char primary_xml_fini[] = "</metadata>\n";
202
203 /*@unchecked@*/ /*@observer@*/
204 static const char filelists_xml_init[] =
205 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
206 "<filelists xmlns=\"http://linux.duke.edu/metadata/filelists\" packages=\"0\">\n";
207 /*@unchecked@*/ /*@observer@*/
208 static const char filelists_xml_fini[] = "</filelists>\n";
209
210 /*@unchecked@*/ /*@observer@*/
211 static const char other_xml_init[] =
212 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
213 "<otherdata xmlns=\"http://linux.duke.edu/metadata/other\" packages=\"0\">\n";
214 /*@unchecked@*/ /*@observer@*/
215 static const char other_xml_fini[] = "</otherdata>\n";
216
217 /*@unchecked@*/ /*@observer@*/
218 static const char repomd_xml_init[] = "\
219 <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
220 <repomd xmlns=\"http://linux.duke.edu/metadata/repo\">\n";
221 /*@unchecked@*/ /*@observer@*/
222 static const char repomd_xml_fini[] = "</repomd>\n";
223
224 /* XXX todo: wire up popt aliases and bury the --queryformat glop externally. */
225 /*@unchecked@*/ /*@observer@*/
226 static const char primary_xml_qfmt[] =
227 #include "yum_primary_xml"
228 ;
229
230 /*@unchecked@*/ /*@observer@*/
231 static const char filelists_xml_qfmt[] =
232 #include "yum_filelists_xml"
233 ;
234
235 /*@unchecked@*/ /*@observer@*/
236 static const char other_xml_qfmt[] =
237 #include "yum_other_xml"
238 ;
239
240 /*@unchecked@*/ /*@observer@*/
241 static const char primary_yaml_qfmt[] =
242 #include "wnh_primary_yaml"
243 ;
244
245 /*@unchecked@*/ /*@observer@*/
246 static const char filelists_yaml_qfmt[] =
247 #include "wnh_filelists_yaml"
248 ;
249
250 /*@unchecked@*/ /*@observer@*/
251 static const char other_yaml_qfmt[] =
252 #include "wnh_other_yaml"
253 ;
254
255 /*@unchecked@*/ /*@observer@*/
256 static const char Packages_qfmt[] =
257 #include "deb_Packages"
258 ;
259
260 /*@unchecked@*/ /*@observer@*/
261 static const char Sources_qfmt[] =
262 #include "deb_Sources"
263 ;
264
265 /*@-nullassign@*/
266 /*@unchecked@*/ /*@observer@*/
267 static const char *primary_sql_init[] = {
268 "PRAGMA synchronous = \"OFF\";",
269 "pragma locking_mode = \"EXCLUSIVE\";",
270 "CREATE TABLE conflicts ( pkgKey INTEGER, name TEXT, flags TEXT, epoch TEXT, version TEXT, release TEXT );",
271 "CREATE TABLE db_info (dbversion INTEGER, checksum TEXT);",
272 "CREATE TABLE files ( pkgKey INTEGER, name TEXT, type TEXT );",
273 "CREATE TABLE obsoletes ( pkgKey INTEGER, name TEXT, flags TEXT, epoch TEXT, version TEXT, release TEXT );",
274 "CREATE TABLE packages ( pkgKey INTEGER PRIMARY KEY, pkgId TEXT, name TEXT, arch TEXT, version TEXT, epoch TEXT, release TEXT, summary TEXT, description TEXT, url TEXT, time_file INTEGER, time_build INTEGER, rpm_license TEXT, rpm_vendor TEXT, rpm_group TEXT, rpm_buildhost TEXT, rpm_sourcerpm TEXT, rpm_header_start INTEGER, rpm_header_end INTEGER, rpm_packager TEXT, size_package INTEGER, size_installed INTEGER, size_archive INTEGER, location_href TEXT, location_base TEXT, checksum_type TEXT);",
275 "CREATE TABLE provides ( pkgKey INTEGER, name TEXT, flags TEXT, epoch TEXT, version TEXT, release TEXT );",
276 "CREATE TABLE requires ( pkgKey INTEGER, name TEXT, flags TEXT, epoch TEXT, version TEXT, release TEXT );",
277 "CREATE INDEX filenames ON files (name);",
278 "CREATE INDEX packageId ON packages (pkgId);",
279 "CREATE INDEX packagename ON packages (name);",
280 "CREATE INDEX pkgconflicts on conflicts (pkgKey);",
281 "CREATE INDEX pkgobsoletes on obsoletes (pkgKey);",
282 "CREATE INDEX pkgprovides on provides (pkgKey);",
283 "CREATE INDEX pkgrequires on requires (pkgKey);",
284 "CREATE INDEX providesname ON provides (name);",
285 "CREATE INDEX requiresname ON requires (name);",
286 "CREATE TRIGGER removals AFTER DELETE ON packages\
287 \n BEGIN\n\
288 \n DELETE FROM files WHERE pkgKey = old.pkgKey;\
289 \n DELETE FROM requires WHERE pkgKey = old.pkgKey;\
290 \n DELETE FROM provides WHERE pkgKey = old.pkgKey;\
291 \n DELETE FROM conflicts WHERE pkgKey = old.pkgKey;\
292 \n DELETE FROM obsoletes WHERE pkgKey = old.pkgKey;\
293 \n END;",
294 "INSERT into db_info values (9, 'direct_create');",
295 NULL
296 };
297 /*XXX todo: DBVERSION needs to be set */
298
299 /*@unchecked@*/ /*@observer@*/
300 static const char *filelists_sql_init[] = {
301 "PRAGMA synchronous = \"OFF\";",
302 "pragma locking_mode = \"EXCLUSIVE\";",
303 "CREATE TABLE db_info (dbversion INTEGER, checksum TEXT);",
304 "CREATE TABLE filelist ( pkgKey INTEGER, name TEXT, type TEXT );",
305 "CREATE TABLE packages ( pkgKey INTEGER PRIMARY KEY, pkgId TEXT);",
306 "CREATE INDEX filelistnames ON filelist (name);",
307 "CREATE INDEX keyfile ON filelist (pkgKey);",
308 "CREATE INDEX pkgId ON packages (pkgId);",
309 "CREATE TRIGGER remove_filelist AFTER DELETE ON packages\
310 \n BEGIN\
311 \n DELETE FROM filelist WHERE pkgKey = old.pkgKey;\
312 \n END;",
313 "INSERT into db_info values (9, 'direct_create');",
314 NULL
315 };
316 /*XXX todo: DBVERSION needs to be set */
317
318 /*@unchecked@*/ /*@observer@*/
319 static const char *other_sql_init[] = {
320 "PRAGMA synchronous = \"OFF\";",
321 "pragma locking_mode = \"EXCLUSIVE\";",
322 "CREATE TABLE changelog ( pkgKey INTEGER, author TEXT, date INTEGER, changelog TEXT);",
323 "CREATE TABLE db_info (dbversion INTEGER, checksum TEXT);",
324 "CREATE TABLE packages ( pkgKey INTEGER PRIMARY KEY, pkgId TEXT);",
325 "CREATE INDEX keychange ON changelog (pkgKey);",
326 "CREATE INDEX pkgId ON packages (pkgId);",
327 "CREATE TRIGGER remove_changelogs AFTER DELETE ON packages\
328 \n BEGIN\
329 \n DELETE FROM changelog WHERE pkgKey = old.pkgKey;\
330 \n END;",
331 "INSERT into db_info values (9, 'direct_create');",
332 NULL
333 };
334 /*XXX todo: DBVERSION needs to be set */
335 /*@=nullassign@*/
336
337 /* packages 1 pkgKey INTEGER PRIMARY KEY */
338 /* packages 2 pkgId TEXT */
339 /* packages 3 name TEXT */
340 /* packages 4 arch TEXT */
341 /* packages 5 version TEXT */
342 /* packages 6 epoch TEXT */
343 /* packages 7 release TEXT */
344 /* packages 8 summary TEXT */
345 /* packages 9 description TEXT */
346 /* packages 10 url TEXT */
347 /* packages 11 time_file INTEGER */
348 /* packages 12 time_build INTEGER */
349 /* packages 13 rpm_license TEXT */
350 /* packages 14 rpm_vendor TEXT */
351 /* packages 15 rpm_group TEXT */
352 /* packages 16 rpm_buildhost TEXT */
353 /* packages 17 rpm_sourcerpm TEXT */
354 /* packages 18 rpm_header_start INTEGER */
355 /* packages 19 rpm_header_end INTEGER */
356 /* packages 20 rpm_packager TEXT */
357 /* packages 21 size_package INTEGER */
358 /* packages 22 size_installed INTEGER */
359 /* packages 23 size_archive INTEGER */
360 /* packages 24 location_href TEXT */
361 /* packages 25 location_base TEXT */
362 /* packages 26 checksum_type TEXT */
363 /* obsoletes 1 pkgKey INTEGER */
364 /* obsoletes 2 name TEXT */
365 /* obsoletes 3 flags TEXT */
366 /* obsoletes 4 epoch TEXT */
367 /* obsoletes 5 version TEXT */
368 /* obsoletes 6 release TEXT */
369 /* provides 1 pkgKey INTEGER */
370 /* provides 2 name TEXT */
371 /* provides 3 flags TEXT */
372 /* provides 4 epoch TEXT */
373 /* provides 5 version TEXT */
374 /* provides 6 release TEXT */
375 /* conflicts 1 pkgKey INTEGER */
376 /* conflicts 2 name TEXT */
377 /* conflicts 3 flags TEXT */
378 /* conflicts 4 epoch TEXT */
379 /* conflicts 5 version TEXT */
380 /* conflicts 6 release TEXT */
381 /* requires 1 pkgKey INTEGER */
382 /* requires 2 name TEXT */
383 /* requires 3 flags TEXT */
384 /* requires 4 epoch TEXT */
385 /* requires 5 version TEXT */
386 /* requires 6 release TEXT */
387 /* files 1 pkgKey INTEGER */
388 /* files 2 name TEXT */
389 /* files 3 type TEXT */
390
391 /*@unchecked@*/ /*@observer@*/
392 static const char primary_sql_qfmt[] =
393 #include "yum_primary_sqlite"
394 ;
395
396 /* packages 1 pkgKey INTEGER PRIMARY KEY */
397 /* packages 2 pkgId TEXT */
398 /* filelist 1 pkgKey INTEGER */
399 /* filelist 2 name TEXT */
400 /* filelist 3 type TEXT */
401
402 /*@unchecked@*/ /*@observer@*/
403 static const char filelists_sql_qfmt[] =
404 #include "yum_filelists_sqlite"
405 ;
406
407 /* packages 1 pkgKey INTEGER PRIMARY KEY */
408 /* packages 2 pkgId TEXT */
409 /* changelog 1 pkgKey INTEGER */
410 /* changelog 2 author TEXT */
411 /* changelog 3 date INTEGER */
412 /* changelog 4 changelog TEXT */
413
414 /*@unchecked@*/ /*@observer@*/
415 static const char other_sql_qfmt[] =
416 #include "yum_other_sqlite"
417 ;
418
419 /*@-fullinitblock@*/
420 /*@unchecked@*/
421 static struct rpmrepo_s __rpmrepo = {
422 .pretty = 1,
423 #if defined(WITH_SQLITE)
424 .database = 0,
425 #endif
426 .tempdir = ".repodata",
427 .finaldir = "repodata",
428 .olddir = ".olddata",
429 .markup = ".xml",
430 .pkgalgo = PGPHASHALGO_SHA1,
431 .algo = PGPHASHALGO_SHA1,
432 .primary = {
433 .type = "primary",
434 .xml_init= primary_xml_init,
435 .xml_qfmt= primary_xml_qfmt,
436 .xml_fini= primary_xml_fini,
437 .sql_init= primary_sql_init,
438 .sql_qfmt= primary_sql_qfmt,
439 #ifdef NOTYET /* XXX char **?!? */
440 .sql_fini= NULL,
441 #endif
442 .yaml_init= NULL,
443 .yaml_qfmt= primary_yaml_qfmt,
444 .yaml_fini= NULL,
445 .Packages_init= NULL,
446 .Packages_qfmt= NULL,
447 .Packages_fini= NULL,
448 .Sources_init= NULL,
449 .Sources_qfmt= NULL,
450 .Sources_fini= NULL
451 },
452 .filelists = {
453 .type = "filelists",
454 .xml_init= filelists_xml_init,
455 .xml_qfmt= filelists_xml_qfmt,
456 .xml_fini= filelists_xml_fini,
457 .sql_init= filelists_sql_init,
458 .sql_qfmt= filelists_sql_qfmt,
459 #ifdef NOTYET /* XXX char **?!? */
460 .sql_fini= NULL,
461 #endif
462 .yaml_init= NULL,
463 .yaml_qfmt= filelists_yaml_qfmt,
464 .yaml_fini= NULL,
465 .Packages_init= NULL,
466 .Packages_qfmt= NULL,
467 .Packages_fini= NULL,
468 .Sources_init= NULL,
469 .Sources_qfmt= NULL,
470 .Sources_fini= NULL
471 },
472 .other = {
473 .type = "other",
474 .xml_init= other_xml_init,
475 .xml_qfmt= other_xml_qfmt,
476 .xml_fini= other_xml_fini,
477 .sql_init= other_sql_init,
478 .sql_qfmt= other_sql_qfmt,
479 #ifdef NOTYET /* XXX char **?!? */
480 .sql_fini= NULL,
481 #endif
482 .yaml_init= NULL,
483 .yaml_qfmt= other_yaml_qfmt,
484 .yaml_fini= NULL,
485 .Packages_init= NULL,
486 .Packages_qfmt= NULL,
487 .Packages_fini= NULL,
488 .Sources_init= NULL,
489 .Sources_qfmt= NULL,
490 .Sources_fini= NULL
491 },
492 .repomd = {
493 .type = "repomd",
494 .xml_init= repomd_xml_init,
495 .xml_qfmt= NULL,
496 .xml_fini= repomd_xml_fini,
497 .sql_init= NULL,
498 .sql_qfmt= NULL,
499 #ifdef NOTYET /* XXX char **?!? */
500 .sql_fini= NULL,
501 #endif
502 .yaml_init= NULL,
503 .yaml_qfmt= NULL,
504 .yaml_fini= NULL,
505 .Packages_init= NULL,
506 .Packages_qfmt= Packages_qfmt,
507 .Packages_fini= NULL,
508 .Sources_init= NULL,
509 .Sources_qfmt= Sources_qfmt,
510 .Sources_fini= NULL
511 }
512 };
513 /*@=fullinitblock@*/
514
515 /*@unchecked@*/
516 static rpmrepo _rpmrepo = &__rpmrepo;
517
518 /*==============================================================*/
524 /*@mayexit@*/
525 static void
526 repo_error(int lvl, const char *fmt, ...)
527 /*@globals fileSystem @*/
528 /*@modifies fileSystem @*/
529 {
530 va_list ap;
531
532 va_start(ap, fmt);
533 (void) fflush(NULL);
534 (void) fprintf(stderr, "%s: ", __progname);
535 (void) vfprintf(stderr, fmt, ap);
536 va_end (ap);
537 (void) fprintf(stderr, "\n");
538 if (lvl)
539 exit(EXIT_FAILURE);
540 }
541
549 static void repoProgress(/*@unused@*/ rpmrepo repo,
550 /*@null@*/ const char * item, int current, int total)
551 /*@globals fileSystem, internalState @*/
552 /*@modifies fileSystem, internalState @*/
553 {
554 static size_t ncols = 80 - 1; /* XXX TIOCGWINSIZ */
555 const char * bn = (item != NULL ? strrchr(item, '/') : NULL);
556 size_t nb;
557
558 if (bn != NULL)
559 bn++;
560 else
561 bn = item;
562 nb = fprintf(stdout, "\r%s: %d/%d", __progname, current, total);
563 if (bn != NULL)
564 nb += fprintf(stdout, " - %s", bn);
565 nb--;
566 if (nb < ncols)
567 fprintf(stdout, "%*s", (int)(ncols - nb), "");
568 ncols = nb;
569 (void) fflush(stdout);
570 }
571
577 static int rpmioExists(const char * fn, /*@out@*/ struct stat * st)
578 /*@globals h_errno, fileSystem, internalState @*/
579 /*@modifies st, fileSystem, internalState @*/
580 {
581 return (Stat(fn, st) == 0);
582 }
583
589 static time_t rpmioCtime(const char * fn)
590 /*@globals h_errno, fileSystem, internalState @*/
591 /*@modifies fileSystem, internalState @*/
592 {
593 struct stat sb;
594 time_t stctime = 0;
595
596 if (rpmioExists(fn, &sb))
597 stctime = sb.st_ctime;
598 return stctime;
599 }
600
606 /*@null@*/
607 static const char * repoRealpath(const char * lpath)
608 /*@globals fileSystem, internalState @*/
609 /*@modifies fileSystem, internalState @*/
610 {
611 /* XXX GLIBC: realpath(path, NULL) return malloc'd */
612 const char *rpath = Realpath(lpath, NULL);
613 if (rpath == NULL) {
614 char fullpath[MAXPATHLEN];
615 rpath = Realpath(lpath, fullpath);
616 if (rpath != NULL)
617 rpath = xstrdup(rpath);
618 }
619 return rpath;
620 }
621
622 /*==============================================================*/
629 static int repoMkdir(rpmrepo repo, const char * dn)
630 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
631 /*@modifies rpmGlobalMacroContext, fileSystem, internalState @*/
632 {
633 const char * dnurl = rpmGetPath(repo->outputdir, "/", dn, NULL);
634 /*@-mods@*/
635 int ut = urlPath(dnurl, &dn);
636 /*@=mods@*/
637 int rc = 0;;
638
639 /* XXX todo: rpmioMkpath doesn't grok URI's */
640 if (ut == URL_IS_UNKNOWN)
641 rc = rpmioMkpath(dn, 0755, (uid_t)-1, (gid_t)-1);
642 else
643 rc = (Mkdir(dnurl, 0755) == 0 || errno == EEXIST ? 0 : -1);
644 if (rc)
645 repo_error(0, _("Cannot create/verify %s: %s"), dnurl, strerror(errno));
646 dnurl = _free(dnurl);
647 return rc;
648 }
649
657 static const char * repoGetPath(rpmrepo repo, const char * dir,
658 const char * type, int compress)
659 /*@globals h_errno, rpmGlobalMacroContext, internalState @*/
660 /*@modifies rpmGlobalMacroContext, internalState @*/
661 {
662 return rpmGetPath(repo->outputdir, "/", dir, "/", type,
663 (repo->markup != NULL ? repo->markup : ""),
664 (repo->suffix != NULL && compress ? repo->suffix : ""), NULL);
665 }
666
672 static int repoTestSetupDirs(rpmrepo repo)
673 /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/
674 /*@modifies repo, rpmGlobalMacroContext, fileSystem, internalState @*/
675 {
676 const char ** directories = repo->directories;
677 struct stat sb, *st = &sb;
678 const char * dn;
679 const char * fn;
680 int rc = 0;
681
682 /* XXX todo: check repo->pkglist existence? */
683
684 if (directories != NULL)
685 while ((dn = *directories++) != NULL) {
686 if (!rpmioExists(dn, st) || !S_ISDIR(st->st_mode)) {
687 repo_error(0, _("Directory %s must exist"), dn);
688 rc = 1;
689 }
690 }
691
692 /* XXX todo create outputdir if it doesn't exist? */
693 if (!rpmioExists(repo->outputdir, st)) {
694 repo_error(0, _("Directory %s does not exist."), repo->outputdir);
695 rc = 1;
696 }
697 if (Access(repo->outputdir, W_OK)) {
698 repo_error(0, _("Directory %s must be writable."), repo->outputdir);
699 rc = 1;
700 }
701
702 if (repoMkdir(repo, repo->tempdir)
703 || repoMkdir(repo, repo->finaldir))
704 rc = 1;
705
706 dn = rpmGetPath(repo->outputdir, "/", repo->olddir, NULL);
707 if (rpmioExists(dn, st)) {
708 repo_error(0, _("Old data directory exists, please remove: %s"), dn);
709 rc = 1;
710 }
711 dn = _free(dn);
712
713 { /*@observer@*/
714 static const char * dirs[] = { ".repodata", "repodata", NULL };
715 /*@observer@*/
716 static const char * types[] =
717 { "primary", "filelists", "other", "repomd", NULL };
718 const char ** dirp, ** typep;
719 for (dirp = dirs; *dirp != NULL; dirp++) {
720 for (typep = types; *typep != NULL; typep++) {
721 fn = repoGetPath(repo, *dirp, *typep, strcmp(*typep, "repomd"));
722 if (rpmioExists(fn, st)) {
723 if (Access(fn, W_OK)) {
724 repo_error(0, _("Path must be writable: %s"), fn);
725 rc = 1;
726 } else
727 if (repo->checkts && st->st_ctime > repo->mdtimestamp)
728 repo->mdtimestamp = st->st_ctime;
729 }
730 fn = _free(fn);
731 }
732 }
733 }
734
735 #ifdef NOTYET /* XXX repo->package_dir needs to go away. */
736 if (repo->groupfile != NULL) {
737 if (repo->split || repo->groupfile[0] != '/') {
738 fn = rpmGetPath(repo->package_dir, "/", repo->groupfile, NULL);
739 repo->groupfile = _free(repo->groupfile);
740 repo->groupfile = fn;
741 fn = NULL;
742 }
743 if (!rpmioExists(repo->groupfile, st)) {
744 repo_error(0, _("groupfile %s cannot be found."), repo->groupfile);
745 rc = 1;
746 }
747 }
748 #endif
749 return rc;
750 }
751
758 static int chkSuffix(const char * fn, const char * suffix)
759 /*@*/
760 {
761 size_t flen = strlen(fn);
762 size_t slen = strlen(suffix);
763 return (flen > slen && !strcmp(fn + flen - slen, suffix));
764 }
765
773 /*@null@*/
774 static const char ** repoGetFileList(rpmrepo repo, const char *roots[],
775 const char * ext)
776 /*@globals fileSystem, internalState @*/
777 /*@modifies repo, fileSystem, internalState @*/
778 {
779 const char ** pkglist = NULL;
780 FTS * t;
781 FTSENT * p;
782 int xx;
783
784 if ((t = Fts_open((char *const *)roots, repo->ftsoptions, NULL)) == NULL)
785 repo_error(1, _("Fts_open: %s"), strerror(errno));
786
787 while ((p = Fts_read(t)) != NULL) {
788 #ifdef NOTYET
789 const char * fts_name = p->fts_name;
790 size_t fts_namelen = p->fts_namelen;
791
792 /* XXX fts(3) (and Fts(3)) have fts_name = "" with pesky trailing '/' */
793 if (p->fts_level == 0 && fts_namelen == 0) {
794 fts_name = ".";
795 fts_namelen = sizeof(".") - 1;
796 }
797 #endif
798
799 /* Should this element be excluded/included? */
800 /* XXX todo: apply globs to fts_path rather than fts_name? */
801 /*@-onlytrans@*/
802 if (mireApply(repo->excludeMire, repo->nexcludes, p->fts_name, 0, -1) >= 0)
803 continue;
804 if (mireApply(repo->includeMire, repo->nincludes, p->fts_name, 0, +1) < 0)
805 continue;
806 /*@=onlytrans@*/
807
808 switch (p->fts_info) {
809 case FTS_D:
810 case FTS_DP:
811 default:
812 continue;
813 /*@notreached@*/ /*@switchbreak@*/ break;
814 case FTS_SL:
815 if (repo->nofollow)
816 continue;
817 /* XXX todo: fuss with symlinks */
818 /*@notreached@*/ /*@switchbreak@*/ break;
819 case FTS_F:
820 /* Is this a *.rpm file? */
821 if (chkSuffix(p->fts_name, ext))
822 xx = argvAdd(&pkglist, p->fts_path);
823 /*@switchbreak@*/ break;
824 }
825 }
826
827 (void) Fts_close(t);
828
829 if (_repo_debug)
830 argvPrint("pkglist", pkglist, NULL);
831
832 return pkglist;
833 }
834
840 static int repoCheckTimeStamps(rpmrepo repo)
841 /*@globals h_errno, fileSystem, internalState @*/
842 /*@modifies fileSystem, internalState @*/
843 {
844 int rc = 0;
845
846 if (repo->checkts) {
847 const char ** pkg;
848
849 if (repo->pkglist != NULL)
850 for (pkg = repo->pkglist; *pkg != NULL ; pkg++) {
851 struct stat sb, *st = &sb;
852 if (!rpmioExists(*pkg, st)) {
853 repo_error(0, _("cannot get to file: %s"), *pkg);
854 rc = 1;
855 } else if (st->st_ctime > repo->mdtimestamp)
856 rc = 1;
857 }
858 } else
859 rc = 1;
860
861 return rc;
862 }
863
870 static int rfileXMLWrite(rpmrfile rfile, /*@only@*/ /*@null@*/ const char * spew)
871 /*@globals fileSystem @*/
872 /*@modifies rfile, fileSystem @*/
873 {
874 size_t nspew = (spew != NULL ? strlen(spew) : 0);
875 /*@-nullpass@*/ /* XXX spew != NULL @*/
876 size_t nb = (nspew > 0 ? Fwrite(spew, 1, nspew, rfile->fd) : 0);
877 /*@=nullpass@*/
878 int rc = 0;
879 if (nspew != nb) {
880 repo_error(0, _("Fwrite failed: expected write %u != %u bytes: %s\n"),
881 (unsigned)nspew, (unsigned)nb, Fstrerror(rfile->fd));
882 rc = 1;
883 }
884 spew = _free(spew);
885 return rc;
886 }
887
894 static int repoFclose(rpmrepo repo, FD_t fd)
895 /*@modifies repo, fd @*/
896 {
897 int rc = 0;
898
899 if (fd != NULL) {
900 if (repo->ts != NULL) {
901 (void) rpmswAdd(rpmtsOp(repo->ts, RPMTS_OP_UNCOMPRESS),
902 fdstat_op(fd, FDSTAT_READ));
903 (void) rpmswAdd(rpmtsOp(repo->ts, RPMTS_OP_DIGEST),
904 fdstat_op(fd, FDSTAT_DIGEST));
905 }
906 rc = Fclose(fd);
907 }
908 return rc;
909 }
910
917 static int repoOpenMDFile(const rpmrepo repo, rpmrfile rfile)
918 /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/
919 /*@modifies rfile, rpmGlobalMacroContext, fileSystem, internalState @*/
920 {
921 const char * spew = rfile->xml_init;
922 size_t nspew = strlen(spew);
923 const char * fn = repoGetPath(repo, repo->tempdir, rfile->type, 1);
924 const char * tail;
925 size_t nb;
926 int rc = 0;
927
928 rfile->fd = Fopen(fn, repo->wmode);
929 assert(rfile->fd != NULL);
930
931 if (repo->algo != PGPHASHALGO_NONE)
932 fdInitDigest(rfile->fd, repo->algo, 0);
933
934 if ((tail = strstr(spew, " packages=\"0\">\n")) != NULL)
935 nspew -= strlen(tail);
936
937 nb = Fwrite(spew, 1, nspew, rfile->fd);
938
939 if (tail != NULL) {
940 char buf[64];
941 size_t tnb = snprintf(buf, sizeof(buf), " packages=\"%d\">\n",
942 repo->pkgcount);
943 nspew += tnb;
944 nb += Fwrite(buf, 1, tnb, rfile->fd);
945 }
946 if (nspew != nb) {
947 repo_error(0, _("Fwrite failed: expected write %u != %u bytes: %s\n"),
948 (unsigned)nspew, (unsigned)nb, Fstrerror(rfile->fd));
949 rc = 1;
950 }
951
952 fn = _free(fn);
953
954 #if defined(WITH_SQLITE)
955 if (repo->database) {
956 const char ** stmt;
957 int xx;
958 fn = rpmGetPath(repo->outputdir, "/", repo->tempdir, "/",
959 rfile->type, ".sqlite", NULL);
960 if ((xx = sqlite3_open(fn, &rfile->sqldb)) != SQLITE_OK)
961 repo_error(1, "sqlite3_open(%s): %s", fn, sqlite3_errmsg(rfile->sqldb));
962 for (stmt = rfile->sql_init; *stmt != NULL; stmt++) {
963 char * msg;
964 xx = sqlite3_exec(rfile->sqldb, *stmt, NULL, NULL, &msg);
965 if (xx != SQLITE_OK)
966 repo_error(1, "sqlite3_exec(%s, \"%s\"): %s\n", fn, *stmt,
967 (msg != NULL ? msg : "failed"));
968 }
969 fn = _free(fn);
970 }
971 #endif
972
973 return rc;
974 }
975
982 static Header repoReadHeader(rpmrepo repo, const char * path)
983 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
984 /*@modifies repo, rpmGlobalMacroContext, fileSystem, internalState @*/
985 {
986 /* XXX todo: read the payload and collect the blessed file digest. */
987 FD_t fd = Fopen(path, "r.ufdio");
988 Header h = NULL;
989
990 if (fd != NULL) {
991 uint32_t algo = repo->pkgalgo;
992 rpmRC rpmrc;
993
994 if (algo != PGPHASHALGO_NONE)
995 fdInitDigest(fd, algo, 0);
996
997 /* XXX what if path needs expansion? */
998 rpmrc = rpmReadPackageFile(repo->ts, fd, path, &h);
999 if (algo != PGPHASHALGO_NONE) {
1000 char buffer[32 * BUFSIZ];
1001 size_t nb = sizeof(buffer);
1002 size_t nr;
1003 while ((nr = Fread(buffer, sizeof(buffer[0]), nb, fd)) == nb)
1004 {};
1005 if (Ferror(fd)) {
1006 fprintf(stderr, _("%s: Fread(%s) failed: %s\n"),
1007 __progname, path, Fstrerror(fd));
1008 rpmrc = RPMRC_FAIL;
1009 } else {
1010 static int asAscii = 1;
1011 const char *digest = NULL;
1012 fdFiniDigest(fd, algo, &digest, NULL, asAscii);
1013 (void) headerSetDigest(h, digest);
1014 digest = _free(digest);
1015 }
1016 }
1017
1018 (void) Fclose(fd);
1019
1020 switch (rpmrc) {
1021 case RPMRC_NOTFOUND:
1022 case RPMRC_FAIL:
1023 default:
1024 (void)headerFree(h);
1025 h = NULL;
1026 break;
1027 case RPMRC_NOTTRUSTED:
1028 case RPMRC_NOKEY:
1029 case RPMRC_OK:
1030 if (repo->baseurl)
1031 (void) headerSetBaseURL(h, repo->baseurl);
1032 break;
1033 }
1034 }
1035 return h;
1036 }
1037
1044 static const char * rfileHeaderSprintf(Header h, const char * qfmt)
1045 /*@globals fileSystem @*/
1046 /*@modifies h, fileSystem @*/
1047 {
1048 const char * msg = NULL;
1049 const char * s = headerSprintf(h, qfmt, NULL, NULL, &msg);
1050 if (s == NULL)
1051 repo_error(1, _("headerSprintf(%s): %s"), qfmt, msg);
1052 assert(s != NULL);
1053 return s;
1054 }
1055
1056 #if defined(WITH_SQLITE)
1057
1062 static int rfileSQL(rpmrfile rfile, const char * msg, int rc)
1063 /*@globals fileSystem @*/
1064 /*@modifies fileSystem @*/
1065 {
1066 if (rc != SQLITE_OK || _repo_debug)
1067 repo_error(0, "sqlite3_%s(%s): %s", msg, rfile->type,
1068 sqlite3_errmsg(rfile->sqldb));
1069 return rc;
1070 }
1071
1077 static int rfileSQLStep(rpmrfile rfile, sqlite3_stmt * stmt)
1078 /*@globals fileSystem @*/
1079 /*@modifies fileSystem @*/
1080 {
1081 int loop = 1;
1082 int rc = 0;
1083 int xx;
1084
1085 /*@-infloops@*/
1086 while (loop) {
1087 rc = sqlite3_step(stmt);
1088 switch (rc) {
1089 default:
1090 rc = rfileSQL(rfile, "step", rc);
1091 /*@fallthrough@*/
1092 case SQLITE_DONE:
1093 loop = 0;
1094 /*@switchbreak@*/ break;
1095 }
1096 }
1097 /*@=infloops@*/
1098
1099 xx = rfileSQL(rfile, "reset",
1100 sqlite3_reset(stmt));
1101
1102 return rc;
1103 }
1104
1111 static const char * rfileHeaderSprintfHack(Header h, const char * qfmt)
1112 /*@globals fileSystem @*/
1113 /*@modifies h, fileSystem @*/
1114 {
1115 static const char mark[] = "'XXX'";
1116 static size_t nmark = sizeof("'XXX'") - 1;
1117 const char * msg = NULL;
1118 char * s = (char *) headerSprintf(h, qfmt, NULL, NULL, &msg);
1119 char * f, * fe;
1120 int nsubs = 0;
1121
1122 if (s == NULL)
1123 repo_error(1, _("headerSprintf(%s): %s"), qfmt, msg);
1124 assert(s != NULL);
1125
1126 /* XXX Find & replace 'XXX' with '%{DBINSTANCE}' the hard way. */
1127 /*@-nullptrarith@*/
1128 for (f = s; *f != '\0' && (fe = strstr(f, "'XXX'")) != NULL; fe += nmark, f = fe)
1129 nsubs++;
1130 /*@=nullptrarith@*/
1131
1132 if (nsubs > 0) {
1133 char instance[64];
1134 int xx = snprintf(instance, sizeof(instance), "'%u'",
1135 (unsigned) headerGetInstance(h));
1136 size_t tlen = strlen(s) + nsubs * ((int)strlen(instance) - (int)nmark);
1137 char * t = xmalloc(tlen + 1);
1138 char * te = t;
1139
1140 xx = xx;
1141 /*@-nullptrarith@*/
1142 for (f = s; *f != '\0' && (fe = strstr(f, mark)) != NULL; fe += nmark, f = fe) {
1143 *fe = '\0';
1144 te = stpcpy( stpcpy(te, f), instance);
1145 }
1146 /*@=nullptrarith@*/
1147 if (*f != '\0')
1148 te = stpcpy(te, f);
1149 s = _free(s);
1150 s = t;
1151 }
1152
1153 return s;
1154 }
1155
1162 static int rfileSQLWrite(rpmrfile rfile, /*@only@*/ const char * cmd)
1163 /*@globals fileSystem @*/
1164 /*@modifies fileSystem @*/
1165 {
1166 sqlite3_stmt * stmt;
1167 const char * tail;
1168 int xx;
1169
1170 xx = rfileSQL(rfile, "prepare",
1171 sqlite3_prepare(rfile->sqldb, cmd, (int)strlen(cmd), &stmt, &tail));
1172
1173 xx = rfileSQL(rfile, "reset",
1174 sqlite3_reset(stmt));
1175
1176 xx = rfileSQLStep(rfile, stmt);
1177
1178 xx = rfileSQL(rfile, "finalize",
1179 sqlite3_finalize(stmt));
1180
1181 cmd = _free(cmd);
1182
1183 return 0;
1184 }
1185 #endif
1186
1194 static int repoWriteMDFile(rpmrepo repo, rpmrfile rfile, Header h)
1195 /*@globals fileSystem @*/
1196 /*@modifies rfile, h, fileSystem @*/
1197 {
1198 int rc = 0;
1199
1200 if (rfile->xml_qfmt != NULL) {
1201 if (rfileXMLWrite(rfile, rfileHeaderSprintf(h, rfile->xml_qfmt)))
1202 rc = 1;
1203 }
1204
1205 #if defined(WITH_SQLITE)
1206 if (repo->database) {
1207 if (rfileSQLWrite(rfile, rfileHeaderSprintfHack(h, rfile->sql_qfmt)))
1208 rc = 1;
1209 }
1210 #endif
1211
1212 return rc;
1213 }
1214
1221 static int repoWriteMetadataDocs(rpmrepo repo, /*@null@*/ const char ** pkglist)
1222 /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/
1223 /*@modifies repo, rpmGlobalMacroContext, fileSystem, internalState @*/
1224 {
1225 const char * pkg;
1226 int rc = 0;
1227
1228 while ((pkg = *pkglist++) != NULL) {
1229 Header h = repoReadHeader(repo, pkg);
1230
1231 repo->current++;
1232 if (h == NULL) {
1233 #ifdef DYING /* XXX repoReadHeader() displays error. Continuing is foolish */
1234 repo_error(0, _("\nError %s: %s\n"), pkg, strerror(errno));
1235 continue;
1236 #else
1237 rc = 1;
1238 break;
1239 #endif
1240 }
1241 (void) headerSetInstance(h, (uint32_t)repo->current);
1242
1243 #ifdef NOTYET
1244 /* XXX todo: rpmGetPath(mydir, "/", filematrix[mydir], NULL); */
1245 reldir = (pkgpath != NULL ? pkgpath : rpmGetPath(repo->basedir, "/", repo->directories[0], NULL));
1246 self.primaryfile.write(po.do_primary_xml_dump(reldir, baseurl=repo->baseurl))
1247 self.flfile.write(po.do_filelists_xml_dump())
1248 self.otherfile.write(po.do_other_xml_dump())
1249 #endif
1250 if (repoWriteMDFile(repo, &repo->primary, h)
1251 || repoWriteMDFile(repo, &repo->filelists, h)
1252 || repoWriteMDFile(repo, &repo->other, h))
1253 rc = 1;
1254
1255 (void)headerFree(h);
1256 h = NULL;
1257 if (rc) break;
1258
1259 if (!repo->quiet) {
1260 if (repo->verbose)
1261 repo_error(0, "%d/%d - %s", repo->current, repo->pkgcount, pkg);
1262 else
1263 repoProgress(repo, pkg, repo->current, repo->pkgcount);
1264 }
1265 }
1266 return rc;
1267 }
1268
1273 static int repoRfileDigest(const rpmrepo repo, rpmrfile rfile,
1274 const char ** digestp)
1275 /*@modifies *digestp @*/
1276 {
1277 static int asAscii = 1;
1278 struct stat sb, *st = &sb;
1279 const char * fn = repoGetPath(repo, repo->tempdir, rfile->type, 1);
1280 const char * path = NULL;
1281 int ut = urlPath(fn, &path);
1282 FD_t fd = NULL;
1283 int rc = 1;
1284 int xx;
1285
1286 memset(st, 0, sizeof(*st));
1287 if (!rpmioExists(fn, st))
1288 goto exit;
1289 fd = Fopen(fn, "r.ufdio");
1290 if (fd == NULL || Ferror(fd))
1291 goto exit;
1292
1293 switch (ut) {
1294 case URL_IS_PATH:
1295 case URL_IS_UNKNOWN:
1296 #if defined(HAVE_MMAP)
1297 { void * mapped = (void *)-1;
1298
1299 if (st->st_size > 0)
1300 mapped = mmap(NULL, st->st_size, PROT_READ, MAP_SHARED, Fileno(fd), 0);
1301 if (mapped != (void *)-1) {
1302 rpmop op = rpmtsOp(repo->ts, RPMTS_OP_DIGEST);
1303 rpmtime_t tstamp = rpmswEnter(op, 0);
1304 DIGEST_CTX ctx = rpmDigestInit(repo->algo, RPMDIGEST_NONE);
1305 xx = rpmDigestUpdate(ctx, mapped, st->st_size);
1306 xx = rpmDigestFinal(ctx, digestp, NULL, asAscii);
1307 tstamp = rpmswExit(op, st->st_size);
1308 xx = munmap(mapped, st->st_size);
1309 break;
1310 }
1311 } /*@fallthrough@*/
1312 #endif
1313 default:
1314 { char buf[64 * BUFSIZ];
1315 size_t nb;
1316 size_t fsize = 0;
1317
1318 fdInitDigest(fd, repo->algo, 0);
1319 while ((nb = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0)
1320 fsize += nb;
1321 if (Ferror(fd))
1322 goto exit;
1323 fdFiniDigest(fd, repo->algo, digestp, NULL, asAscii);
1324 } break;
1325 }
1326
1327 rc = 0;
1328
1329 exit:
1330 if (fd)
1331 xx = repoFclose(repo, fd);
1332 fn = _free(fn);
1333 return rc;
1334 }
1335
1342 static int repoCloseMDFile(const rpmrepo repo, rpmrfile rfile)
1343 /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/
1344 /*@modifies rfile, rpmGlobalMacroContext, fileSystem, internalState @*/
1345 {
1346 static int asAscii = 1;
1347 char * xmlfn = xstrdup(fdGetOPath(rfile->fd));
1348 int rc = 0;
1349
1350 if (!repo->quiet)
1351 repo_error(0, _("Saving %s metadata"), basename(xmlfn));
1352
1353 if (rfileXMLWrite(rfile, xstrdup(rfile->xml_fini)))
1354 rc = 1;
1355
1356 if (repo->algo > 0)
1357 fdFiniDigest(rfile->fd, repo->algo, &rfile->digest, NULL, asAscii);
1358 else
1359 rfile->digest = xstrdup("");
1360
1361 (void) repoFclose(repo, rfile->fd);
1362 rfile->fd = NULL;
1363
1364 /* Compute the (usually compressed) ouput file digest too. */
1365 rfile->Zdigest = NULL;
1366 (void) repoRfileDigest(repo, rfile, &rfile->Zdigest);
1367
1368 #if defined(WITH_SQLITE)
1369 if (repo->database && rfile->sqldb != NULL) {
1370 const char *dbfn = rpmGetPath(repo->outputdir, "/", repo->tempdir, "/",
1371 rfile->type, ".sqlite", NULL);
1372 int xx;
1373 if ((xx = sqlite3_close(rfile->sqldb)) != SQLITE_OK)
1374 repo_error(1, "sqlite3_close(%s): %s", dbfn, sqlite3_errmsg(rfile->sqldb));
1375 rfile->sqldb = NULL;
1376 dbfn = _free(dbfn);
1377 }
1378 #endif
1379
1380 rfile->ctime = rpmioCtime(xmlfn);
1381 xmlfn = _free(xmlfn);
1382
1383 return rc;
1384 }
1385
1391 static int repoDoPkgMetadata(rpmrepo repo)
1392 /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/
1393 /*@modifies repo, rpmGlobalMacroContext, fileSystem, internalState @*/
1394 {
1395 int rc = 0;
1396
1397 repo->current = 0;
1398
1399 #ifdef NOTYET
1400 def _getFragmentUrl(self, url, fragment):
1401 import urlparse
1402 urlparse.uses_fragment.append('media')
1403 if not url:
1404 return url
1405 (scheme, netloc, path, query, fragid) = urlparse.urlsplit(url)
1406 return urlparse.urlunsplit((scheme, netloc, path, query, str(fragment)))
1407
1408 def doPkgMetadata(self):
1409 """all the heavy lifting for the package metadata"""
1410 if (argvCount(repo->directories) == 1) {
1411 MetaDataGenerator.doPkgMetadata(self)
1412 return
1413 }
1414
1415 ARGV_t roots = NULL;
1416 filematrix = {}
1417 for mydir in repo->directories {
1418 if (mydir[0] == '/')
1419 thisdir = xstrdup(mydir);
1420 else if (mydir[0] == '.' && mydir[1] == '.' && mydir[2] == '/')
1421 thisdir = Realpath(mydir, NULL);
1422 else
1423 thisdir = rpmGetPath(repo->basedir, "/", mydir, NULL);
1424
1425 xx = argvAdd(&roots, thisdir);
1426 thisdir = _free(thisdir);
1427
1428 filematrix[mydir] = repoGetFileList(repo, roots, '.rpm')
1429 self.trimRpms(filematrix[mydir])
1430 repo->pkgcount = argvCount(filematrix[mydir]);
1431 roots = argvFree(roots);
1432 }
1433
1434 mediano = 1;
1435 repo->baseurl = self._getFragmentUrl(repo->baseurl, mediano)
1436 #endif
1437
1438 if (repoOpenMDFile(repo, &repo->primary)
1439 || repoOpenMDFile(repo, &repo->filelists)
1440 || repoOpenMDFile(repo, &repo->other))
1441 rc = 1;
1442 if (rc) return rc;
1443
1444 #ifdef NOTYET
1445 for mydir in repo->directories {
1446 repo->baseurl = self._getFragmentUrl(repo->baseurl, mediano)
1447 /* XXX todo: rpmGetPath(mydir, "/", filematrix[mydir], NULL); */
1448 if (repoWriteMetadataDocs(repo, filematrix[mydir]))
1449 rc = 1;
1450 mediano++;
1451 }
1452 repo->baseurl = self._getFragmentUrl(repo->baseurl, 1)
1453 #else
1454 if (repoWriteMetadataDocs(repo, repo->pkglist))
1455 rc = 1;
1456 #endif
1457
1458 if (!repo->quiet)
1459 fprintf(stderr, "\n");
1460 if (repoCloseMDFile(repo, &repo->primary)
1461 || repoCloseMDFile(repo, &repo->filelists)
1462 || repoCloseMDFile(repo, &repo->other))
1463 rc = 1;
1464
1465 return rc;
1466 }
1467
1470 static /*@observer@*/ /*@null@*/ const char *
1471 algo2tagname(uint32_t algo)
1472 /*@*/
1473 {
1474 const char * tagname = NULL;
1475
1476 switch (algo) {
1477 case PGPHASHALGO_NONE: tagname = "none"; break;
1478 case PGPHASHALGO_MD5: tagname = "md5"; break;
1479 /* XXX todo: should be "sha1" */
1480 case PGPHASHALGO_SHA1: tagname = "sha"; break;
1481 case PGPHASHALGO_RIPEMD160: tagname = "rmd160"; break;
1482 case PGPHASHALGO_MD2: tagname = "md2"; break;
1483 case PGPHASHALGO_TIGER192: tagname = "tiger192"; break;
1484 case PGPHASHALGO_HAVAL_5_160: tagname = "haval160"; break;
1485 case PGPHASHALGO_SHA256: tagname = "sha256"; break;
1486 case PGPHASHALGO_SHA384: tagname = "sha384"; break;
1487 case PGPHASHALGO_SHA512: tagname = "sha512"; break;
1488 case PGPHASHALGO_MD4: tagname = "md4"; break;
1489 case PGPHASHALGO_RIPEMD128: tagname = "rmd128"; break;
1490 case PGPHASHALGO_CRC32: tagname = "crc32"; break;
1491 case PGPHASHALGO_ADLER32: tagname = "adler32"; break;
1492 case PGPHASHALGO_CRC64: tagname = "crc64"; break;
1493 case PGPHASHALGO_JLU32: tagname = "jlu32"; break;
1494 case PGPHASHALGO_SHA224: tagname = "sha224"; break;
1495 case PGPHASHALGO_RIPEMD256: tagname = "rmd256"; break;
1496 case PGPHASHALGO_RIPEMD320: tagname = "rmd320"; break;
1497 case PGPHASHALGO_SALSA10: tagname = "salsa10"; break;
1498 case PGPHASHALGO_SALSA20: tagname = "salsa20"; break;
1499 default: tagname = NULL; break;
1500 }
1501 return tagname;
1502 }
1503
1509 static const char * repoMDExpand(rpmrepo repo, rpmrfile rfile)
1510 /*@globals h_errno, rpmGlobalMacroContext, internalState @*/
1511 /*@modifies rpmGlobalMacroContext, internalState @*/
1512 {
1513 const char * spewalgo = algo2tagname(repo->algo);
1514 char spewtime[64];
1515
1516 (void) snprintf(spewtime, sizeof(spewtime), "%u", (unsigned)rfile->ctime);
1517 return rpmExpand("\
1518 <data type=\"", rfile->type, "\">\n\
1519 <checksum type=\"", spewalgo, "\">", rfile->Zdigest, "</checksum>\n\
1520 <timestamp>", spewtime, "</timestamp>\n\
1521 <open-checksum type=\"",spewalgo,"\">", rfile->digest, "</open-checksum>\n\
1522 <location href=\"", repo->finaldir, "/", rfile->type, (repo->markup != NULL ? repo->markup : ""), (repo->suffix != NULL ? repo->suffix : ""), "\"/>\n\
1523 </data>\n", NULL);
1524 }
1525
1531 static int repoDoRepoMetadata(rpmrepo repo)
1532 /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/
1533 /*@modifies repo, rpmGlobalMacroContext, fileSystem, internalState @*/
1534 {
1535 rpmrfile rfile = &repo->repomd;
1536 const char * fn = repoGetPath(repo, repo->tempdir, rfile->type, 0);
1537 int rc = 0;
1538
1539 if ((rfile->fd = Fopen(fn, "w.ufdio")) != NULL) { /* no compression */
1540 if (rfileXMLWrite(rfile, xstrdup(rfile->xml_init))
1541 || rfileXMLWrite(rfile, repoMDExpand(repo, &repo->other))
1542 || rfileXMLWrite(rfile, repoMDExpand(repo, &repo->filelists))
1543 || rfileXMLWrite(rfile, repoMDExpand(repo, &repo->primary))
1544 || rfileXMLWrite(rfile, xstrdup(rfile->xml_fini)))
1545 rc = 1;
1546 (void) repoFclose(repo, rfile->fd);
1547 rfile->fd = NULL;
1548 }
1549
1550 fn = _free(fn);
1551 if (rc) return rc;
1552
1553 #ifdef NOTYET
1554 def doRepoMetadata(self):
1555 """wrapper to generate the repomd.xml file that stores the info on the other files"""
1556 const char * repopath =
1557 rpmGetPath(repo->outputdir, "/", repo->tempdir, NULL);
1558 repodoc = libxml2.newDoc("1.0")
1559 reporoot = repodoc.newChild(None, "repomd", None)
1560 repons = reporoot.newNs("http://linux.duke.edu/metadata/repo", None)
1561 reporoot.setNs(repons)
1562 repopath = rpmGetPath(repo->outputdir, "/", repo->tempdir, NULL);
1563 fn = repoGetPath(repo, repo->tempdir, repo->repomd.type, 1);
1564
1565 repoid = "garbageid";
1566
1567 if (repo->database) {
1568 if (!repo->quiet) repo_error(0, _("Generating sqlite DBs"));
1569 try:
1570 dbversion = str(sqlitecachec.DBVERSION)
1571 except AttributeError:
1572 dbversion = "9"
1573 rp = sqlitecachec.RepodataParserSqlite(repopath, repoid, None)
1574 }
1575
1576 { static const char * types[] =
1577 { "primary", "filelists", "other", NULL };
1578 const char ** typep;
1579 for (typep = types; *typep != NULL; typep++) {
1580 complete_path = repoGetPath(repo, repo->tempdir, *typep, 1);
1581
1582 zfo = _gzipOpen(complete_path)
1583 uncsum = misc.checksum(algo2tagname(repo->algo), zfo)
1584 zfo.close()
1585 csum = misc.checksum(algo2tagname(repo->algo), complete_path)
1586 (void) rpmioExists(complete_path, st)
1587 timestamp = os.stat(complete_path)[8]
1588
1589 db_csums = {}
1590 db_compressed_sums = {}
1591
1592 if (repo->database) {
1593 if (repo->verbose) {
1594 time_t now = time(NULL);
1595 repo_error(0, _("Starting %s db creation: %s"),
1596 *typep, ctime(&now));
1597 }
1598
1599 if (!strcmp(*typep, "primary"))
1600 rp.getPrimary(complete_path, csum)
1601 else if (!strcmp(*typep, "filelists"));
1602 rp.getFilelists(complete_path, csum)
1603 else if (!strcmp(*typep, "other"))
1604 rp.getOtherdata(complete_path, csum)
1605
1606 { const char * tmp_result_path =
1607 rpmGetPath(repo->outputdir, "/", repo->tempdir, "/",
1608 *typep, ".xml.gz.sqlite", NULL);
1609 const char * resultpath =
1610 rpmGetPath(repo->outputdir, "/", repo->tempdir, "/",
1611 *typep, ".sqlite", NULL);
1612
1613 /* rename from silly name to not silly name */
1614 xx = Rename(tmp_result_path, resultpath);
1615 tmp_result_path = _free(tmp_result_path);
1616 result_compressed =
1617 rpmGetPath(repo->outputdir, "/", repo->tempdir, "/",
1618 *typep, ".sqlite.bz2", NULL);
1619 db_csums[*typep] = misc.checksum(algo2tagname(repo->algo), resultpath)
1620
1621 /* compress the files */
1622 bzipFile(resultpath, result_compressed)
1623 /* csum the compressed file */
1624 db_compressed_sums[*typep] = misc.checksum(algo2tagname(repo->algo), result_compressed)
1625 /* remove the uncompressed file */
1626 xx = Unlink(resultpath);
1627 resultpath = _free(resultpath);
1628 }
1629
1630 if (repo->uniquemdfilenames) {
1631 const char * csum_result_compressed =
1632 rpmGetPath(repo->outputdir, "/", repo->tempdir, "/",
1633 db_compressed_sums[*typep], "-", *typep, ".sqlite.bz2", NULL);
1634 xx = Rename(result_compressed, csum_result_compressed);
1635 result_compressed = _free(result_compressed);
1636 result_compressed = csum_result_compressed;
1637 }
1638
1639 /* timestamp the compressed file */
1640 (void) rpmioExists(result_compressed, st)
1641 db_timestamp = os.stat(result_compressed)[8]
1642
1643 /* add this data as a section to the repomdxml */
1644 db_data_type = rpmExpand(*typep, "_db", NULL);
1645 data = reporoot.newChild(None, 'data', None)
1646 data.newProp('type', db_data_type)
1647 location = data.newChild(None, 'location', None)
1648 if (repo->baseurl != NULL) {
1649 location.newProp('xml:base', repo->baseurl)
1650 }
1651
1652 location.newProp('href', rpmGetPath(repo->finaldir, "/", *typep, ".sqlite.bz2", NULL));
1653 checksum = data.newChild(None, 'checksum', db_compressed_sums[*typep])
1654 checksum.newProp('type', algo2tagname(repo->algo))
1655 db_tstamp = data.newChild(None, 'timestamp', str(db_timestamp))
1656 unchecksum = data.newChild(None, 'open-checksum', db_csums[*typep])
1657 unchecksum.newProp('type', algo2tagname(repo->algo))
1658 database_version = data.newChild(None, 'database_version', dbversion)
1659 if (repo->verbose) {
1660 time_t now = time(NULL);
1661 repo_error(0, _("Ending %s db creation: %s"),
1662 *typep, ctime(&now));
1663 }
1664 }
1665
1666 data = reporoot.newChild(None, 'data', None)
1667 data.newProp('type', *typep)
1668
1669 checksum = data.newChild(None, 'checksum', csum)
1670 checksum.newProp('type', algo2tagname(repo->algo))
1671 timestamp = data.newChild(None, 'timestamp', str(timestamp))
1672 unchecksum = data.newChild(None, 'open-checksum', uncsum)
1673 unchecksum.newProp('type', algo2tagname(repo->algo))
1674 location = data.newChild(None, 'location', None)
1675 if (repo->baseurl != NULL)
1676 location.newProp('xml:base', repo->baseurl)
1677 if (repo->uniquemdfilenames) {
1678 orig_file = repoGetPath(repo, repo->tempdir, *typep, strcmp(*typep, "repomd"));
1679 res_file = rpmExpand(csum, "-", *typep,
1680 (repo->markup ? repo->markup : ""),
1681 (repo->suffix && strcmp(*typep, "repomd") ? repo->suffix : ""), NULL);
1682 dest_file = rpmGetPath(repo->outputdir, "/", repo->tempdir, "/", res_file, NULL);
1683 xx = Rename(orig_file, dest_file);
1684
1685 } else
1686 res_file = rpmExpand(*typep,
1687 (repo->markup ? repo->markup : ""),
1688 (repo->suffix && strcmp(*typep, "repomd") ? repo->suffix : ""), NULL);
1689
1690 location.newProp('href', rpmGetPath(repo->finaldir, "/", res_file, NULL));
1691 }
1692 }
1693
1694 if (!repo->quiet && repo->database)
1695 repo_error(0, _("Sqlite DBs complete"));
1696
1697 if (repo->groupfile != NULL) {
1698 self.addArbitraryMetadata(repo->groupfile, 'group_gz', reporoot)
1699 self.addArbitraryMetadata(repo->groupfile, 'group', reporoot, compress=False)
1700 }
1701
1702 /* save it down */
1703 try:
1704 repodoc.saveFormatFileEnc(fn, 'UTF-8', 1)
1705 except:
1706 repo_error(0, _("Error saving temp file for %s%s%s: %s"),
1707 rfile->type,
1708 (repo->markup ? repo->markup : ""),
1709 (repo->suffix && strcmp(*typep, "repomd") ? repo->suffix : ""),
1710 fn);
1711 repo_error(1, _("Could not save temp file: %s"), fn);
1712
1713 del repodoc
1714 #endif
1715
1716 return rc;
1717 }
1718
1724 static int repoDoFinalMove(rpmrepo repo)
1725 /*@globals h_errno, rpmGlobalMacroContext, fileSystem, internalState @*/
1726 /*@modifies rpmGlobalMacroContext, fileSystem, internalState @*/
1727 {
1728 const char * output_final_dir =
1729 rpmGetPath(repo->outputdir, "/", repo->finaldir, NULL);
1730 const char * output_old_dir =
1731 rpmGetPath(repo->outputdir, "/", repo->olddir, NULL);
1732 const char * oldfile;
1733 struct stat sb, *st = &sb;
1734 int xx;
1735
1736 if (rpmioExists(output_final_dir, st)) {
1737 if ((xx = Rename(output_final_dir, output_old_dir)) != 0)
1738 repo_error(1, _("Error moving final %s to old dir %s"),
1739 output_final_dir, output_old_dir);
1740 }
1741
1742 { const char * output_temp_dir =
1743 rpmGetPath(repo->outputdir, "/", repo->tempdir, NULL);
1744 if ((xx = Rename(output_temp_dir, output_final_dir)) != 0) {
1745 xx = Rename(output_old_dir, output_final_dir);
1746 repo_error(1, _("Error moving final metadata into place"));
1747 }
1748 output_temp_dir = _free(output_temp_dir);
1749 }
1750
1751 { /*@observer@*/
1752 static const char * types[] =
1753 { "primary", "filelists", "other", "repomd", "group", NULL };
1754 const char ** typep;
1755
1756 for (typep = types; *typep != NULL; typep++) {
1757 oldfile = rpmGetPath(output_old_dir, "/", *typep,
1758 (repo->markup != NULL ? repo->markup : ""),
1759 (repo->suffix != NULL && strcmp(*typep, "repomd")
1760 ? repo->suffix : ""), NULL);
1761 if (rpmioExists(oldfile, st)) {
1762 if (Unlink(oldfile))
1763 repo_error(1, _("Could not remove old metadata file: %s: %s"),
1764 oldfile, strerror(errno));
1765 }
1766 oldfile = _free(oldfile);
1767 }
1768 }
1769
1770 { DIR * dir = Opendir(output_old_dir);
1771 struct dirent * dp;
1772
1773 if (dir != NULL) {
1774 while ((dp = Readdir(dir)) != NULL) {
1775 const char * finalfile;
1776
1777 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1778 continue;
1779
1780 finalfile = rpmGetPath(output_final_dir, "/", dp->d_name, NULL);
1781 oldfile = rpmGetPath(output_old_dir, "/", dp->d_name, NULL);
1782
1783 if (!strcmp(dp->d_name, "filelists.sqlite.bz2")
1784 || !strcmp(dp->d_name, "other.sqlite.bz2")
1785 || !strcmp(dp->d_name, "primary.sqlite.bz2"))
1786 {
1787 xx = Unlink(oldfile);
1788 oldfile = _free(oldfile);
1789 continue;
1790 }
1791
1792 if (rpmioExists(finalfile, st)) {
1793 if (!S_ISDIR(st->st_mode)) {
1794 if ((xx = Unlink(oldfile)) != 0)
1795 repo_error(1, _("Could not remove old metadata file: %s: %s"),
1796 oldfile, strerror(errno));
1797 }
1798 #ifdef NOTYET
1799 else {
1800 shutil.rmtree(oldfile)
1801 }
1802 #endif
1803 } else {
1804 if ((xx = Rename(oldfile, finalfile)) != 0) {
1805 repo_error(1, _("Could not restore old non-metadata file: %s -> %s: %s"),
1806 oldfile, finalfile, strerror(errno));
1807 }
1808 }
1809 oldfile = _free(oldfile);
1810 finalfile = _free(finalfile);
1811 }
1812 xx = Closedir(dir);
1813 }
1814 }
1815
1816 if ((xx = Rmdir(output_old_dir)) != 0) {
1817 repo_error(1, _("Could not remove old metadata dir: %s: %s"),
1818 repo->olddir, strerror(errno));
1819 }
1820 output_old_dir = _free(output_old_dir);
1821 output_final_dir = _free(output_final_dir);
1822
1823 return 0;
1824 }
1825
1826 /*==============================================================*/
1827
1830 static void repoArgCallback(poptContext con,
1831 /*@unused@*/ enum poptCallbackReason reason,
1832 const struct poptOption * opt, /*@unused@*/ const char * arg,
1833 /*@unused@*/ void * data)
1834 /*@globals _rpmrepo, fileSystem, internalState @*/
1835 /*@modifies _rpmrepo, fileSystem, internalState @*/
1836 {
1837 rpmrepo repo = _rpmrepo;
1838
1839 /* XXX avoid accidental collisions with POPT_BIT_SET for flags */
1840 if (opt->arg == NULL)
1841 switch (opt->val) {
1842
1843 case 'v': /* --verbose */
1844 repo->verbose++;
1845 break;
1846 case '?':
1847 default:
1848 fprintf(stderr, _("%s: Unknown option -%c\n"), __progname, opt->val);
1849 poptPrintUsage(con, stderr, 0);
1850 exit(EXIT_FAILURE);
1851 /*@notreached@*/ break;
1852 }
1853 }
1854
1855 /*@unchecked@*/
1856 static int compression = -1;
1857
1858 /*@unchecked@*/ /*@observer@*/
1859 static struct poptOption repoCompressionPoptTable[] = {
1860 { "uncompressed", '\0', POPT_ARG_VAL, &compression, 0,
1861 N_("don't compress"), NULL },
1862 { "gzip", 'Z', POPT_ARG_VAL, &compression, 1,
1863 N_("use gzip compression"), NULL },
1864 { "bzip2", '\0', POPT_ARG_VAL, &compression, 2,
1865 N_("use bzip2 compression"), NULL },
1866 { "lzma", '\0', POPT_ARG_VAL, &compression, 3,
1867 N_("use lzma compression"), NULL },
1868 { "xz", '\0', POPT_ARG_VAL, &compression, 4,
1869 N_("use xz compression"), NULL },
1870 POPT_TABLEEND
1871 };
1872
1873 /*@unchecked@*/ /*@observer@*/
1874 static struct poptOption optionsTable[] = {
1875 /*@-type@*/ /* FIX: cast? */
1876 { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA | POPT_CBFLAG_CONTINUE,
1877 repoArgCallback, 0, NULL, NULL },
1878 /*@=type@*/
1879
1880 { "repodebug", '\0', POPT_ARG_VAL|POPT_ARGFLAG_DOC_HIDDEN, &_repo_debug, -1,
1881 N_("debug repo handling"), NULL },
1882
1883 { "quiet", 'q', POPT_ARG_VAL, &__rpmrepo.quiet, 0,
1884 N_("output nothing except for serious errors"), NULL },
1885 { "verbose", 'v', 0, NULL, (int)'v',
1886 N_("output more debugging info."), NULL },
1887 { "dryrun", '\0', POPT_ARG_VAL, &__rpmrepo.dryrun, 1,
1888 N_("sanity check arguments, don't create metadata"), NULL },
1889 { "excludes", 'x', POPT_ARG_ARGV, &__rpmrepo.exclude_patterns, 0,
1890 N_("glob PATTERN(s) to exclude"), N_("PATTERN") },
1891 { "includes", 'i', POPT_ARG_ARGV, &__rpmrepo.include_patterns, 0,
1892 N_("glob PATTERN(s) to include"), N_("PATTERN") },
1893 { "basedir", '\0', POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN, &__rpmrepo.basedir, 0,
1894 N_("top level directory"), N_("DIR") },
1895 { "baseurl", 'u', POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN, &__rpmrepo.baseurl, 0,
1896 N_("baseurl to append on all files"), N_("BASEURL") },
1897 #ifdef NOTYET
1898 { "groupfile", 'g', POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN, &__rpmrepo.groupfile, 0,
1899 N_("path to groupfile to include in metadata"), N_("FILE") },
1900 #endif
1901 { "pretty", 'p', POPT_ARG_VAL|POPT_ARGFLAG_DOC_HIDDEN, &__rpmrepo.pretty, 1,
1902 N_("make sure all xml generated is formatted"), NULL },
1903 { "checkts", 'C', POPT_ARG_VAL|POPT_ARGFLAG_DOC_HIDDEN, &__rpmrepo.checkts, 1,
1904 N_("check timestamps on files vs the metadata to see if we need to update"), NULL },
1905 #if defined(WITH_SQLITE)
1906 { "database", 'd', POPT_ARG_VAL, &__rpmrepo.database, 1,
1907 N_("create sqlite3 database files"), NULL },
1908 #endif
1909 { "split", '\0', POPT_ARG_VAL|POPT_ARGFLAG_DOC_HIDDEN, &__rpmrepo.split, 1,
1910 N_("generate split media"), NULL },
1911 { "pkglist", 'l', POPT_ARG_ARGV|POPT_ARGFLAG_DOC_HIDDEN, &__rpmrepo.manifests, 0,
1912 N_("use only the files listed in this file from the directory specified"), N_("FILE") },
1913 { "outputdir", 'o', POPT_ARG_STRING, &__rpmrepo.outputdir, 0,
1914 N_("<dir> = optional directory to output to"), N_("DIR") },
1915 { "skip-symlinks", 'S', POPT_ARG_VAL, &__rpmrepo.nofollow, 1,
1916 N_("ignore symlinks of packages"), NULL },
1917 { "unique-md-filenames", '\0', POPT_ARG_VAL|POPT_ARGFLAG_DOC_HIDDEN, &__rpmrepo.uniquemdfilenames, 1,
1918 N_("include the file's checksum in the filename, helps with proxies"), NULL },
1919
1920 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioFtsPoptTable, 0,
1921 N_("Fts(3) traversal options:"), NULL },
1922
1923 #ifdef NOTYET
1924 { "version", '\0', 0, NULL, POPT_SHOWVERSION,
1925 N_("print the version"), NULL },
1926 #endif
1927
1928 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, repoCompressionPoptTable, 0,
1929 N_("Available compressions:"), NULL },
1930
1931 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioDigestPoptTable, 0,
1932 N_("Available digests:"), NULL },
1933
1934 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioAllPoptTable, 0,
1935 N_("Common options for all rpmio executables:"),
1936 NULL },
1937
1938 POPT_AUTOALIAS
1939 POPT_AUTOHELP
1940 POPT_TABLEEND
1941
1942 };
1943
1944 int
1945 main(int argc, char *argv[])
1946 /*@globals _rpmrepo,
1947 rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
1948 /*@modifies _rpmrepo,
1949 rpmGlobalMacroContext, fileSystem, internalState @*/
1950 {
1951 rpmrepo repo = _rpmrepo;
1952 poptContext optCon;
1953 const char ** av = NULL;
1954 int ndirs = 0;
1955 int nfiles = 0;
1956 int rc = 1; /* assume failure. */
1957 int xx;
1958 int i;
1959
1960 #if !defined(__LCLINT__) /* XXX force "rpmrepo" name. */
1961 __progname = "rpmrepo";
1962 #endif
1963
1964 /* Process options. */
1965 optCon = rpmioInit(argc, argv, optionsTable);
1966
1967 repo->ftsoptions = (rpmioFtsOpts ? rpmioFtsOpts : FTS_PHYSICAL);
1968 switch (repo->ftsoptions & (FTS_LOGICAL|FTS_PHYSICAL)) {
1969 case (FTS_LOGICAL|FTS_PHYSICAL):
1970 repo_error(1, "FTS_LOGICAL and FTS_PYSICAL are mutually exclusive");
1971 /*@notreached@*/ break;
1972 case 0:
1973 repo->ftsoptions |= FTS_PHYSICAL;
1974 break;
1975 }
1976
1977 repo->algo = (rpmioDigestHashAlgo >= 0 ? (rpmioDigestHashAlgo & 0xff) : PGPHASHALGO_SHA1);
1978
1979 repo->compression = (compression >= 0 ? compression : 1);
1980 switch (repo->compression) {
1981 case 0:
1982 repo->suffix = NULL;
1983 repo->wmode = "w.ufdio";
1984 break;
1985 default:
1986 /*@fallthrough@*/
1987 case 1:
1988 repo->suffix = ".gz";
1989 repo->wmode = "w9.gzdio";
1990 break;
1991 case 2:
1992 repo->suffix = ".bz2";
1993 repo->wmode = "w9.bzdio";
1994 break;
1995 case 3:
1996 repo->suffix = ".lzma";
1997 repo->wmode = "w.lzdio";
1998 break;
1999 case 4:
2000 repo->suffix = ".xz";
2001 repo->wmode = "w.xzdio";
2002 break;
2003 }
2004
2005 av = poptGetArgs(optCon);
2006 if (av == NULL || av[0] == NULL) {
2007 repo_error(0, _("Must specify path(s) to index."));
2008 poptPrintUsage(optCon, stderr, 0);
2009 goto exit;
2010 }
2011
2012 if (av != NULL)
2013 for (i = 0; av[i] != NULL; i++) {
2014 char fullpath[MAXPATHLEN];
2015 struct stat sb;
2016 const char * rpath;
2017 const char * lpath = NULL;
2018 int ut = urlPath(av[i], &lpath);
2019 size_t nb = (size_t)(lpath - av[i]);
2020 int isdir = (lpath[strlen(lpath)-1] == '/');
2021
2022 /* Convert to absolute/clean/malloc'd path. */
2023 if (lpath[0] != '/') {
2024 if ((rpath = repoRealpath(lpath)) == NULL)
2025 repo_error(1, _("Realpath(%s): %s"), lpath, strerror(errno));
2026 lpath = rpmGetPath(rpath, NULL);
2027 rpath = _free(rpath);
2028 } else
2029 lpath = rpmGetPath(lpath, NULL);
2030
2031 /* Reattach the URI to the absolute/clean path. */
2032 /* XXX todo: rpmGenPath was confused by file:///path/file URI's. */
2033 switch (ut) {
2034 case URL_IS_DASH:
2035 case URL_IS_UNKNOWN:
2036 rpath = lpath;
2037 lpath = NULL;
2038 /*@switchbreak@*/ break;
2039 default:
2040 assert(nb < sizeof(fullpath));
2041 strncpy(fullpath, av[i], nb);
2042 fullpath[nb] = '\0';
2043 rpath = rpmGenPath(fullpath, lpath, NULL);
2044 lpath = _free(lpath);
2045 /*@switchbreak@*/ break;
2046 }
2047
2048 /* Add a trailing '/' on directories. */
2049 lpath = (isdir || (!Stat(rpath, &sb) && S_ISDIR(sb.st_mode))
2050 ? "/" : NULL);
2051 if (lpath != NULL) {
2052 lpath = rpmExpand(rpath, lpath, NULL);
2053 xx = argvAdd(&repo->directories, lpath);
2054 lpath = _free(lpath);
2055 ndirs++;
2056 } else {
2057 xx = argvAdd(&repo->pkglist, rpath);
2058 nfiles++;
2059 }
2060 rpath = _free(rpath);
2061 }
2062
2063 if (_repo_debug || repo->dryrun)
2064 argvPrint("repo->directories", repo->directories, NULL);
2065
2066 #ifdef NOTYET
2067 if (repo->basedir == NULL)
2068 repo->basedir = xstrdup(repo->directories[0]);
2069 #endif
2070
2071 if (repo->outputdir == NULL) {
2072 if (repo->directories != NULL && repo->directories[0] != NULL)
2073 repo->outputdir = xstrdup(repo->directories[0]);
2074 else {
2075 repo->outputdir = repoRealpath(".");
2076 if (repo->outputdir == NULL)
2077 repo_error(1, _("Realpath(%s): %s"), ".", strerror(errno));
2078 }
2079 }
2080
2081 if (repo->split && repo->checkts)
2082 repo_error(1, _("--split and --checkts options are mutually exclusive"));
2083
2084 #ifdef NOTYET
2085 /* Add manifest(s) contents to rpm list. */
2086 if (repo->manifests != NULL) {
2087 const char ** av = repo->manifests;
2088 const char * fn;
2089 /* Load the rpm list from manifest(s). */
2090 while ((fn = *av++) != NULL) {
2091 /* XXX todo: parse paths from files. */
2092 /* XXX todo: convert to absolute paths. */
2093 /* XXX todo: check for existence. */
2094 xx = argvAdd(&repo->pkglist, fn);
2095 }
2096 }
2097 #endif
2098
2099 /* Set up mire patterns (no error returns with globs, easy pie). */
2100 if (mireLoadPatterns(RPMMIRE_GLOB, 0, repo->exclude_patterns, NULL,
2101 &repo->excludeMire, &repo->nexcludes))
2102 repo_error(1, _("Error loading exclude glob patterns."));
2103 if (mireLoadPatterns(RPMMIRE_GLOB, 0, repo->include_patterns, NULL,
2104 &repo->includeMire, &repo->nincludes))
2105 repo_error(1, _("Error loading include glob patterns."));
2106
2107 /* Load the rpm list from a multi-rooted directory traversal. */
2108 if (repo->directories != NULL) {
2109 ARGV_t pkglist = repoGetFileList(repo, repo->directories, ".rpm");
2110 xx = argvAppend(&repo->pkglist, pkglist);
2111 pkglist = argvFree(pkglist);
2112 }
2113
2114 /* XXX todo: check for duplicates in repo->pkglist? */
2115 xx = argvSort(repo->pkglist, NULL);
2116
2117 if (_repo_debug || repo->dryrun)
2118 argvPrint("repo->pkglist", repo->pkglist, NULL);
2119
2120 repo->pkgcount = argvCount(repo->pkglist);
2121
2122 /* XXX enable --stats using transaction set. */
2123 _rpmts_stats = _rpmsw_stats;
2124 repo->ts = rpmtsCreate();
2125
2126 /* XXX todo wire up usual rpm CLI options. hotwire --nosignature for now */
2127 (void) rpmtsSetVSFlags(repo->ts, _RPMVSF_NOSIGNATURES);
2128
2129 rc = repoTestSetupDirs(repo);
2130
2131 if (rc || repo->dryrun)
2132 goto exit;
2133
2134 if (!repo->split) {
2135 rc = repoCheckTimeStamps(repo);
2136 if (rc == 0) {
2137 fprintf(stdout, _("repo is up to date\n"));
2138 goto exit;
2139 }
2140 }
2141
2142 if ((rc = repoDoPkgMetadata(repo)) != 0)
2143 goto exit;
2144 if ((rc = repoDoRepoMetadata(repo)) != 0)
2145 goto exit;
2146 if ((rc = repoDoFinalMove(repo)) != 0)
2147 goto exit;
2148
2149 exit:
2150 (void)rpmtsFree(repo->ts);
2151 repo->ts = NULL;
2152 repo->primary.digest = _free(repo->primary.digest);
2153 repo->primary.Zdigest = _free(repo->primary.Zdigest);
2154 repo->filelists.digest = _free(repo->filelists.digest);
2155 repo->filelists.Zdigest = _free(repo->filelists.Zdigest);
2156 repo->other.digest = _free(repo->other.digest);
2157 repo->other.Zdigest = _free(repo->other.Zdigest);
2158 repo->repomd.digest = _free(repo->repomd.digest);
2159 repo->repomd.Zdigest = _free(repo->repomd.Zdigest);
2160 repo->outputdir = _free(repo->outputdir);
2161 repo->pkglist = argvFree(repo->pkglist);
2162 repo->directories = argvFree(repo->directories);
2163 repo->manifests = argvFree(repo->manifests);
2164 /*@-onlytrans -refcounttrans @*/
2165 repo->excludeMire = mireFreeAll(repo->excludeMire, repo->nexcludes);
2166 repo->includeMire = mireFreeAll(repo->includeMire, repo->nincludes);
2167 /*@=onlytrans =refcounttrans @*/
2168 repo->exclude_patterns = argvFree(repo->exclude_patterns);
2169 repo->include_patterns = argvFree(repo->include_patterns);
2170
2171 tagClean(NULL);
2172 optCon = rpmioFini(optCon);
2173
2174 return rc;
2175 }