2 * Copyright (c) 2018, SUSE LLC.
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
22 #include "repo_rpmdb.h"
26 #include "repo_repomdxml.h"
27 #include "repo_rpmmd.h"
28 #include "repo_updateinfoxml.h"
29 #include "repo_deltainfoxml.h"
32 #ifdef ENABLE_SUSEREPO
33 #include "repo_content.h"
34 #include "repo_susetags.h"
38 #include "repo_autopattern.h"
41 #include "repo_appdata.h"
43 #include "common_write.h"
44 #include "solv_xfopen.h"
55 int add_changelog = 0;
56 int filtered_filelist = 0;
59 #define REPO_PLAINDIR 1
61 #define REPO_RPMMD_REPODATA 3
62 #define REPO_SUSETAGS 4
65 autodetect_repotype(Pool *pool, const char *dir)
71 tmp = pool_tmpjoin(pool, dir, "/repomd.xml", 0);
72 if (stat(tmp, &stb) == 0)
74 tmp = pool_tmpjoin(pool, dir, "/repodata/repomd.xml", 0);
75 if (stat(tmp, &stb) == 0)
76 return REPO_RPMMD_REPODATA;
77 tmp = pool_tmpjoin(pool, dir, "/content", 0);
78 if ((fp = fopen(tmp, "r")) != 0)
80 char buf[512], *descrdir = 0;
81 while (fgets(buf, sizeof(buf), fp))
85 if (buf[l - 1] != '\n')
88 while ((c = getc(fp)) != EOF && c != '\n')
92 while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
95 while (*bp == ' ' || *bp == '\t')
97 if (strncmp(bp, "DESCRDIR", 8) != 0 || (bp[8] != ' ' && bp[8] != '\t'))
100 while (*bp == ' ' || *bp == '\t')
108 tmp = pool_tmpjoin(pool, dir, "/", descrdir);
109 if (stat(tmp, &stb) == 0 && S_ISDIR(stb.st_mode))
110 return REPO_SUSETAGS;
113 tmp = pool_tmpjoin(pool, dir, "/suse/setup/descr", 0);
114 if (stat(tmp, &stb) == 0 && S_ISDIR(stb.st_mode))
115 return REPO_SUSETAGS;
116 return REPO_PLAINDIR;
123 read_plaindir_repo(Repo *repo, const char *dir)
125 Pool *pool = repo->pool;
139 /* run find command */
145 while ((pid = fork()) == (pid_t)-1)
164 if (dup2(fds[1], 1) == -1)
172 execl("/usr/bin/find", "/usr/bin/find", ".", "-name", ".", "-o", "-name", ".*", "-prune", "-o", "-name", "*.delta.rpm", "-o", "-name", "*.patch.rpm", "-o", "-name", "*.rpm", "-a", "-type", "f", "-print0", (char *)0);
174 execl("/usr/bin/find", "/usr/bin/find", ".", "-maxdepth", "1", "-name", ".", "-o", "-name", ".*", "-prune", "-o", "-name", "*.delta.rpm", "-o", "-name", "*.patch.rpm", "-o", "-name", "*.rpm", "-a", "-type", "f", "-print0", (char *)0);
175 perror("/usr/bin/find");
179 if ((fp = fdopen(fds[0], "r")) == 0)
184 data = repo_add_repodata(repo, 0);
186 while ((c = getc(fp)) != EOF)
190 size_t len = bp - buf;
191 buf = solv_realloc(buf, len + 4096);
199 rpm = solv_dupjoin(dir, "/", bp[0] == '.' && bp[1] == '/' ? bp + 2 : bp);
200 if ((p = repo_add_rpm(repo, rpm, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION|(filtered_filelist ? RPM_ADD_FILTERED_FILELIST : 0))) == 0)
202 fprintf(stderr, "%s: %s\n", rpm, pool_errstr(pool));
208 repodata_set_location(data, p, 0, 0, bp[0] == '.' && bp[1] == '/' ? bp + 2 : bp);
213 while (waitpid(pid, &wstatus, 0) == -1)
222 fprintf(stderr, "find: exit status %d\n", (wstatus >> 8) | (wstatus & 255) << 8);
227 repo_internalize(repo);
234 read_plaindir_repo(Repo *repo, const char *dir)
236 fprintf(stderr, "plaindir repo type is not supported\n");
242 #ifdef ENABLE_SUSEREPO
245 susetags_find(char **files, int nfiles, const char *what)
248 size_t l = strlen(what);
249 for (i = 0; i < nfiles; i++)
252 if (strncmp(fn, what, l) != 0)
258 if (strchr(fn + l + 1, '.') != 0)
260 if (solv_xfopen_iscompressed(fn) <= 0)
268 susetags_open(const char *dir, const char *filename, char **tmpp, int missingok)
276 *tmpp = solv_dupjoin(dir, "/", filename);
277 if ((fp = solv_xfopen(*tmpp, "r")) == 0)
284 *tmpp = solv_free(*tmpp);
291 susetags_extend(Repo *repo, const char *dir, char **files, int nfiles, char *what, Id defvendor, char *language, int missingok)
293 const char *filename;
297 filename = susetags_find(files, nfiles, what);
300 if ((fp = susetags_open(dir, filename, &tmp, missingok)) != 0)
302 if (repo_add_susetags(repo, fp, defvendor, language, REPO_EXTEND_SOLVABLES))
304 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(repo->pool));
313 susetags_extend_languages(Repo *repo, const char *dir, char **files, int nfiles, Id defvendor, int missingok)
316 for (i = 0; i < nfiles; i++)
320 if (strncmp(fn, "packages.", 9) != 0)
322 if (strlen(fn + 9) + 1 >= sizeof(lang))
324 strncpy(lang, fn + 9, sizeof(lang) - 1);
325 lang[sizeof(lang) - 1] = 0;
326 p = strrchr(lang, '.');
329 if (solv_xfopen_iscompressed(lang) <= 0)
333 if (strchr(lang, '.'))
335 if (!strcmp(lang, "en"))
336 continue; /* already did that one */
337 if (!strcmp(lang, "DU"))
338 continue; /* disk usage */
339 if (!strcmp(lang, "FL"))
340 continue; /* file list */
341 if (!strcmp(lang, "DL"))
342 continue; /* deltas */
343 susetags_extend(repo, dir, files, nfiles, fn, defvendor, lang, missingok);
348 susetags_dircmp(const void *ap, const void *bp, void *dp)
350 return strcmp(*(const char **)ap, *(const char **)bp);
354 read_susetags_repo(Repo *repo, const char *dir)
356 Pool *pool = repo->pool;
357 const char *filename;
362 const char *descrdir = 0;
368 /* read content file */
369 repo_add_repodata(repo, 0);
370 tmp = solv_dupjoin(dir, "/content", 0);
371 if ((fp = fopen(tmp, "r")) != 0)
373 if (repo_add_content(repo, fp, REPO_REUSE_REPODATA))
375 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
379 descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
380 defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
383 descrdir = "suse/setup/descr";
384 tmp = solv_free(tmp);
386 /* get content of descrdir directory */
387 ddir = solv_dupjoin(dir, "/", descrdir);
388 if ((dp = opendir(ddir)) == 0)
393 while ((de = readdir(dp)) != 0)
395 if (de->d_name[0] == 0 || de->d_name[0] == '.')
397 files = solv_extend(files, nfiles, 1, sizeof(char *), 63);
398 files[nfiles++] = solv_strdup(de->d_name);
402 solv_sort(files, nfiles, sizeof(char *), susetags_dircmp, 0);
405 filename = susetags_find(files, nfiles, "packages");
406 if (filename && (fp = susetags_open(ddir, filename, &tmp, 1)) != 0)
408 if (repo_add_susetags(repo, fp, defvendor, 0, SUSETAGS_RECORD_SHARES))
410 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
414 tmp = solv_free(tmp);
416 /* now extend the packages */
417 susetags_extend(repo, ddir, files, nfiles, "packages.DU", defvendor, 0, 1);
418 susetags_extend(repo, ddir, files, nfiles, "packages.en", defvendor, 0, 1);
419 susetags_extend_languages(repo, ddir, files, nfiles, defvendor, 1);
421 susetags_extend(repo, ddir, files, nfiles, "packages.FL", defvendor, 0, 1);
425 filename = susetags_find(files, nfiles, "packages.DL");
426 if (filename && (fp = susetags_open(ddir, filename, &tmp, 1)) != 0)
428 if (repo_add_susetags(repo, fp, defvendor, 0, 0))
430 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
434 tmp = solv_free(tmp);
437 /* add legacy patterns */
438 tmp = solv_dupjoin(ddir, "/patterns", 0);
439 if ((fp = fopen(tmp, "r")) != 0)
443 repo_add_repodata(repo, 0);
444 while (fgets(pbuf, sizeof(pbuf), fp))
448 if (strchr(pbuf, '/') != 0)
450 if ((p = strchr(pbuf, '\n')) != 0)
455 tmp = solv_dupjoin(ddir, "/", pbuf);
456 if ((pfp = solv_xfopen(tmp, "r")) != 0)
458 if (repo_add_susetags(repo, pfp, defvendor, 0, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA))
460 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
468 tmp = solv_free(tmp);
470 #ifdef ENABLE_APPDATA
472 filename = add_appdata ? susetags_find(files, nfiles, "appdata.xml") : 0;
473 if (filename && (fp = susetags_open(ddir, filename, &tmp, 1)) != 0)
475 if (repo_add_appdata(repo, fp, 0))
477 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
481 tmp = solv_free(tmp);
486 solv_free(files[--nfiles]);
489 repo_internalize(repo);
496 read_susetags_repo(Repo *repo, const char *dir)
498 fprintf(stderr, "susetags repo type is not supported\n");
508 repomd_find(Repo *repo, const char *what)
510 Pool *pool = repo->pool;
512 const char *filename;
515 dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
516 dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
517 if (dataiterator_step(&di))
519 dataiterator_setpos_parent(&di);
520 filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
522 dataiterator_free(&di);
523 if (filename && strncmp(filename, "repodata/", 9) == 0)
529 repomd_open(const char *dir, const char *filename, char **tmpp, int missingok)
537 *tmpp = solv_dupjoin(dir, "/", filename);
538 if ((fp = solv_xfopen(*tmpp, "r")) == 0)
545 *tmpp = solv_free(*tmpp);
552 repomd_extend(Repo *repo, const char *dir, const char *what, const char *language, int missingok)
554 const char *filename;
558 filename = repomd_find(repo, what);
561 fp = repomd_open(dir, filename, &tmp, missingok);
564 if (repo_add_rpmmd(repo, fp, language, REPO_EXTEND_SOLVABLES))
566 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(repo->pool));
575 repomd_extend_languages(Repo *repo, const char *dir, int missingok)
577 char **susedatas = 0;
578 int nsusedatas = 0, i;
580 dataiterator_init(&di, repo->pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, "susedata.", SEARCH_STRINGSTART);
581 dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
582 while (dataiterator_step(&di))
584 susedatas = solv_extend(susedatas, nsusedatas, 1, sizeof(char *), 15);
585 susedatas[nsusedatas++] = solv_strdup(di.kv.str);
587 dataiterator_free(&di);
588 for (i = 0; i < nsusedatas; i++)
590 repomd_extend(repo, dir, susedatas[i], susedatas[i] + 9, missingok);
591 susedatas[i] = solv_free(susedatas[i]);
593 solv_free(susedatas);
597 add_rpmmd_file(Repo *repo, const char *dir, const char *filename, int missingok)
602 fp = repomd_open(dir, filename, &tmp, missingok);
605 if (repo_add_rpmmd(repo, fp, 0, 0))
607 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(repo->pool));
615 read_rpmmd_repo(Repo *repo, const char *dir)
617 Pool *pool = repo->pool;
620 const char *filename;
622 /* add repomd.xml and suseinfo.xml */
623 fp = repomd_open(dir, "repomd.xml", &tmp, 0);
624 if (repo_add_repomdxml(repo, fp, 0))
626 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
630 tmp = solv_free(tmp);
631 filename = repomd_find(repo, "suseinfo");
632 if (filename && (fp = repomd_open(dir, filename, &tmp, 0)) != 0)
634 if (repo_add_repomdxml(repo, fp, REPO_REUSE_REPODATA))
636 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
640 tmp = solv_free(tmp);
643 /* first all primary packages */
644 filename = repomd_find(repo, "primary");
647 add_rpmmd_file(repo, dir, filename, 0);
648 repomd_extend(repo, dir, "susedata", 0, 1);
649 repomd_extend_languages(repo, dir, 1);
651 repomd_extend(repo, dir, "filelists", 0, 1);
653 repomd_extend(repo, dir, "other", 0, 1);
656 /* some legacy stuff */
657 filename = repomd_find(repo, "products");
659 filename = repomd_find(repo, "product");
661 add_rpmmd_file(repo, dir, filename, 1);
662 filename = repomd_find(repo, "patterns");
663 add_rpmmd_file(repo, dir, filename, 1);
666 filename = repomd_find(repo, "updateinfo");
667 if (filename && (fp = repomd_open(dir, filename, &tmp, 0)) != 0)
669 if (repo_add_updateinfoxml(repo, fp, 0))
671 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
675 tmp = solv_free(tmp);
679 filename = repomd_find(repo, "deltainfo");
681 filename = repomd_find(repo, "prestodelta");
682 if (filename && (fp = repomd_open(dir, filename, &tmp, 1)) != 0)
684 if (repo_add_deltainfoxml(repo, fp, 0))
686 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
690 tmp = solv_free(tmp);
693 #ifdef ENABLE_APPDATA
695 filename = add_appdata ? repomd_find(repo, "appdata") : 0;
696 if (filename && (fp = repomd_open(dir, filename, &tmp, 1)) != 0)
698 if (repo_add_appdata(repo, fp, 0))
700 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
704 tmp = solv_free(tmp);
708 repo_internalize(repo);
715 read_rpmmd_repo(Repo *repo, const char *dir)
717 fprintf(stderr, "rpmmd repo type is not supported\n");
726 fprintf(stderr, "\nUsage:\n"
727 "repo2solv [-R] [-X] [-A] [-o <out.solv>] <dir>\n"
728 " Convert a repository in <dir> to a solv file\n"
729 " -h : print help & exit\n"
730 " -o <out.solv>: write to this file instead of stdout\n"
731 " -F : add filelist\n"
732 " -R : also search subdirectories for rpms\n"
733 " -X : generate pattern/product pseudo packages\n"
734 " -A : add appdata packages\n"
740 main(int argc, char **argv)
748 Pool *pool = pool_create();
749 Repo *repo = repo_create(pool, "<repo>");
751 while ((c = getopt(argc, argv, "hAXRFCo:")) >= 0)
764 #ifdef ENABLE_APPDATA
769 repotype = REPO_PLAINDIR;
786 if (optind + 1 != argc)
794 if (!S_ISDIR(stb.st_mode))
796 fprintf(stderr, "%s: not a directory\n", dir);
799 dir = solv_strdup(dir);
801 repotype = autodetect_repotype(pool, dir);
806 res = read_rpmmd_repo(repo, dir);
808 case REPO_RPMMD_REPODATA:
809 dir = solv_dupappend(dir, "/repodata", 0);
810 res = read_rpmmd_repo(repo, dir);
813 res = read_susetags_repo(repo, dir);
816 res = read_plaindir_repo(repo, dir);
819 fprintf(stderr, "unknown repotype %d\n", repotype);
822 if (outfile && freopen(outfile, "w", stdout) == 0)
829 repo_add_autopattern(repo, 0);
831 tool_write(repo, 0, 0);