main.c
author Kristian H?gsberg <krh@redhat.com>
Mon Jun 09 12:47:37 2008 -0400 (2008-06-09)
changeset 230 c1e2aed8dd07
parent 212 e8f493d8ff9a
child 231 fef9808171ff
permissions -rw-r--r--
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.
     1 /*
     2  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
     3  * Copyright (C) 2008  Red Hat, Inc
     4  *
     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.
     9  *
    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.
    14  *
    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.
    18  */
    19 
    20 #include <stdlib.h>
    21 #include <stddef.h>
    22 #include <stdio.h>
    23 #include <stdint.h>
    24 #include <string.h>
    25 #include <sys/stat.h>
    26 #include <unistd.h>
    27 #include <fcntl.h>
    28 #include <dirent.h>
    29 #include <curl/curl.h>
    30 #include <fnmatch.h>
    31 #include <errno.h>
    32 #include "razor.h"
    33 #include "razor-internal.h"
    34 
    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;
    42 
    43 static int
    44 command_list(int argc, const char *argv[])
    45 {
    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;
    51 
    52 	if (i < argc && strcmp(argv[i], "--only-names") == 0) {
    53 		only_names = 1;
    54 		i++;
    55 	}
    56 
    57 	pattern = argv[i];
    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)
    63 			continue;
    64 
    65 		if (only_names)
    66 			printf("%s\n", name);
    67 		else
    68 			printf("%s-%s.%s\n", name, version, arch);
    69 	}
    70 	razor_package_iterator_destroy(pi);
    71 	razor_set_destroy(set);
    72 
    73 	return 0;
    74 }
    75 
    76 static int
    77 list_properties(const char *package_name,
    78 		enum razor_property_type required_type)
    79 {
    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;
    88 
    89 	set = razor_set_open(repo_filename);
    90 	if (package_name)
    91 		package = razor_set_get_package(set, package_name);
    92 	else
    93 		package = NULL;
    94 
    95 	pi = razor_property_iterator_create(set, package);
    96 	while (razor_property_iterator_next(pi, &property,
    97 					    &name, &relation, &version,
    98 					    &type)) {
    99 		if (type != required_type)
   100 			continue;
   101 		if (version[0] == '\0')
   102 			printf("%s\n", name);
   103 		else
   104 			printf("%s %s %s\n", name,
   105 			       relation_string[relation], version);
   106 	}
   107 	razor_property_iterator_destroy(pi);
   108 
   109 	razor_set_destroy(set);
   110 
   111 	return 0;
   112 }
   113 
   114 static int
   115 command_list_requires(int argc, const char *argv[])
   116 {
   117 	return list_properties(argv[0], RAZOR_PROPERTY_REQUIRES);
   118 }
   119 
   120 static int
   121 command_list_provides(int argc, const char *argv[])
   122 {
   123 	return list_properties(argv[0], RAZOR_PROPERTY_PROVIDES);
   124 }
   125 
   126 static int
   127 command_list_obsoletes(int argc, const char *argv[])
   128 {
   129 	return list_properties(argv[0], RAZOR_PROPERTY_OBSOLETES);
   130 }
   131 
   132 static int
   133 command_list_conflicts(int argc, const char *argv[])
   134 {
   135 	return list_properties(argv[0], RAZOR_PROPERTY_CONFLICTS);
   136 }
   137 
   138 static int
   139 command_list_files(int argc, const char *argv[])
   140 {
   141 	struct razor_set *set;
   142 
   143 	set = razor_set_open(repo_filename);
   144 	if (set == NULL)
   145 		return 1;
   146 	razor_set_list_files(set, argv[0]);
   147 	razor_set_destroy(set);
   148 
   149 	return 0;
   150 }
   151 
   152 static int
   153 command_list_file_packages(int argc, const char *argv[])
   154 {
   155 	struct razor_set *set;
   156 	struct razor_package_iterator *pi;
   157 	struct razor_package *package;
   158 	const char *name, *version, *arch;
   159 
   160 	set = razor_set_open(repo_filename);
   161 	if (set == NULL)
   162 		return 1;
   163 
   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);
   169 
   170 	razor_set_destroy(set);
   171 
   172 	return 0;
   173 }
   174 
   175 static int
   176 command_list_package_files(int argc, const char *argv[])
   177 {
   178 	struct razor_set *set;
   179 
   180 	set = razor_set_open(repo_filename);
   181 	if (set == NULL)
   182 		return 1;
   183 	razor_set_list_package_files(set, argv[0]);
   184 	razor_set_destroy(set);
   185 
   186 	return 0;
   187 }
   188 
   189 static void
   190 list_packages_for_property(struct razor_set *set,
   191 			   struct razor_property *property)
   192 {
   193 	struct razor_package_iterator *pi;
   194 	struct razor_package *package;
   195 	const char *name, *version, *arch;
   196 
   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);
   202 }
   203 
   204 static int
   205 list_property_packages(const char *ref_name,
   206 		       const char *ref_version,
   207 		       enum razor_property_type ref_type)
   208 {
   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;
   215 
   216 	if (ref_name == NULL)
   217 		return 0;
   218 
   219 	set = razor_set_open(repo_filename);
   220 	if (set == NULL)
   221 		return 1;
   222 
   223 	pi = razor_property_iterator_create(set, NULL);
   224 	while (razor_property_iterator_next(pi, &property,
   225 					    &name, &relation, &version,
   226 					    &type)) {
   227 		if (strcmp(ref_name, name) != 0)
   228 			continue;
   229 		if (ref_version && relation == RAZOR_VERSION_EQUAL &&
   230 		    strcmp(ref_version, version) != 0)
   231 			continue;
   232 		if (ref_type != type)
   233 			continue;
   234 
   235 		list_packages_for_property(set, property);
   236 	}
   237 	razor_property_iterator_destroy(pi);
   238 
   239 	return 0;
   240 }
   241 
   242 static int
   243 command_what_requires(int argc, const char *argv[])
   244 {
   245 	return list_property_packages(argv[0], argv[1],
   246 				      RAZOR_PROPERTY_REQUIRES);
   247 }
   248 
   249 static int
   250 command_what_provides(int argc, const char *argv[])
   251 {
   252 	return list_property_packages(argv[0], argv[1],
   253 				      RAZOR_PROPERTY_PROVIDES);
   254 }
   255 
   256 static int
   257 show_progress(void *clientp,
   258 	      double dltotal, double dlnow, double ultotal, double ulnow)
   259 {
   260 	const char *file = clientp;
   261 
   262 	if (!dlnow < dltotal)
   263 		fprintf(stderr, "\rdownloading %s, %dkB/%dkB",
   264 			file, (int) dlnow / 1024, (int) dltotal / 1024);
   265 
   266 	return 0;
   267 }
   268 
   269 static int
   270 download_if_missing(const char *url, const char *file)
   271 {
   272 	CURL *curl;
   273 	struct stat buf;
   274 	char error[256];
   275 	FILE *fp;
   276 	CURLcode res;
   277 	long response;
   278 
   279 	curl = curl_easy_init();
   280 	if (curl == NULL)
   281 		return 1;
   282 
   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);
   287 
   288 	if (stat(file, &buf) < 0) {
   289 		fp = fopen(file, "w");
   290 		if (fp == NULL) {
   291 			fprintf(stderr,
   292 				"failed to open %s for writing\n", file);
   293 			return -1;
   294 		}
   295 		curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
   296 		curl_easy_setopt(curl, CURLOPT_URL, url);
   297 		res = curl_easy_perform(curl);
   298 		fclose(fp);
   299 		if (res != CURLE_OK) {
   300 			fprintf(stderr, "curl error: %s\n", error);
   301 			unlink(file);
   302 			return -1;
   303 		}
   304 		res = curl_easy_getinfo(curl,
   305 					CURLINFO_RESPONSE_CODE, &response);
   306 		if (res != CURLE_OK) {
   307 			fprintf(stderr, "curl error: %s\n", error);
   308                         unlink(file);
   309                         return -1;
   310 		}
   311 		if (response != 200) {
   312 			fprintf(stderr, " - failed %ld\n", response);
   313                         unlink(file);
   314                         return -1;
   315 		}
   316 		fprintf(stderr, "\n");
   317 	}
   318 
   319 	curl_easy_cleanup(curl);
   320 
   321 	return 0;
   322 }
   323 
   324 #define REPO_URL "http://download.fedora.redhat.com" \
   325 	"/pub/fedora/linux/development/i386/os"
   326 
   327 static int
   328 command_import_yum(int argc, const char *argv[])
   329 {
   330 	struct razor_set *set;
   331 
   332 	if (download_if_missing(REPO_URL "/repodata/primary.xml.gz",
   333 				"primary.xml.gz") < 0)
   334 		return -1;
   335 	if (download_if_missing(REPO_URL "/repodata/filelists.xml.gz",
   336 				"filelists.xml.gz") < 0)
   337 		return -1;
   338 
   339 	set = razor_set_create_from_yum();
   340 	if (set == NULL)
   341 		return 1;
   342 	razor_set_write(set, rawhide_repo_filename);
   343 	razor_set_destroy(set);
   344 	printf("wrote %s\n", rawhide_repo_filename);
   345 
   346 	return 0;
   347 }
   348 
   349 static int
   350 command_import_rpmdb(int argc, const char *argv[])
   351 {
   352 	struct razor_set *set;
   353 
   354 	set = razor_set_create_from_rpmdb();
   355 	if (set == NULL)
   356 		return 1;
   357 	razor_set_write(set, repo_filename);
   358 	razor_set_destroy(set);
   359 	printf("wrote %s\n", repo_filename);
   360 
   361 	return 0;
   362 }
   363 
   364 static int
   365 mark_packages_for_update(struct razor_transaction *trans,
   366 			 struct razor_set *set, const char *pattern)
   367 {
   368 	struct razor_package_iterator *pi;
   369 	struct razor_package *package;
   370 	const char *name, *version, *arch;
   371 	int matches = 0;
   372 
   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);
   378 			matches++;
   379 		}
   380 	}
   381 	razor_package_iterator_destroy(pi);
   382 
   383 	return matches;
   384 }
   385 
   386 static int
   387 mark_packages_for_removal(struct razor_transaction *trans,
   388 			  struct razor_set *set, const char *pattern)
   389 {
   390 	struct razor_package_iterator *pi;
   391 	struct razor_package *package;
   392 	const char *name, *version, *arch;
   393 	int matches = 0;
   394 
   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);
   400 			matches++;
   401 		}
   402 	}
   403 	razor_package_iterator_destroy(pi);
   404 
   405 	return matches;
   406 }
   407 
   408 static int
   409 command_update(int argc, const char *argv[])
   410 {
   411 	struct razor_set *set, *upstream;
   412 	struct razor_transaction *trans;
   413 	int i, errors;
   414 
   415 	set = razor_set_open(repo_filename);
   416 	upstream = razor_set_open(rawhide_repo_filename);
   417 	if (set == NULL || upstream == NULL)
   418 		return 1;
   419 
   420 	trans = razor_transaction_create(set, upstream);
   421 	if (argc == 0)
   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]);
   426 			return 1;
   427 		}
   428 	}
   429 		
   430 	errors = razor_transaction_resolve(trans);
   431 	if (errors)
   432 		return 1;
   433 
   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");
   439 
   440 	return 0;
   441 }
   442 
   443 static int
   444 command_remove(int argc, const char *argv[])
   445 {
   446 	struct razor_set *set;
   447 	struct razor_transaction *trans;
   448 	int i, errors;
   449 
   450 	set = razor_set_open(repo_filename);
   451 	if (set == NULL)
   452 		return 1;
   453 
   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]);
   458 			return 1;
   459 		}
   460 	}
   461 
   462 	errors = razor_transaction_resolve(trans);
   463 	if (errors)
   464 		return 1;
   465 
   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");
   470 
   471 	return 0;
   472 }
   473 
   474 static void
   475 print_diff(const char *name,
   476 	   const char *old_version, const char *new_version, const char *arch,
   477 	   void *data)
   478 {
   479 	if (old_version)
   480 		printf("removing %s %s\n", name, old_version);
   481 	else
   482 		printf("install %s %s\n", name, new_version);
   483 }
   484 
   485 static int
   486 command_diff(int argc, const char *argv[])
   487 {
   488 	struct razor_set *set, *updated;
   489 
   490 	set = razor_set_open(repo_filename);
   491 	updated = razor_set_open(updated_repo_filename);
   492 	if (set == NULL || updated == NULL)
   493 		return 1;
   494 
   495 	razor_set_diff(set, updated, print_diff, NULL);	
   496 
   497 	razor_set_destroy(set);
   498 	razor_set_destroy(updated);
   499 
   500 	return 0;
   501 }
   502 
   503 static int
   504 command_import_rpms(int argc, const char *argv[])
   505 {
   506 	DIR *dir;
   507 	struct dirent *de;
   508 	struct razor_importer *importer;
   509 	struct razor_set *set;
   510 	struct razor_rpm *rpm;
   511 	int len;
   512 	char filename[256];
   513 	const char *dirname = argv[0];
   514 
   515 	if (dirname == NULL) {
   516 		fprintf(stderr, "usage: razor import-rpms DIR\n");
   517 		return -1;
   518 	}
   519 
   520 	dir = opendir(dirname);
   521 	if (dir == NULL) {
   522 		fprintf(stderr, "couldn't read dir %s\n", dirname);
   523 		return -1;
   524 	}
   525 
   526 	importer = razor_importer_new();
   527 
   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)
   531 		    continue;
   532 		snprintf(filename, sizeof filename,
   533 			 "%s/%s", dirname, de->d_name);
   534 		rpm = razor_rpm_open(filename);
   535 		if (rpm == NULL) {
   536 			fprintf(stderr,
   537 				"failed to open rpm \"%s\"\n", filename);
   538 			continue;
   539 		}
   540 		if (razor_importer_add_rpm(importer, rpm)) {
   541 			fprintf(stderr, "couldn't import %s\n", filename);
   542 			break;
   543 		}
   544 		razor_rpm_close(rpm);
   545 	}
   546 
   547 	if (de != NULL) {
   548 		razor_importer_destroy(importer);
   549 		return -1;
   550 	}
   551 
   552 	set = razor_importer_finish(importer);
   553 
   554 	razor_set_write(set, repo_filename);
   555 	razor_set_destroy(set);
   556 	printf("wrote %s\n", repo_filename);
   557 
   558 	return 0;
   559 }
   560 
   561 static void
   562 download_package(const char *name,
   563 		 const char *old_version,
   564 		 const char *new_version,
   565 		 const char *arch,
   566 		 void *data)
   567 {
   568 	char file[PATH_MAX], url[256];
   569 	const char *v;
   570 	int *errors = data;
   571 
   572 	if (old_version)
   573 		return;
   574 
   575 	/* Skip epoch */
   576 	v = strchr(new_version, ':');
   577 	if (v != NULL)
   578 		v = v + 1;
   579 	else
   580 		v = new_version;
   581 
   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)
   587 		(*errors)++;
   588 }
   589 
   590 static void
   591 install_package(const char *name,
   592 		const char *old_version,
   593 		const char *new_version,
   594 		const char *arch,
   595 		void *data)
   596 {
   597 	const char *v, *root = data;
   598 	char file[PATH_MAX];
   599 	struct razor_rpm *rpm;
   600 
   601 	if (old_version) {
   602 		printf("removing %s %s not handled\n", name, old_version);
   603 		return;
   604 	}
   605 
   606 	/* Skip epoch */
   607 	v = strchr(new_version, ':');
   608 	if (v != NULL)
   609 		v = v + 1;
   610 	else
   611 		v = new_version;
   612 
   613 	printf("install %s %s\n", name, v);
   614 	snprintf(file, sizeof file, "rpms/%s-%s.%s.rpm", name, v, arch);
   615 
   616  	rpm = razor_rpm_open(file);
   617 	if (rpm == NULL) {
   618 		fprintf(stderr, "failed to open rpm %s\n", file);
   619 		return;
   620 	}
   621 	if (razor_rpm_install(rpm, root) < 0) {
   622 		fprintf(stderr,
   623 			"failed to install rpm %s\n", file);
   624 		return;
   625 	}
   626 	razor_rpm_close(rpm);
   627 }
   628 
   629 static int
   630 command_install(int argc, const char *argv[])
   631 {
   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;
   636 
   637 	if (i < argc && strcmp(argv[i], "--no-dependencies") == 0) {
   638 		dependencies = 0;
   639 		i++;
   640 	}
   641 
   642 	/* Create the new next repo file up front to ensure exclusive
   643 	 * access. */
   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);
   647 	if (fd < 0) {
   648 		fprintf(stderr, "failed to get lock file, "
   649 			"maybe previous operation crashed?\n");
   650 
   651 		/* FIXME: Use fcntl advisory locking to figure out
   652 		 * whether previous operation crashed or is still in
   653 		 * progress. */
   654 
   655 		return -1;
   656 	}
   657 
   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) {
   663 		unlink(new_path);
   664 		return 1;
   665 	}
   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]);
   670 			unlink(new_path);
   671 			return 1;
   672 		}
   673 	}
   674 
   675 	if (dependencies) {
   676 		errors = razor_transaction_resolve(trans);
   677 		if (errors) {
   678 			unlink(new_path);
   679 			return 1;
   680 		}
   681 	}
   682 
   683 	next = razor_transaction_finish(trans);
   684 
   685 	razor_set_write_to_fd(next, fd);
   686 	printf("wrote %s\n", new_path);
   687 
   688 	if (mkdir("rpms", 0777) && errno != EEXIST) {
   689 		fprintf(stderr, "failed to create rpms directory.\n");
   690 		return 1;
   691 	}
   692 
   693 	razor_set_diff(system, next, download_package, &errors);
   694 	if (errors > 0) {
   695 		fprintf(stderr, "failed to download %d packages\n", errors);
   696 		unlink(new_path);
   697                 return 1;
   698         }
   699 
   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);
   703 
   704 	razor_set_destroy(next);
   705 	razor_set_destroy(system);
   706 	razor_set_destroy(upstream);
   707 
   708 	/* Make it so. */
   709 	rename(new_path, path);
   710 	printf("renamed %s to %s\n", new_path, path);
   711 
   712 	return 0;
   713 }
   714 
   715 static int
   716 command_init(int argc, const char *argv[])
   717 {
   718 	struct stat buf;
   719 	struct razor_set *set;
   720 	char path[PATH_MAX];
   721 
   722 	if (stat(root, &buf) < 0) {
   723 		if (mkdir(root, 0777) < 0) {
   724 			fprintf(stderr,
   725 				"could not create install root \"%s\"\n",
   726 				root);
   727 			return -1;
   728 		}
   729 		fprintf(stderr, "created install root \"%s\"\n", root);
   730 	} else if (!S_ISDIR(buf.st_mode)) {
   731 		fprintf(stderr,
   732 			"install root \"%s\" exists, but is not a directory\n",
   733 			root);
   734 		return -1;
   735 	}
   736 
   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);
   742 		return -1;
   743 	}
   744 
   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");
   750 		return -1;
   751 	}
   752 	razor_set_destroy(set);
   753 
   754 	return 0;
   755 }
   756 
   757 static int
   758 command_download(int argc, const char *argv[])
   759 {
   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];
   765 	int matches = 0;
   766 
   767 	if (mkdir("rpms", 0777) && errno != EEXIST) {
   768 		fprintf(stderr, "failed to create rpms directory.\n");
   769 		return 1;
   770 	}
   771 
   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)
   777 			continue;
   778 
   779 		matches++;
   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);
   786 	}
   787 	razor_package_iterator_destroy(pi);
   788 	razor_set_destroy(set);
   789 
   790 	if (matches == 0)
   791 		fprintf(stderr, "no packages matched \"%s\"\n", pattern);
   792 	else if (matches == 1)
   793 		fprintf(stderr, "downloaded 1 package\n");
   794 	else
   795 		fprintf(stderr, "downloaded %d packages\n", matches);
   796 
   797 	return 0;
   798 }
   799 
   800 static struct {
   801 	const char *name;
   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 }
   824 };
   825 
   826 static int
   827 usage(void)
   828 {
   829 	int i;
   830 
   831 	printf("usage:\n");
   832 	for (i = 0; i < ARRAY_SIZE(razor_commands); i++)
   833 		printf("  %-20s%s\n",
   834 		       razor_commands[i].name, razor_commands[i].description);
   835 
   836 	return 1;
   837 }
   838 
   839 int
   840 main(int argc, const char *argv[])
   841 {
   842 	char *repo;
   843 	int i;
   844 
   845 	repo = getenv("RAZOR_REPO");
   846 	if (repo != NULL)
   847 		repo_filename = repo;
   848 
   849 	if (argc < 2)
   850 		return usage();
   851 
   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);
   855 
   856 	return usage();
   857 }