rpm  5.2.1
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@*/
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@*/
129 /*@relnull@*/
132 /*@null@*/
134 /*@relnull@*/
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@*/
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 
168 
169 /*@null@*/
171 /*@null@*/
173  unsigned current;
174  unsigned pkgcount;
175 
176 /*@null@*/
179  uint32_t pkgalgo;
180  uint32_t algo;
182 /*@observer@*/
183  const char * markup;
184 /*@observer@*/ /*@null@*/
185  const char * suffix;
186 /*@observer@*/
187  const char * wmode;
188 
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);
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 
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. */
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 @*/
2170 
2171  tagClean(NULL);
2172  optCon = rpmioFini(optCon);
2173 
2174  return rc;
2175 }