src/main.c
author J. Ali Harlow <ali@juiblex.co.uk>
Fri Jun 12 16:59:11 2009 +0100 (2009-06-12)
changeset 369 f8c27fe9fe63
parent 363 c75a2d5caae9
child 372 6e93e5485947
permissions -rw-r--r--
Add basic support for uninstall scripts.
RPM_INSTALL_PREFIX{n} is not yet supported and upgrading a package
where an uninstall script changes may need more work to ensure the
old script doesn't get included in the merged set (when it is too
late to remove). I haven't yet tested whether this is a real problem.
     1 /*
     2  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
     3  * Copyright (C) 2008  Red Hat, Inc
     4  * Copyright (C) 2009  J. Ali Harlow <ali@juiblex.co.uk>
     5  *
     6  * This program is free software; you can redistribute it and/or modify
     7  * it under the terms of the GNU General Public License as published by
     8  * the Free Software Foundation; either version 2 of the License, or
     9  * (at your option) any later version.
    10  *
    11  * This program is distributed in the hope that it will be useful,
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  * GNU General Public License for more details.
    15  *
    16  * You should have received a copy of the GNU General Public License along
    17  * with this program; if not, write to the Free Software Foundation, Inc.,
    18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    19  */
    20 
    21 #define _GNU_SOURCE
    22 
    23 #include "config.h"
    24 
    25 #include <stdlib.h>
    26 #include <stddef.h>
    27 #include <stdio.h>
    28 #include <stdint.h>
    29 #include <string.h>
    30 #include <sys/stat.h>
    31 #include <unistd.h>
    32 #include <fcntl.h>
    33 #include <dirent.h>
    34 #include <limits.h>
    35 #ifdef HAVE_CURL
    36 #include <curl/curl.h>
    37 #endif
    38 #include <fnmatch.h>
    39 #include <errno.h>
    40 #include "razor.h"
    41 
    42 static const char system_repo_filename[] = "system.rzdb";
    43 static const char next_repo_filename[] = "system-next.rzdb";
    44 static const char rawhide_repo_filename[] = "rawhide.rzdb";
    45 static const char updated_repo_filename[] = "system-updated.rzdb";
    46 static const char *install_root = "";
    47 static const char *repo_filename = system_repo_filename;
    48 static const char *yum_url;
    49 
    50 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
    51 
    52 static int
    53 update_packages(struct razor_transaction *trans, struct razor_set *system,
    54 		struct razor_set *next, struct razor_relocations *relocations);
    55 
    56 static struct razor_package_iterator *
    57 create_iterator_from_argv(struct razor_set *set, int argc, const char *argv[])
    58 {
    59 	struct razor_package_query *query;
    60 	struct razor_package_iterator *iter;
    61 	struct razor_package *package;
    62 	const char *name, *pattern;
    63 	int i, count;
    64 
    65 	if (argc == 0)
    66 		return razor_package_iterator_create(set);
    67 
    68 	query = razor_package_query_create(set);
    69 
    70 	for (i = 0; i < argc; i++) {
    71 		iter = razor_package_iterator_create(set);
    72 		pattern = argv[i];
    73 		count = 0;
    74 		while (razor_package_iterator_next(iter, &package,
    75 						   RAZOR_DETAIL_NAME, &name,
    76 						   RAZOR_DETAIL_LAST)) {
    77 			if (fnmatch(pattern, name, 0) != 0)
    78 				continue;
    79 
    80 			razor_package_query_add_package(query, package);
    81 			count++;
    82 		}
    83 		razor_package_iterator_destroy(iter);
    84 
    85 		if (count == 0)
    86 			fprintf(stderr,
    87 				"no package matches \"%s\"\n", pattern);
    88 	}
    89 
    90 	return razor_package_query_finish(query);
    91 }
    92 
    93 #define LIST_PACKAGES_ONLY_NAMES 0x01
    94 
    95 static void
    96 list_packages(struct razor_package_iterator *iter, uint32_t flags)
    97 {
    98 	struct razor_package *package;
    99 	const char *name, *version, *arch;
   100 
   101 	while (razor_package_iterator_next(iter, &package,
   102 					   RAZOR_DETAIL_NAME, &name,
   103 					   RAZOR_DETAIL_VERSION, &version,
   104 					   RAZOR_DETAIL_ARCH, &arch,
   105 					   RAZOR_DETAIL_LAST)) {
   106 		if (flags & LIST_PACKAGES_ONLY_NAMES)
   107 			printf("%s\n", name);
   108 		else
   109 			printf("%s-%s.%s\n", name, version, arch);
   110 	}
   111 }
   112 
   113 static int
   114 command_list(int argc, const char *argv[])
   115 {
   116 	struct razor_package_iterator *pi;
   117 	struct razor_set *set;
   118 	uint32_t flags = 0;
   119 	int i = 0;
   120 
   121 	if (i < argc && strcmp(argv[i], "--only-names") == 0) {
   122 		flags |= LIST_PACKAGES_ONLY_NAMES;
   123 		i++;
   124 	}
   125 
   126 	set = razor_root_open_read_only(install_root);
   127 	if (set == NULL)
   128 		return 1;
   129 
   130 	pi = create_iterator_from_argv(set, argc - i, argv + i);
   131 	list_packages(pi, flags);
   132 	razor_package_iterator_destroy(pi);
   133 	razor_set_destroy(set);
   134 
   135 	return 0;
   136 }
   137 
   138 static void
   139 list_package_properties(struct razor_set *set,
   140 			struct razor_package *package, uint32_t type)
   141 {
   142 	struct razor_property_iterator *pi;
   143 	struct razor_property *property;
   144 	const char *name, *version;
   145 	uint32_t flags;
   146 
   147 	pi = razor_property_iterator_create(set, package);
   148 	while (razor_property_iterator_next(pi, &property,
   149 					    &name, &flags, &version)) {
   150 		if ((flags & RAZOR_PROPERTY_TYPE_MASK) != type)
   151 			continue;
   152 		printf("%s", name);
   153 		if (version[0] != '\0')
   154 			printf(" %s %s",
   155 			       razor_property_relation_to_string(property),
   156 			       version);
   157 
   158 		if (flags & ~(RAZOR_PROPERTY_RELATION_MASK | RAZOR_PROPERTY_TYPE_MASK)) {
   159 			printf(" [");
   160 			if (flags & RAZOR_PROPERTY_PRE)
   161 				printf(" pre");
   162 			if (flags & RAZOR_PROPERTY_POST)
   163 				printf(" post");
   164 			if (flags & RAZOR_PROPERTY_PREUN)
   165 				printf(" preun");
   166 			if (flags & RAZOR_PROPERTY_POSTUN)
   167 				printf(" postun");
   168 			printf(" ]");
   169 		}
   170 		printf("\n");
   171 	}
   172 	razor_property_iterator_destroy(pi);
   173 }
   174 
   175 static int
   176 list_properties(int argc, const char *argv[], uint32_t type)
   177 {
   178 	struct razor_set *set;
   179 	struct razor_package *package;
   180 	struct razor_package_iterator *pi;
   181 	const char *name, *version, *arch;
   182 
   183 	set = razor_root_open_read_only(install_root);
   184 	if (set == NULL)
   185 		return 1;
   186 
   187 	pi = create_iterator_from_argv(set, argc, argv);
   188 	while (razor_package_iterator_next(pi, &package,
   189 					   RAZOR_DETAIL_NAME, &name,
   190 					   RAZOR_DETAIL_VERSION, &version,
   191 					   RAZOR_DETAIL_ARCH, &arch,
   192 					   RAZOR_DETAIL_LAST))
   193 		list_package_properties(set, package, type);
   194 	razor_package_iterator_destroy(pi);
   195 	razor_set_destroy(set);
   196 
   197 	return 0;
   198 }
   199 
   200 static int
   201 command_list_requires(int argc, const char *argv[])
   202 {
   203 	return list_properties(argc, argv, RAZOR_PROPERTY_REQUIRES);
   204 }
   205 
   206 static int
   207 command_list_provides(int argc, const char *argv[])
   208 {
   209 	return list_properties(argc, argv, RAZOR_PROPERTY_PROVIDES);
   210 }
   211 
   212 static int
   213 command_list_obsoletes(int argc, const char *argv[])
   214 {
   215 	return list_properties(argc, argv, RAZOR_PROPERTY_OBSOLETES);
   216 }
   217 
   218 static int
   219 command_list_conflicts(int argc, const char *argv[])
   220 {
   221 	return list_properties(argc, argv, RAZOR_PROPERTY_CONFLICTS);
   222 }
   223 
   224 static int
   225 command_list_scripts(int argc, const char *argv[])
   226 {
   227 	struct razor_set *set;
   228 	struct razor_package *package;
   229 	struct razor_package_iterator *pi;
   230 	const char *preunprog, *preun, *postunprog, *postun;
   231 
   232 	set = razor_root_open_read_only(install_root);
   233 	if (set == NULL)
   234 		return 1;
   235 
   236 	pi = create_iterator_from_argv(set, argc, argv);
   237 	while (razor_package_iterator_next(pi, &package,
   238 					   RAZOR_DETAIL_PREUNPROG, &preunprog,
   239 					   RAZOR_DETAIL_PREUN, &preun,
   240 					   RAZOR_DETAIL_POSTUNPROG, &postunprog,
   241 					   RAZOR_DETAIL_POSTUN, &postun,
   242 					   RAZOR_DETAIL_LAST)) {
   243 		if (preun && *preun) {
   244 			printf("preuninstall scriptlet");
   245 			if (preunprog && *preunprog)
   246 				printf(" (using %s)",preunprog);
   247 			printf(":\n%s\n",preun);
   248 		}
   249 		if (postun && *postun) {
   250 			printf("postuninstall scriptlet");
   251 			if (postunprog && *postunprog)
   252 				printf(" (using %s)",postunprog);
   253 			printf(":\n%s\n",postun);
   254 		}
   255 	}
   256 	razor_package_iterator_destroy(pi);
   257 	razor_set_destroy(set);
   258 
   259 	return 0;
   260 }
   261 
   262 static int
   263 command_list_files(int argc, const char *argv[])
   264 {
   265 	struct razor_set *set;
   266 
   267 	set = razor_root_open_read_only(install_root);
   268 	if (set == NULL)
   269 		return 1;
   270 
   271 	razor_set_list_files(set, argv[0]);
   272 	razor_set_destroy(set);
   273 
   274 	return 0;
   275 }
   276 
   277 static int
   278 command_list_file_packages(int argc, const char *argv[])
   279 {
   280 	struct razor_set *set;
   281 	struct razor_package_iterator *pi;
   282 
   283 	set = razor_root_open_read_only(install_root);
   284 	if (set == NULL)
   285 		return 1;
   286 
   287 	pi = razor_package_iterator_create_for_file(set, argv[0]);
   288 	list_packages(pi, 0);
   289 	razor_package_iterator_destroy(pi);
   290 
   291 	razor_set_destroy(set);
   292 
   293 	return 0;
   294 }
   295 
   296 static int
   297 command_list_package_files(int argc, const char *argv[])
   298 {
   299 	struct razor_set *set;
   300 	struct razor_package_iterator *pi;
   301 	struct razor_package *package;
   302 	const char *name, *version, *arch;
   303 
   304 	set = razor_root_open_read_only(install_root);
   305 	if (set == NULL)
   306 		return 1;
   307 
   308 	pi = create_iterator_from_argv(set, argc, argv);
   309 	while (razor_package_iterator_next(pi, &package,
   310 					   RAZOR_DETAIL_NAME, &name,
   311 					   RAZOR_DETAIL_VERSION, &version,
   312 					   RAZOR_DETAIL_ARCH, &arch,
   313 					   RAZOR_DETAIL_LAST))
   314 		razor_set_list_package_files(set, package);
   315 	razor_package_iterator_destroy(pi);
   316 
   317 	razor_set_destroy(set);
   318 
   319 	return 0;
   320 }
   321 
   322 static int
   323 list_property_packages(const char *ref_name,
   324 		       const char *ref_version,
   325 		       uint32_t type)
   326 {
   327 	struct razor_set *set;
   328 	struct razor_property *property;
   329 	struct razor_property_iterator *prop_iter;
   330 	struct razor_package_iterator *pkg_iter;
   331 	const char *name, *version;
   332 	uint32_t flags;
   333 
   334 	if (ref_name == NULL)
   335 		return 0;
   336 
   337 	set = razor_root_open_read_only(install_root);
   338 	if (set == NULL)
   339 		return 1;
   340 
   341 	prop_iter = razor_property_iterator_create(set, NULL);
   342 	while (razor_property_iterator_next(prop_iter, &property,
   343 					    &name, &flags, &version)) {
   344 		if (strcmp(ref_name, name) != 0)
   345 			continue;
   346 		if (ref_version &&
   347 		    (flags & RAZOR_PROPERTY_RELATION_MASK) == RAZOR_PROPERTY_EQUAL &&
   348 		    strcmp(ref_version, version) != 0)
   349 			continue;
   350 		if ((flags & RAZOR_PROPERTY_TYPE_MASK) != type)
   351 			continue;
   352 
   353 		pkg_iter =
   354 			razor_package_iterator_create_for_property(set,
   355 								   property);
   356 		list_packages(pkg_iter, 0);
   357 		razor_package_iterator_destroy(pkg_iter);
   358 	}
   359 	razor_property_iterator_destroy(prop_iter);
   360 
   361 	razor_set_destroy(set);
   362 
   363 	return 0;
   364 }
   365 
   366 static int
   367 command_what_requires(int argc, const char *argv[])
   368 {
   369 	return list_property_packages(argv[0], argv[1],
   370 				      RAZOR_PROPERTY_REQUIRES);
   371 }
   372 
   373 static int
   374 command_what_provides(int argc, const char *argv[])
   375 {
   376 	return list_property_packages(argv[0], argv[1],
   377 				      RAZOR_PROPERTY_PROVIDES);
   378 }
   379 
   380 static int
   381 show_progress(void *clientp,
   382 	      double dltotal, double dlnow, double ultotal, double ulnow)
   383 {
   384 	const char *file = clientp;
   385 
   386 	if (!dlnow < dltotal)
   387 		fprintf(stderr, "\rdownloading %s, %dkB/%dkB",
   388 			file, (int) dlnow / 1024, (int) dltotal / 1024);
   389 
   390 	return 0;
   391 }
   392 
   393 static int
   394 download_if_missing(const char *url, const char *file)
   395 {
   396 #ifndef HAVE_CURL
   397 	return 1;
   398 #else
   399 	CURL *curl;
   400 	struct stat buf;
   401 	char error[256];
   402 	FILE *fp;
   403 	CURLcode res;
   404 	long response;
   405 
   406 	curl = curl_easy_init();
   407 	if (curl == NULL)
   408 		return 1;
   409 
   410 	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error);
   411 	curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
   412 	curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, show_progress);
   413 	curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, file);
   414 
   415 	if (stat(file, &buf) < 0) {
   416 		fp = fopen(file, "wb");
   417 		if (fp == NULL) {
   418 			fprintf(stderr,
   419 				"failed to open %s for writing\n", file);
   420 			return -1;
   421 		}
   422 		curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
   423 		curl_easy_setopt(curl, CURLOPT_URL, url);
   424 		res = curl_easy_perform(curl);
   425 		fclose(fp);
   426 		if (res != CURLE_OK) {
   427 			fprintf(stderr, "curl error: %s\n", error);
   428 			unlink(file);
   429 			return -1;
   430 		}
   431 		res = curl_easy_getinfo(curl,
   432 					CURLINFO_RESPONSE_CODE, &response);
   433 		if (res != CURLE_OK) {
   434 			fprintf(stderr, "curl error: %s\n", error);
   435                         unlink(file);
   436                         return -1;
   437 		}
   438 		if (response != 200) {
   439 			fprintf(stderr, " - failed %ld\n", response);
   440                         unlink(file);
   441                         return -1;
   442 		}
   443 		fprintf(stderr, "\n");
   444 	}
   445 
   446 	curl_easy_cleanup(curl);
   447 
   448 	return 0;
   449 #endif	/* HAVE_CURL */
   450 }
   451 
   452 #define YUM_URL "http://download.fedora.redhat.com" \
   453 	"/pub/fedora/linux/development/i386/os"
   454 
   455 static int
   456 command_import_yum(int argc, const char *argv[])
   457 {
   458 	struct razor_set *set;
   459 	char buffer[512];
   460 
   461 	printf("downloading from %s.\n", yum_url);
   462 	snprintf(buffer, sizeof buffer,
   463 		 "%s/repodata/primary.xml.gz", yum_url);
   464 	if (download_if_missing(buffer, "primary.xml.gz") < 0)
   465 		return -1;
   466 	snprintf(buffer, sizeof buffer,
   467 		 "%s/repodata/filelists.xml.gz", yum_url);
   468 	if (download_if_missing(buffer, "filelists.xml.gz") < 0)
   469 		return -1;
   470 
   471 	set = razor_set_create_from_yum();
   472 	if (set == NULL)
   473 		return 1;
   474 	if (razor_set_write(set, rawhide_repo_filename, RAZOR_REPO_FILE_MAIN)) {
   475 		perror(rawhide_repo_filename);
   476 		return -1;
   477 	}
   478 	if (razor_set_write(set, "rawhide-details.rzdb",
   479 	    RAZOR_REPO_FILE_DETAILS)) {
   480 		perror("rawhide-details.rzdb");
   481 		return -1;
   482 	}
   483 	if (razor_set_write(set, "rawhide-files.rzdb", RAZOR_REPO_FILE_FILES)) {
   484 		perror("rawhide-files.rzdb");
   485 		return -1;
   486 	}
   487 	razor_set_destroy(set);
   488 	printf("wrote %s\n", rawhide_repo_filename);
   489 
   490 	return 0;
   491 }
   492 
   493 #if HAVE_RPMLIB
   494 static int
   495 command_import_rpmdb(int argc, const char *argv[])
   496 {
   497 	struct razor_set *set;
   498 	struct razor_root *root;
   499 
   500 	root = razor_root_open(install_root);
   501 	if (root == NULL)
   502 		return 1;
   503 
   504 	set = razor_set_create_from_rpmdb();
   505 	if (set == NULL)
   506 		return 1;
   507 
   508 	razor_root_update(root, set);
   509 
   510 	return razor_root_commit(root);
   511 }
   512 #endif
   513 
   514 static int
   515 mark_packages_for_update(struct razor_transaction *trans,
   516 			 struct razor_set *set, const char *pattern)
   517 {
   518 	struct razor_package_iterator *pi;
   519 	struct razor_package *package;
   520 	const char *name;
   521 	int matches = 0;
   522 
   523 	pi = razor_package_iterator_create(set);
   524 	while (razor_package_iterator_next(pi, &package,
   525 					   RAZOR_DETAIL_NAME, &name,
   526 					   RAZOR_DETAIL_LAST)) {
   527 		if (pattern && fnmatch(pattern, name, 0) == 0) {
   528 			razor_transaction_update_package(trans, package);
   529 			matches++;
   530 		}
   531 	}
   532 	razor_package_iterator_destroy(pi);
   533 
   534 	return matches;
   535 }
   536 
   537 static int
   538 mark_packages_for_removal(struct razor_transaction *trans,
   539 			  struct razor_set *set, const char *pattern)
   540 {
   541 	struct razor_package_iterator *pi;
   542 	struct razor_package *package;
   543 	const char *name;
   544 	int matches = 0;
   545 
   546 	pi = razor_package_iterator_create(set);
   547 	while (razor_package_iterator_next(pi, &package,
   548 					   RAZOR_DETAIL_NAME, &name,
   549 					   RAZOR_DETAIL_LAST)) {
   550 		if (pattern && fnmatch(pattern, name, 0) == 0) {
   551 			razor_transaction_remove_package(trans, package);
   552 			matches++;
   553 		}
   554 	}
   555 	razor_package_iterator_destroy(pi);
   556 
   557 	return matches;
   558 }
   559 
   560 static int
   561 command_update(int argc, const char *argv[])
   562 {
   563 	struct razor_set *set, *upstream;
   564 	struct razor_transaction *trans;
   565 	int i, errors;
   566 
   567 	set = razor_root_open_read_only(install_root);
   568 	if (set == NULL)
   569 		return 1;
   570 
   571 	upstream = razor_set_open(rawhide_repo_filename);
   572 	if (upstream == NULL ||
   573 	    razor_set_open_details(upstream, "rawhide-details.rzdb") ||
   574 	    razor_set_open_files(upstream, "rawhide-files.rzdb"))
   575 		return 1;
   576 
   577 	trans = razor_transaction_create(set, upstream);
   578 	if (argc == 0)
   579 		razor_transaction_update_all(trans);
   580 	for (i = 0; i < argc; i++) {
   581 		if (mark_packages_for_update(trans, set, argv[i]) == 0) {
   582 			fprintf(stderr, "no match for %s\n", argv[i]);
   583 			razor_transaction_destroy(trans);
   584 			return 1;
   585 		}
   586 	}
   587 
   588 	razor_transaction_resolve(trans);
   589 	errors = razor_transaction_describe(trans);
   590 	if (errors) {
   591 		fprintf(stderr, "unresolved dependencies\n");
   592 		razor_transaction_destroy(trans);
   593 		return 1;
   594 	}
   595 
   596 	set = razor_transaction_commit(trans);
   597 	razor_set_write(set, updated_repo_filename, RAZOR_REPO_FILE_MAIN);
   598 	razor_transaction_destroy(trans);
   599 	razor_set_destroy(set);
   600 	razor_set_destroy(upstream);
   601 	printf("wrote system-updated.rzdb\n");
   602 
   603 	return 0;
   604 }
   605 
   606 static int
   607 command_remove(int argc, const char *argv[])
   608 {
   609 	struct razor_root *root;
   610 	struct razor_set *system, *upstream, *next;
   611 	struct razor_transaction *trans;
   612 	int i, errors;
   613 
   614 	root = razor_root_open(install_root);
   615 	system = razor_root_get_system_set(root);
   616 	if (system == NULL) {
   617 		razor_root_close(root);
   618 		return 1;
   619 	}
   620 
   621 	upstream = razor_set_create_without_root();
   622 	trans = razor_transaction_create(system, upstream);
   623 	for (i = 0; i < argc; i++) {
   624 		if (mark_packages_for_removal(trans, system, argv[i]) == 0) {
   625 			fprintf(stderr, "no match for %s\n", argv[i]);
   626 			razor_transaction_destroy(trans);
   627 			razor_set_destroy(upstream);
   628 			razor_root_close(root);
   629 			return 1;
   630 		}
   631 	}
   632 
   633 	razor_transaction_resolve(trans);
   634 	errors = razor_transaction_describe(trans);
   635 	if (errors) {
   636 		razor_transaction_destroy(trans);
   637 		razor_set_destroy(upstream);
   638 		razor_root_close(root);
   639 		return 1;
   640 	}
   641 
   642 	next = razor_transaction_commit(trans);
   643 	update_packages(trans, system, next, NULL);
   644 	razor_root_update(root, next);
   645 
   646 	razor_transaction_destroy(trans);
   647 	razor_set_destroy(next);
   648 	razor_set_destroy(upstream);
   649 
   650 	return razor_root_commit(root);
   651 }
   652 
   653 static void
   654 print_diff(enum razor_diff_action action,
   655 	   struct razor_package *package,
   656 	   const char *name,
   657 	   const char *version,
   658 	   const char *arch,
   659 	   void *data)
   660 {
   661 	if (action == RAZOR_DIFF_ACTION_ADD)
   662 		printf("install %s-%s.%s\n", name, version, arch);
   663 	if (action == RAZOR_DIFF_ACTION_REMOVE)
   664 		printf("remove %s-%s.%s\n", name, version, arch);
   665 }
   666 
   667 static int
   668 command_diff(int argc, const char *argv[])
   669 {
   670 	struct razor_set *set, *updated;
   671 
   672 	set = razor_root_open_read_only(install_root);
   673 	updated = razor_set_open(updated_repo_filename);
   674 	if (set == NULL || updated == NULL)
   675 		return 1;
   676 
   677 	razor_set_diff(set, updated, print_diff, NULL);
   678 
   679 	razor_set_destroy(set);
   680 	razor_set_destroy(updated);
   681 
   682 	return 0;
   683 }
   684 
   685 static int
   686 command_import_rpms(int argc, const char *argv[])
   687 {
   688 	DIR *dir;
   689 	struct dirent *de;
   690 	struct razor_importer *importer;
   691 	struct razor_set *set;
   692 	struct razor_rpm *rpm;
   693 	int len, imported_count = 0;
   694 	char filename[256];
   695 	const char *dirname = argv[0];
   696 
   697 	if (dirname == NULL) {
   698 		fprintf(stderr, "usage: razor import-rpms DIR\n");
   699 		return -1;
   700 	}
   701 
   702 	dir = opendir(dirname);
   703 	if (dir == NULL) {
   704 		fprintf(stderr, "couldn't read dir %s\n", dirname);
   705 		return -1;
   706 	}
   707 
   708 	importer = razor_importer_create();
   709 
   710 	while (de = readdir(dir), de != NULL) {
   711 		len = strlen(de->d_name);
   712 		if (len < 5 || strcmp(de->d_name + len - 4, ".rpm") != 0)
   713 		    continue;
   714 		snprintf(filename, sizeof filename,
   715 			 "%s/%s", dirname, de->d_name);
   716 		rpm = razor_rpm_open(filename);
   717 		if (rpm == NULL) {
   718 			fprintf(stderr,
   719 				"failed to open rpm \"%s\"\n", filename);
   720 			continue;
   721 		}
   722 		if (razor_importer_add_rpm(importer, rpm)) {
   723 			fprintf(stderr, "couldn't import %s\n", filename);
   724 			break;
   725 		}
   726 		razor_rpm_close(rpm);
   727 
   728 		printf("\rimporting %d", ++imported_count);
   729 		fflush(stdout);
   730 	}
   731 
   732 	if (de != NULL) {
   733 		razor_importer_destroy(importer);
   734 		return -1;
   735 	}
   736 
   737 	printf("\nsaving\n");
   738 	set = razor_importer_finish(importer);
   739 
   740 	razor_set_write(set, repo_filename, RAZOR_REPO_FILE_MAIN);
   741 	razor_set_write(set, "system-details.rzdb", RAZOR_REPO_FILE_DETAILS);
   742 	razor_set_write(set, "system-files.rzdb", RAZOR_REPO_FILE_FILES);
   743 	razor_set_destroy(set);
   744 	printf("wrote %s\n", repo_filename);
   745 
   746 	return 0;
   747 }
   748 
   749 static const char *
   750 rpm_filename(const char *name, const char *version, const char *arch)
   751 {
   752 	static char file[PATH_MAX];
   753  	const char *v;
   754  
   755  	/* Skip epoch */
   756 	v = strchr(version, ':');
   757  	if (v != NULL)
   758  		v = v + 1;
   759  	else
   760 		v = version;
   761 
   762 	snprintf(file, sizeof file, "%s-%s.%s.rpm", name, v, arch);
   763 
   764 	return file;
   765 }
   766 
   767 static int
   768 download_packages(struct razor_set *system, struct razor_set *next)
   769 {
   770 	struct razor_install_iterator *ii;
   771 	struct razor_package *package;
   772 	struct razor_set *set;
   773 	enum razor_install_action action;
   774 	const char *name, *version, *arch;
   775 	char file[PATH_MAX], url[256];
   776 	int errors = 0, count;
   777 
   778 	ii = razor_set_create_install_iterator(system, next);
   779 	while (razor_install_iterator_next(ii, &set, &package,
   780 					   &action, &count)) {
   781 		if (action == RAZOR_INSTALL_ACTION_REMOVE)
   782 			continue;
   783 
   784 		razor_package_get_details(set, package,
   785 					  RAZOR_DETAIL_NAME, &name,
   786 					  RAZOR_DETAIL_VERSION, &version,
   787 					  RAZOR_DETAIL_ARCH, &arch,
   788 					  RAZOR_DETAIL_LAST);
   789 		
   790 		snprintf(url, sizeof url,
   791 			 "%s/Packages/%s",
   792 			 yum_url, rpm_filename(name, version, arch));
   793 		snprintf(file, sizeof file,
   794 			 "rpms/%s", rpm_filename(name, version, arch));
   795 		if (download_if_missing(url, file) < 0)
   796 			errors++;
   797 	}
   798 	razor_install_iterator_destroy(ii);
   799 
   800 	if (errors > 0) {
   801 		fprintf(stderr, "failed to download %d packages\n", errors);
   802                 return -1;
   803         }
   804 
   805 	return 0;
   806 }
   807 
   808 static struct razor_set *
   809 relocate_packages(struct razor_set *set, struct razor_relocations *relocations)
   810 {
   811 	struct razor_importer *importer;
   812 	struct razor_property_iterator *prop_iter;
   813 	struct razor_package_iterator *pkg_iter;
   814  	struct razor_file_iterator *file_iter;
   815  	struct razor_package *package;
   816 	struct razor_property *property;
   817 	struct razor_rpm *rpm;
   818 	const char *name, *version, *arch, *summary, *desc, *url, *license;
   819 	const char *preunprog, *preun, *postunprog, *postun;
   820 	char file[PATH_MAX];
   821 	uint32_t flags;
   822 
   823 	importer = razor_importer_create();
   824 	pkg_iter = razor_package_iterator_create(set);
   825 
   826 	while (razor_package_iterator_next(pkg_iter, &package,
   827 					   RAZOR_DETAIL_NAME, &name,
   828 					   RAZOR_DETAIL_VERSION, &version,
   829 					   RAZOR_DETAIL_ARCH, &arch,
   830 					   RAZOR_DETAIL_SUMMARY, &summary,
   831 					   RAZOR_DETAIL_DESCRIPTION, &desc,
   832 					   RAZOR_DETAIL_URL, &url,
   833 					   RAZOR_DETAIL_LICENSE, &license,
   834 					   RAZOR_DETAIL_PREUNPROG, &preunprog,
   835 					   RAZOR_DETAIL_PREUN, &preun,
   836 					   RAZOR_DETAIL_POSTUNPROG, &postunprog,
   837 					   RAZOR_DETAIL_POSTUN, &postun,
   838 					   RAZOR_DETAIL_LAST)) {
   839 		snprintf(file, sizeof file,
   840 			 "rpms/%s", rpm_filename(name, version, arch));
   841 		rpm = razor_rpm_open(file);
   842 		if (rpm == NULL) {
   843 			fprintf(stderr, "failed to open rpm %s\n", file);
   844 			razor_package_iterator_destroy(pkg_iter);
   845 			razor_importer_destroy(importer);
   846 			return NULL;
   847 		}
   848 
   849 		razor_relocations_set_rpm(relocations, rpm);
   850 		razor_rpm_close(rpm);
   851 
   852 		razor_importer_begin_package(importer, name, version, arch);
   853 		razor_importer_add_details(importer,
   854 					   summary, desc, url, license);
   855 
   856 		prop_iter = razor_property_iterator_create(set, package);
   857 		while (razor_property_iterator_next(prop_iter, &property,
   858 						    &name, &flags, &version))
   859 			razor_importer_add_property(importer,
   860 						    name, flags, version);
   861 		razor_property_iterator_destroy(prop_iter);
   862 
   863 		file_iter = razor_file_iterator_create(set, package);
   864 		while (razor_file_iterator_next(file_iter, &name)) {
   865 			name = razor_relocations_apply(relocations, name);
   866 			razor_importer_add_file(importer, name);
   867 		}
   868 		razor_file_iterator_destroy(file_iter);
   869 
   870 		razor_importer_add_script(importer, RAZOR_PROPERTY_PREUN,
   871 					  preunprog, preun);
   872 		razor_importer_add_script(importer, RAZOR_PROPERTY_POSTUN,
   873 					  postunprog, postun);
   874 
   875 		razor_importer_finish_package(importer);
   876 	}
   877 
   878 	razor_package_iterator_destroy(pkg_iter);
   879 	return razor_importer_finish(importer);
   880 }
   881 
   882 static int
   883 install_package(struct razor_transaction *trans, struct razor_set *set,
   884 		struct razor_package *package,
   885 		struct razor_relocations *relocations)
   886 {
   887 	int retval;
   888 	const char *name, *version, *arch;
   889 	char file[PATH_MAX];
   890 	struct razor_rpm *rpm;
   891 
   892 	razor_package_get_details(set, package,
   893 				  RAZOR_DETAIL_NAME, &name,
   894 				  RAZOR_DETAIL_VERSION, &version,
   895 				  RAZOR_DETAIL_ARCH, &arch,
   896 				  RAZOR_DETAIL_LAST);
   897 
   898 	printf("install %s-%s\n", name, version);
   899 
   900 	snprintf(file, sizeof file,
   901 		 "rpms/%s", rpm_filename(name, version, arch));
   902 	rpm = razor_rpm_open(file);
   903 	if (rpm == NULL) {
   904 		fprintf(stderr, "failed to open rpm %s\n", file);
   905 		return -1;
   906 	}
   907 	if (relocations)
   908 		razor_rpm_set_relocations(rpm, relocations);
   909 	razor_transaction_fixup_package(trans, package, rpm);
   910 	retval = razor_rpm_install(rpm, install_root);
   911 	if (retval < 0)
   912 		fprintf(stderr, "failed to install rpm %s\n", file);
   913 	razor_rpm_close(rpm);
   914 	return retval;
   915 }
   916 
   917 static int
   918 update_packages(struct razor_transaction *trans, struct razor_set *system,
   919 		struct razor_set *next, struct razor_relocations *relocations)
   920 {
   921 	struct razor_install_iterator *ii;
   922 	struct razor_package *package;
   923 	struct razor_set *set;
   924 	enum razor_install_action action;
   925 	int retval = 0, count;
   926 
   927 	ii = razor_set_create_install_iterator(system, next);
   928 	while (!retval && razor_install_iterator_next(ii, &set, &package,
   929 						      &action, &count)) {
   930 		if (action == RAZOR_INSTALL_ACTION_ADD)
   931 			retval = install_package(trans, set, package,
   932 						 relocations);
   933 		else if (action == RAZOR_INSTALL_ACTION_REMOVE)
   934 			retval = razor_package_remove(set, package,
   935 						      install_root);
   936 	}
   937 	razor_install_iterator_destroy(ii);
   938 
   939 	return retval;
   940 }
   941 
   942 static int
   943 command_install(int argc, const char *argv[])
   944 {
   945 	struct razor_root *root;
   946 	struct razor_relocations *relocations=NULL;
   947 	struct razor_set *system, *upstream, *next, *set;
   948 	struct razor_transaction *trans;
   949 	int i, len, dependencies = 1;
   950 	char *oldpath;
   951 
   952 	root = razor_root_open(install_root);
   953 	if (root == NULL)
   954 		return 1;
   955 
   956 	for (i = 0; i < argc; i++) {
   957 		if (strcmp(argv[i], "--no-dependencies") == 0)
   958 			dependencies = 0;
   959 		else if (strcmp(argv[i], "--relocate") == 0) {
   960 			i++;
   961 			if (i >= argc || strchr(argv[i], '=') == NULL) {
   962 				fprintf(stderr,
   963 				    "Usage: razor install [OPTION...] RPM\n");
   964 				fprintf(stderr, "Options:\n");
   965 				fprintf(stderr, "    [--no-dependencies]\n");
   966 				fprintf(stderr,
   967 				    "    [--relocate OLDPATH=NEWPATH] RPM\n");
   968 				return -1;
   969 			}
   970 			len = strchr(argv[i], '=') - argv[i];
   971 			oldpath = malloc(len + 1);
   972 			strncpy(oldpath, argv[i], len);
   973 			oldpath[len] = '\0';
   974 			if (!relocations)
   975 			       relocations = razor_relocations_create();
   976 			razor_relocations_add(relocations, oldpath,
   977 					      argv[i] + len + 1);
   978 			free(oldpath);
   979 		} else
   980 			break;
   981 	}
   982 
   983 	system = razor_root_get_system_set(root);
   984 	upstream = razor_set_open(rawhide_repo_filename);
   985 	if (upstream == NULL ||
   986 	    razor_set_open_details(upstream, "rawhide-details.rzdb") ||
   987 	    razor_set_open_files(upstream, "rawhide-files.rzdb")) {
   988 			fprintf(stderr, "couldn't open rawhide repo\n");
   989 			razor_root_close(root);
   990 			return 1;
   991 	}		
   992 
   993 	if (relocations) {
   994 		set = relocate_packages(upstream, relocations);
   995 		razor_set_destroy(upstream);
   996 		upstream = set;
   997 	}
   998 
   999 	trans = razor_transaction_create(system, upstream);
  1000 
  1001 	for (; i < argc; i++) {
  1002 		if (mark_packages_for_update(trans, upstream, argv[i]) == 0) {
  1003 			fprintf(stderr, "no package matched %s\n", argv[i]);
  1004 			razor_transaction_destroy(trans);
  1005 			razor_root_close(root);
  1006 			return 1;
  1007 		}
  1008 	}
  1009 
  1010 	if (dependencies) {
  1011 		razor_transaction_resolve(trans);
  1012 		if (razor_transaction_describe(trans) > 0) {
  1013 			razor_transaction_destroy(trans);
  1014 			razor_root_close(root);
  1015 			return 1;
  1016 		}
  1017 	}
  1018 
  1019 	next = razor_transaction_commit(trans);
  1020 
  1021 	if (mkdir("rpms", 0777) && errno != EEXIST) {
  1022 		fprintf(stderr, "failed to create rpms directory.\n");
  1023 		razor_transaction_destroy(trans);
  1024 		razor_root_close(root);
  1025 		return 1;
  1026 	}
  1027 
  1028 	if (download_packages(system, next) < 0) {
  1029 		razor_transaction_destroy(trans);
  1030 		razor_root_close(root);
  1031                 return 1;
  1032         }
  1033 
  1034 	update_packages(trans, system, next, relocations);
  1035 
  1036 	razor_root_update(root, next);
  1037 
  1038 	razor_transaction_destroy(trans);
  1039 	if (relocations)
  1040 		razor_relocations_destroy(relocations);
  1041 	razor_set_destroy(next);
  1042 	razor_set_destroy(upstream);
  1043 
  1044 	return razor_root_commit(root);
  1045 }
  1046 
  1047 static int
  1048 command_init(int argc, const char *argv[])
  1049 {
  1050 	return razor_root_create(install_root);
  1051 }
  1052 
  1053 static int
  1054 command_download(int argc, const char *argv[])
  1055 {
  1056 	struct razor_set *set;
  1057 	struct razor_package_iterator *pi;
  1058 	struct razor_package *package;
  1059 	const char *pattern = argv[0], *name, *version, *arch;
  1060 	char url[256], file[256];
  1061 	int matches = 0;
  1062 
  1063 	if (mkdir("rpms", 0777) && errno != EEXIST) {
  1064 		fprintf(stderr, "failed to create rpms directory.\n");
  1065 		return 1;
  1066 	}
  1067 
  1068 	set = razor_set_open(rawhide_repo_filename);
  1069 	pi = razor_package_iterator_create(set);
  1070 	while (razor_package_iterator_next(pi, &package,
  1071 					   RAZOR_DETAIL_NAME, &name,
  1072 					   RAZOR_DETAIL_VERSION, &version,
  1073 					   RAZOR_DETAIL_ARCH, &arch,
  1074 					   RAZOR_DETAIL_LAST)) {
  1075 		if (pattern && fnmatch(pattern, name, 0) != 0)
  1076 			continue;
  1077 
  1078 		matches++;
  1079 		snprintf(url, sizeof url,
  1080 			 "%s/Packages/%s-%s.%s.rpm",
  1081 			 yum_url, name, version, arch);
  1082 		snprintf(file, sizeof file,
  1083 			 "rpms/%s-%s.%s.rpm", name, version, arch);
  1084 		download_if_missing(url, file);
  1085 	}
  1086 	razor_package_iterator_destroy(pi);
  1087 	razor_set_destroy(set);
  1088 
  1089 	if (matches == 0)
  1090 		fprintf(stderr, "no packages matched \"%s\"\n", pattern);
  1091 	else if (matches == 1)
  1092 		fprintf(stderr, "downloaded 1 package\n");
  1093 	else
  1094 		fprintf(stderr, "downloaded %d packages\n", matches);
  1095 
  1096 	return 0;
  1097 }
  1098 
  1099 static int
  1100 command_info(int argc, const char *argv[])
  1101 {
  1102 	struct razor_set *set;
  1103 	struct razor_package_iterator *pi;
  1104 	struct razor_package *package;
  1105 	const char *pattern = argv[0], *name, *version, *arch;
  1106 	const char *summary, *description, *url, *license;
  1107 
  1108 	set = razor_root_open_read_only(install_root);
  1109 	if (set == NULL)
  1110 		return 1;
  1111 
  1112 	pi = razor_package_iterator_create(set);
  1113 	while (razor_package_iterator_next(pi, &package,
  1114 					   RAZOR_DETAIL_NAME, &name,
  1115 					   RAZOR_DETAIL_VERSION, &version,
  1116 					   RAZOR_DETAIL_ARCH, &arch,
  1117 					   RAZOR_DETAIL_LAST)) {
  1118 		if (pattern && fnmatch(pattern, name, 0) != 0)
  1119 			continue;
  1120 
  1121 		razor_package_get_details (set, package,
  1122 					   RAZOR_DETAIL_SUMMARY, &summary,
  1123 					   RAZOR_DETAIL_DESCRIPTION, &description,
  1124 					   RAZOR_DETAIL_URL, &url,
  1125 					   RAZOR_DETAIL_LICENSE, &license,
  1126 					   RAZOR_DETAIL_LAST);
  1127 
  1128 		printf ("Name:        %s\n", name);
  1129 		printf ("Arch:        %s\n", arch);
  1130 		printf ("Version:     %s\n", version);
  1131 		printf ("URL:         %s\n", url);
  1132 		printf ("License:     %s\n", license);
  1133 		printf ("Summary:     %s\n", summary);
  1134 		printf ("Description:\n");
  1135 		printf ("%s\n", description);
  1136 		printf ("\n");
  1137 	}
  1138 	razor_package_iterator_destroy(pi);
  1139 	razor_set_destroy(set);
  1140 
  1141 	return 0;
  1142 }
  1143 
  1144 #define SEARCH_MAX 256
  1145 
  1146 static int
  1147 command_search(int argc, const char *argv[])
  1148 {
  1149 	struct razor_set *set;
  1150 	struct razor_package_iterator *pi;
  1151 	struct razor_package *package;
  1152 	char pattern[SEARCH_MAX];
  1153 	const char *name, *version, *arch;
  1154 	const char *summary, *description, *url, *license;
  1155 
  1156 	if (!argv[0]) {
  1157 		fprintf(stderr, "must specify a search term\n");
  1158 		return 1;
  1159 	}
  1160 
  1161 	snprintf(pattern, sizeof pattern, "*%s*", argv[0]);
  1162 
  1163 	set = razor_set_open(rawhide_repo_filename);
  1164 	if (set == NULL)
  1165 		return 1;
  1166 
  1167 	if (razor_set_open_details(set, "rawhide-details.rzdb"))
  1168 		return 1;
  1169 
  1170 	pi = razor_package_iterator_create(set);
  1171 	while (razor_package_iterator_next(pi, &package,
  1172 					   RAZOR_DETAIL_NAME, &name,
  1173 					   RAZOR_DETAIL_VERSION, &version,
  1174 					   RAZOR_DETAIL_ARCH, &arch,
  1175 					   RAZOR_DETAIL_SUMMARY, &summary,
  1176 					   RAZOR_DETAIL_DESCRIPTION, &description,
  1177 					   RAZOR_DETAIL_URL, &url,
  1178 					   RAZOR_DETAIL_LICENSE, &license,
  1179 					   RAZOR_DETAIL_LAST)) {
  1180 		if (!fnmatch(pattern, name, FNM_CASEFOLD) ||
  1181 		    !fnmatch(pattern, url, FNM_CASEFOLD) ||
  1182 		    !fnmatch(pattern, summary, FNM_CASEFOLD) ||
  1183 		    !fnmatch(pattern, description, FNM_CASEFOLD))
  1184 			printf("%s-%s.%s: %s\n", name, version, arch, summary);
  1185 	}
  1186 	razor_package_iterator_destroy(pi);
  1187 	razor_set_destroy(set);
  1188 
  1189 	return 0;
  1190 }
  1191 
  1192 static struct {
  1193 	const char *name;
  1194 	const char *description;
  1195 	int (*func)(int argc, const char *argv[]);
  1196 } razor_commands[] = {
  1197 	{ "list", "list all packages", command_list },
  1198 	{ "list-requires", "list all requires for the given package", command_list_requires },
  1199 	{ "list-provides", "list all provides for the given package", command_list_provides },
  1200 	{ "list-obsoletes", "list all obsoletes for the given package", command_list_obsoletes },
  1201 	{ "list-conflicts", "list all conflicts for the given package", command_list_conflicts },
  1202 	{ "list-scripts", "list all scripts for the given package", command_list_scripts },
  1203 	{ "list-files", "list files for package set", command_list_files },
  1204 	{ "list-file-packages", "list packages owning file", command_list_file_packages },
  1205 	{ "list-package-files", "list files in package", command_list_package_files },
  1206 	{ "what-requires", "list the packages that have the given requires", command_what_requires },
  1207 	{ "what-provides", "list the packages that have the given provides", command_what_provides },
  1208 	{ "import-yum", "import yum metadata files", command_import_yum },
  1209 #if HAVE_RPMLIB
  1210 	{ "import-rpmdb", "import the system rpm database", command_import_rpmdb },
  1211 #endif
  1212 	{ "import-rpms", "import rpms from the given directory", command_import_rpms },
  1213 	{ "update", "update all or specified packages", command_update },
  1214 	{ "remove", "remove specified packages", command_remove },
  1215 	{ "diff", "show diff between two package sets", command_diff },
  1216 	{ "install", "install rpm", command_install },
  1217 	{ "init", "init razor root", command_init },
  1218 	{ "download", "download packages", command_download },
  1219 	{ "info", "display package details", command_info },
  1220 	{ "search", "search package details", command_search }
  1221 };
  1222 
  1223 static int
  1224 usage(void)
  1225 {
  1226 	int i;
  1227 
  1228 	printf("usage:\n");
  1229 	for (i = 0; i < ARRAY_SIZE(razor_commands); i++)
  1230 		printf("  %-20s%s\n",
  1231 		       razor_commands[i].name, razor_commands[i].description);
  1232 
  1233 	return 1;
  1234 }
  1235 
  1236 int
  1237 main(int argc, const char *argv[])
  1238 {
  1239 	char *repo, *root;
  1240 	int i;
  1241 
  1242 	repo = getenv("RAZOR_REPO");
  1243 	if (repo != NULL)
  1244 		repo_filename = repo;
  1245 
  1246 	root = getenv("RAZOR_ROOT");
  1247 	if (root != NULL)
  1248 		install_root = root;
  1249 
  1250 	yum_url = getenv("YUM_URL");
  1251 	if (yum_url == NULL)
  1252 		yum_url = YUM_URL;
  1253 
  1254 	if (getenv("RAZOR_NO_ROOT_NAME_CHECKS"))
  1255 		razor_disable_root_name_checks(1);
  1256 
  1257 	if (argc < 2)
  1258 		return usage();
  1259 
  1260 	for (i = 0; i < ARRAY_SIZE(razor_commands); i++)
  1261 		if (strcmp(razor_commands[i].name, argv[1]) == 0)
  1262 			return razor_commands[i].func(argc - 2, argv + 2);
  1263 
  1264 	return usage();
  1265 }