Rewrite depsolver to use a series of passes over all packages.
The big change is that we follow one step of the depedency chain for
each package to resolve in each iteration, and repeat until there are
no more possible moves. In contrast the old depsolver would try to
follow the dependency chain completely for one package at a time.
This new approach is simpler and faster, and at the same time more
roboust. Instead of knowing how one newly installed package may
affect other packages (obsoleting, pulling in new packages etc), the
new algorithm just looks at the total list of requires, provides,
obsoletes and conflicts after installing new packages.
2 * Copyright (C) 2008 Kristian Høgsberg <krh@redhat.com>
3 * Copyright (C) 2008 Red Hat, Inc
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include <curl/curl.h>
33 #include "razor-internal.h"
35 static const char system_repo_filename[] = "system.repo";
36 static const char next_repo_filename[] = "system-next.repo";
37 static const char rawhide_repo_filename[] = "rawhide.repo";
38 static const char updated_repo_filename[] = "system-updated.repo";
39 static const char razor_root_path[] = "/var/lib/razor";
40 static const char root[] = "install";
41 static const char *repo_filename = system_repo_filename;
44 command_list(int argc, const char *argv[])
46 struct razor_set *set;
47 struct razor_package_iterator *pi;
48 struct razor_package *package;
49 const char *pattern, *name, *version, *arch;
50 int only_names = 0, i = 0;
52 if (i < argc && strcmp(argv[i], "--only-names") == 0) {
58 set = razor_set_open(repo_filename);
59 pi = razor_package_iterator_create(set);
60 while (razor_package_iterator_next(pi, &package,
61 &name, &version, &arch)) {
62 if (pattern && fnmatch(pattern, name, 0) != 0)
68 printf("%s-%s.%s\n", name, version, arch);
70 razor_package_iterator_destroy(pi);
71 razor_set_destroy(set);
77 list_properties(const char *package_name,
78 enum razor_property_type required_type)
80 static const char *relation_string[] = { "<", "<=", "=", ">=", ">" };
81 struct razor_set *set;
82 struct razor_property *property;
83 struct razor_package *package;
84 struct razor_property_iterator *pi;
85 const char *name, *version;
86 enum razor_property_type type;
87 enum razor_version_relation relation;
89 set = razor_set_open(repo_filename);
91 package = razor_set_get_package(set, package_name);
95 pi = razor_property_iterator_create(set, package);
96 while (razor_property_iterator_next(pi, &property,
97 &name, &relation, &version,
99 if (type != required_type)
101 if (version[0] == '\0')
102 printf("%s\n", name);
104 printf("%s %s %s\n", name,
105 relation_string[relation], version);
107 razor_property_iterator_destroy(pi);
109 razor_set_destroy(set);
115 command_list_requires(int argc, const char *argv[])
117 return list_properties(argv[0], RAZOR_PROPERTY_REQUIRES);
121 command_list_provides(int argc, const char *argv[])
123 return list_properties(argv[0], RAZOR_PROPERTY_PROVIDES);
127 command_list_obsoletes(int argc, const char *argv[])
129 return list_properties(argv[0], RAZOR_PROPERTY_OBSOLETES);
133 command_list_conflicts(int argc, const char *argv[])
135 return list_properties(argv[0], RAZOR_PROPERTY_CONFLICTS);
139 command_list_files(int argc, const char *argv[])
141 struct razor_set *set;
143 set = razor_set_open(repo_filename);
146 razor_set_list_files(set, argv[0]);
147 razor_set_destroy(set);
153 command_list_file_packages(int argc, const char *argv[])
155 struct razor_set *set;
156 struct razor_package_iterator *pi;
157 struct razor_package *package;
158 const char *name, *version, *arch;
160 set = razor_set_open(repo_filename);
164 pi = razor_package_iterator_create_for_file(set, argv[0]);
165 while (razor_package_iterator_next(pi, &package,
166 &name, &version, &arch))
167 printf("%s-%s\n", name, version);
168 razor_package_iterator_destroy(pi);
170 razor_set_destroy(set);
176 command_list_package_files(int argc, const char *argv[])
178 struct razor_set *set;
180 set = razor_set_open(repo_filename);
183 razor_set_list_package_files(set, argv[0]);
184 razor_set_destroy(set);
190 list_packages_for_property(struct razor_set *set,
191 struct razor_property *property)
193 struct razor_package_iterator *pi;
194 struct razor_package *package;
195 const char *name, *version, *arch;
197 pi = razor_package_iterator_create_for_property(set, property);
198 while (razor_package_iterator_next(pi, &package,
199 &name, &version, &arch))
200 printf("%s-%s.%s\n", name, version, arch);
201 razor_package_iterator_destroy(pi);
205 list_property_packages(const char *ref_name,
206 const char *ref_version,
207 enum razor_property_type ref_type)
209 struct razor_set *set;
210 struct razor_property *property;
211 struct razor_property_iterator *pi;
212 const char *name, *version;
213 enum razor_property_type type;
214 enum razor_version_relation relation;
216 if (ref_name == NULL)
219 set = razor_set_open(repo_filename);
223 pi = razor_property_iterator_create(set, NULL);
224 while (razor_property_iterator_next(pi, &property,
225 &name, &relation, &version,
227 if (strcmp(ref_name, name) != 0)
229 if (ref_version && relation == RAZOR_VERSION_EQUAL &&
230 strcmp(ref_version, version) != 0)
232 if (ref_type != type)
235 list_packages_for_property(set, property);
237 razor_property_iterator_destroy(pi);
243 command_what_requires(int argc, const char *argv[])
245 return list_property_packages(argv[0], argv[1],
246 RAZOR_PROPERTY_REQUIRES);
250 command_what_provides(int argc, const char *argv[])
252 return list_property_packages(argv[0], argv[1],
253 RAZOR_PROPERTY_PROVIDES);
257 show_progress(void *clientp,
258 double dltotal, double dlnow, double ultotal, double ulnow)
260 const char *file = clientp;
262 if (!dlnow < dltotal)
263 fprintf(stderr, "\rdownloading %s, %dkB/%dkB",
264 file, (int) dlnow / 1024, (int) dltotal / 1024);
270 download_if_missing(const char *url, const char *file)
279 curl = curl_easy_init();
283 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error);
284 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
285 curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, show_progress);
286 curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, file);
288 if (stat(file, &buf) < 0) {
289 fp = fopen(file, "w");
292 "failed to open %s for writing\n", file);
295 curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
296 curl_easy_setopt(curl, CURLOPT_URL, url);
297 res = curl_easy_perform(curl);
299 if (res != CURLE_OK) {
300 fprintf(stderr, "curl error: %s\n", error);
304 res = curl_easy_getinfo(curl,
305 CURLINFO_RESPONSE_CODE, &response);
306 if (res != CURLE_OK) {
307 fprintf(stderr, "curl error: %s\n", error);
311 if (response != 200) {
312 fprintf(stderr, " - failed %ld\n", response);
316 fprintf(stderr, "\n");
319 curl_easy_cleanup(curl);
324 #define REPO_URL "http://download.fedora.redhat.com" \
325 "/pub/fedora/linux/development/i386/os"
328 command_import_yum(int argc, const char *argv[])
330 struct razor_set *set;
332 if (download_if_missing(REPO_URL "/repodata/primary.xml.gz",
333 "primary.xml.gz") < 0)
335 if (download_if_missing(REPO_URL "/repodata/filelists.xml.gz",
336 "filelists.xml.gz") < 0)
339 set = razor_set_create_from_yum();
342 razor_set_write(set, rawhide_repo_filename);
343 razor_set_destroy(set);
344 printf("wrote %s\n", rawhide_repo_filename);
350 command_import_rpmdb(int argc, const char *argv[])
352 struct razor_set *set;
354 set = razor_set_create_from_rpmdb();
357 razor_set_write(set, repo_filename);
358 razor_set_destroy(set);
359 printf("wrote %s\n", repo_filename);
365 mark_packages_for_update(struct razor_transaction *trans,
366 struct razor_set *set, const char *pattern)
368 struct razor_package_iterator *pi;
369 struct razor_package *package;
370 const char *name, *version, *arch;
373 pi = razor_package_iterator_create(set);
374 while (razor_package_iterator_next(pi, &package,
375 &name, &version, &arch)) {
376 if (pattern && fnmatch(pattern, name, 0) == 0) {
377 razor_transaction_update_package(trans, package);
381 razor_package_iterator_destroy(pi);
387 mark_packages_for_removal(struct razor_transaction *trans,
388 struct razor_set *set, const char *pattern)
390 struct razor_package_iterator *pi;
391 struct razor_package *package;
392 const char *name, *version, *arch;
395 pi = razor_package_iterator_create(set);
396 while (razor_package_iterator_next(pi, &package,
397 &name, &version, &arch)) {
398 if (pattern && fnmatch(pattern, name, 0) == 0) {
399 razor_transaction_remove_package(trans, package);
403 razor_package_iterator_destroy(pi);
409 command_update(int argc, const char *argv[])
411 struct razor_set *set, *upstream;
412 struct razor_transaction *trans;
415 set = razor_set_open(repo_filename);
416 upstream = razor_set_open(rawhide_repo_filename);
417 if (set == NULL || upstream == NULL)
420 trans = razor_transaction_create(set, upstream);
422 razor_transaction_update_all(trans);
423 for (i = 0; i < argc; i++) {
424 if (mark_packages_for_update(trans, set, argv[i]) == 0) {
425 fprintf(stderr, "no match for %s\n", argv[i]);
430 errors = razor_transaction_resolve(trans);
434 set = razor_transaction_finish(trans);
435 razor_set_write(set, updated_repo_filename);
436 razor_set_destroy(set);
437 razor_set_destroy(upstream);
438 printf("wrote system-updated.repo\n");
444 command_remove(int argc, const char *argv[])
446 struct razor_set *set;
447 struct razor_transaction *trans;
450 set = razor_set_open(repo_filename);
454 trans = razor_transaction_create(set, NULL);
455 for (i = 0; i < argc; i++) {
456 if (mark_packages_for_removal(trans, set, argv[i]) == 0) {
457 fprintf(stderr, "no match for %s\n", argv[i]);
462 errors = razor_transaction_resolve(trans);
466 set = razor_transaction_finish(trans);
467 razor_set_write(set, updated_repo_filename);
468 razor_set_destroy(set);
469 printf("wrote system-updated.repo\n");
475 print_diff(const char *name,
476 const char *old_version, const char *new_version, const char *arch,
480 printf("removing %s %s\n", name, old_version);
482 printf("install %s %s\n", name, new_version);
486 command_diff(int argc, const char *argv[])
488 struct razor_set *set, *updated;
490 set = razor_set_open(repo_filename);
491 updated = razor_set_open(updated_repo_filename);
492 if (set == NULL || updated == NULL)
495 razor_set_diff(set, updated, print_diff, NULL);
497 razor_set_destroy(set);
498 razor_set_destroy(updated);
504 command_import_rpms(int argc, const char *argv[])
508 struct razor_importer *importer;
509 struct razor_set *set;
510 struct razor_rpm *rpm;
513 const char *dirname = argv[0];
515 if (dirname == NULL) {
516 fprintf(stderr, "usage: razor import-rpms DIR\n");
520 dir = opendir(dirname);
522 fprintf(stderr, "couldn't read dir %s\n", dirname);
526 importer = razor_importer_new();
528 while (de = readdir(dir), de != NULL) {
529 len = strlen(de->d_name);
530 if (len < 5 || strcmp(de->d_name + len - 4, ".rpm") != 0)
532 snprintf(filename, sizeof filename,
533 "%s/%s", dirname, de->d_name);
534 rpm = razor_rpm_open(filename);
537 "failed to open rpm \"%s\"\n", filename);
540 if (razor_importer_add_rpm(importer, rpm)) {
541 fprintf(stderr, "couldn't import %s\n", filename);
544 razor_rpm_close(rpm);
548 razor_importer_destroy(importer);
552 set = razor_importer_finish(importer);
554 razor_set_write(set, repo_filename);
555 razor_set_destroy(set);
556 printf("wrote %s\n", repo_filename);
562 download_package(const char *name,
563 const char *old_version,
564 const char *new_version,
568 char file[PATH_MAX], url[256];
576 v = strchr(new_version, ':');
582 snprintf(url, sizeof url,
583 REPO_URL "/Packages/%s-%s.%s.rpm", name, v, arch);
584 snprintf(file, sizeof file,
585 "rpms/%s-%s.%s.rpm", name, v, arch);
586 if (download_if_missing(url, file) < 0)
591 install_package(const char *name,
592 const char *old_version,
593 const char *new_version,
597 const char *v, *root = data;
599 struct razor_rpm *rpm;
602 printf("removing %s %s not handled\n", name, old_version);
607 v = strchr(new_version, ':');
613 printf("install %s %s\n", name, v);
614 snprintf(file, sizeof file, "rpms/%s-%s.%s.rpm", name, v, arch);
616 rpm = razor_rpm_open(file);
618 fprintf(stderr, "failed to open rpm %s\n", file);
621 if (razor_rpm_install(rpm, root) < 0) {
623 "failed to install rpm %s\n", file);
626 razor_rpm_close(rpm);
630 command_install(int argc, const char *argv[])
632 struct razor_set *system, *upstream, *next;
633 struct razor_transaction *trans;
634 char path[PATH_MAX], new_path[PATH_MAX];
635 int i = 0, errors, fd, dependencies = 1;
637 if (i < argc && strcmp(argv[i], "--no-dependencies") == 0) {
642 /* Create the new next repo file up front to ensure exclusive
644 snprintf(new_path, sizeof new_path,
645 "%s%s/%s", root, razor_root_path, next_repo_filename);
646 fd = open(new_path, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0666);
648 fprintf(stderr, "failed to get lock file, "
649 "maybe previous operation crashed?\n");
651 /* FIXME: Use fcntl advisory locking to figure out
652 * whether previous operation crashed or is still in
658 upstream = razor_set_open(rawhide_repo_filename);
659 snprintf(path, sizeof path,
660 "%s%s/%s", root, razor_root_path, system_repo_filename);
661 system = razor_set_open(path);
662 if (system == NULL || upstream == NULL) {
666 trans = razor_transaction_create(system, upstream);
667 for (; i < argc; i++) {
668 if (mark_packages_for_update(trans, upstream, argv[i]) == 0) {
669 fprintf(stderr, "no package matched %s\n", argv[i]);
676 errors = razor_transaction_resolve(trans);
683 next = razor_transaction_finish(trans);
685 razor_set_write_to_fd(next, fd);
686 printf("wrote %s\n", new_path);
688 if (mkdir("rpms", 0777) && errno != EEXIST) {
689 fprintf(stderr, "failed to create rpms directory.\n");
693 razor_set_diff(system, next, download_package, &errors);
695 fprintf(stderr, "failed to download %d packages\n", errors);
700 /* FIXME: We need to figure out the right install order here,
701 * so the post and pre scripts can run. */
702 razor_set_diff(system, next, install_package, (void *) root);
704 razor_set_destroy(next);
705 razor_set_destroy(system);
706 razor_set_destroy(upstream);
709 rename(new_path, path);
710 printf("renamed %s to %s\n", new_path, path);
716 command_init(int argc, const char *argv[])
719 struct razor_set *set;
722 if (stat(root, &buf) < 0) {
723 if (mkdir(root, 0777) < 0) {
725 "could not create install root \"%s\"\n",
729 fprintf(stderr, "created install root \"%s\"\n", root);
730 } else if (!S_ISDIR(buf.st_mode)) {
732 "install root \"%s\" exists, but is not a directory\n",
737 snprintf(path, sizeof path, "%s/%s",
738 razor_root_path, system_repo_filename);
739 if (razor_create_dir(root, path) < 0) {
740 fprintf(stderr, "could not create %s%s\n",
741 root, razor_root_path);
745 set = razor_set_create();
746 snprintf(path, sizeof path, "%s%s/%s",
747 root, razor_root_path, system_repo_filename);
748 if (razor_set_write(set, path) < 0) {
749 fprintf(stderr, "could not write initial package set\n");
752 razor_set_destroy(set);
758 command_download(int argc, const char *argv[])
760 struct razor_set *set;
761 struct razor_package_iterator *pi;
762 struct razor_package *package;
763 const char *pattern = argv[0], *name, *version, *arch;
764 char url[256], file[256];
767 if (mkdir("rpms", 0777) && errno != EEXIST) {
768 fprintf(stderr, "failed to create rpms directory.\n");
772 set = razor_set_open(rawhide_repo_filename);
773 pi = razor_package_iterator_create(set);
774 while (razor_package_iterator_next(pi, &package,
775 &name, &version, &arch)) {
776 if (pattern && fnmatch(pattern, name, 0) != 0)
780 snprintf(url, sizeof url,
781 REPO_URL "/Packages/%s-%s.%s.rpm",
782 name, version, arch);
783 snprintf(file, sizeof file,
784 "rpms/%s-%s.%s.rpm", name, version, arch);
785 download_if_missing(url, file);
787 razor_package_iterator_destroy(pi);
788 razor_set_destroy(set);
791 fprintf(stderr, "no packages matched \"%s\"\n", pattern);
792 else if (matches == 1)
793 fprintf(stderr, "downloaded 1 package\n");
795 fprintf(stderr, "downloaded %d packages\n", matches);
802 const char *description;
803 int (*func)(int argc, const char *argv[]);
804 } razor_commands[] = {
805 { "list", "list all packages", command_list },
806 { "list-requires", "list all requires for the given package", command_list_requires },
807 { "list-provides", "list all provides for the given package", command_list_provides },
808 { "list-obsoletes", "list all obsoletes for the given package", command_list_obsoletes },
809 { "list-conflicts", "list all conflicts for the given package", command_list_conflicts },
810 { "list-files", "list files for package set", command_list_files },
811 { "list-file-packages", "list packages owning file", command_list_file_packages },
812 { "list-package-files", "list files in package", command_list_package_files },
813 { "what-requires", "list the packages that have the given requires", command_what_requires },
814 { "what-provides", "list the packages that have the given provides", command_what_provides },
815 { "import-yum", "import yum metadata files", command_import_yum },
816 { "import-rpmdb", "import the system rpm database", command_import_rpmdb },
817 { "import-rpms", "import rpms from the given directory", command_import_rpms },
818 { "update", "update all or specified packages", command_update },
819 { "remove", "remove specified packages", command_remove },
820 { "diff", "show diff between two package sets", command_diff },
821 { "install", "install rpm", command_install },
822 { "init", "init razor root", command_init },
823 { "download", "download packages", command_download }
832 for (i = 0; i < ARRAY_SIZE(razor_commands); i++)
834 razor_commands[i].name, razor_commands[i].description);
840 main(int argc, const char *argv[])
845 repo = getenv("RAZOR_REPO");
847 repo_filename = repo;
852 for (i = 0; i < ARRAY_SIZE(razor_commands); i++)
853 if (strcmp(razor_commands[i].name, argv[1]) == 0)
854 return razor_commands[i].func(argc - 2, argv + 2);