src/rpm.c
author James Bowes <jbowes@redhat.com>
Wed Jul 09 10:11:13 2008 -0400 (2008-07-09)
changeset 318 829d6711b316
parent 307 95b6bcadd6c4
child 369 f8c27fe9fe63
permissions -rw-r--r--
Use strings to identify section types in the on-disk repo format.

Previously, a given razor file type had a fixed number of sections in a
fixed order, identified by an integer type. Now, sections are identified
by a named string (stored in a string pool after the section lists).

This will allow for razor files to contain arbitrary sections.

For bonus points, also drop the 4k section alignment and change the
magic byte string to "RZDB".

committer: Kristian H?gsberg <krh@redhat.com>
     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 "config.h"
    21 
    22 #include <stdlib.h>
    23 #include <string.h>
    24 #include <stdio.h>
    25 #include <stdint.h>
    26 #include <dirent.h>
    27 #include "razor.h"
    28 
    29 enum option_type {
    30 	OPTION_LAST,
    31 	OPTION_GROUP,
    32 	OPTION_BOOL,
    33 	OPTION_STRING
    34 };
    35 
    36 struct option {
    37 	enum option_type type;
    38 	const char *name;
    39 	char short_name;
    40 	const char *arg_name;
    41 	const char *description;
    42 	void *data;
    43 };
    44 
    45 /* A note about all these options: rpm allows options to mean
    46  * different things depending on what other options are present on the
    47  * command line.  For example, if -q or --query is present, -i no
    48  * longer means install, but info.  The way we handle this is by
    49  * setting all the options that may match (ie if -i is given we set
    50  * install and info), and then look at the relevent one depending on
    51  * what else in on the command line. */
    52 
    53 static int option_all, option_list, option_whatrequires, option_whatprovides;
    54 static int option_package, option_file;
    55 
    56 static const struct option query_options[] = {
    57 	{ OPTION_BOOL, "configfiles", 'c', NULL, "list all configuration files", NULL },
    58 	{ OPTION_BOOL, "docfiles", 'd', NULL, "list all documentation files", NULL },
    59 	{ OPTION_BOOL, "dump", 0, NULL, "dump basic file information", NULL },
    60 	{ OPTION_BOOL, "list", 'l', NULL, "list files in package", &option_list },
    61 	{ OPTION_STRING, "queryformat", 0, "QUERYFORMAT", "use the following query format", NULL },
    62 	{ OPTION_BOOL, "state", 's', NULL, "display the states of the listed files", NULL },
    63 	{ OPTION_BOOL, "all", 'a', NULL, "query/verify all packages", &option_all },
    64 	{ OPTION_BOOL, "file", 'f', NULL, "query/verify package(s) owning file", &option_file },
    65 	{ OPTION_BOOL, "group", 'g', NULL, "query/verify package(s) in group", NULL },
    66 	{ OPTION_BOOL, "package", 'p', NULL, "query/verify a package file", &option_package },
    67 	{ OPTION_BOOL, "ftswalk", 'W', NULL, "query/verify package(s) from TOP file tree walk", NULL },
    68 	{ OPTION_BOOL, "pkgid", 0, NULL, "query/verify package(s) with package identifier", NULL },
    69 	{ OPTION_BOOL, "hdrid", 0, NULL, "query/verify package(s) with header identifier", NULL },
    70 	{ OPTION_BOOL, "fileid", 0, NULL, "query/verify package(s) with file identifier", NULL },
    71 	{ OPTION_BOOL, "specfile", 0, NULL, "query a spec file", NULL, },
    72 	{ OPTION_BOOL, "triggeredby", 0, NULL, "query the package(s) triggered by the package", NULL },
    73 	{ OPTION_BOOL, "whatrequires", 0, NULL, "query/verify the package(s) which require a dependency", &option_whatrequires },
    74 	{ OPTION_BOOL, "whatprovides", 0, NULL, "query/verify the package(s) which provide a dependency", &option_whatprovides },
    75 	{ OPTION_BOOL, "nomanifest", 0, NULL, "do not process non-package files as manifests", NULL },
    76 	{ }
    77 };
    78 
    79 static int option_nodeps;
    80 
    81 static const struct option verify_options[] = {
    82 	{ OPTION_BOOL, "nomd5", 0, NULL, "don't verify MD5 digest of files", NULL },
    83 	{ OPTION_BOOL, "nofiles", 0, NULL, "don't verify files in package", NULL },
    84 	{ OPTION_BOOL, "nodeps", 0, NULL, "don't verify package dependencies", &option_nodeps },
    85 	{ OPTION_BOOL, "noscript", 0, NULL, "don't execute verify script(s)", NULL, },
    86 	{ OPTION_BOOL, "all", 'a', NULL, "query/verify all packages", &option_all },
    87 	{ OPTION_BOOL, "file", 'f', NULL, "query/verify package(s) owning file", &option_file },
    88 	{ OPTION_BOOL, "group", 'g', NULL, "query/verify package(s) in group", NULL },
    89 	{ OPTION_BOOL, "package", 'p', NULL, "query/verify a package file", &option_package },
    90 	{ OPTION_BOOL, "ftswalk", 'W', NULL, "query/verify package(s) from TOP file tree walk", NULL },
    91 	{ OPTION_BOOL, "pkgid", 0, NULL, "query/verify package(s) with package identifier", NULL },
    92 	{ OPTION_BOOL, "hdrid", 0, NULL, "query/verify package(s) with header identifier", NULL },
    93 	{ OPTION_BOOL, "fileid", 0, NULL, "query/verify package(s) with file identifier", NULL },
    94 	{ OPTION_BOOL, "specfile", 0, NULL, "query a spec file", NULL },
    95 	{ OPTION_BOOL, "triggeredby", 0, NULL, "query the package(s) triggered by the package", NULL },
    96 	{ OPTION_BOOL, "whatrequires", 0, NULL, "query/verify the package(s) which require a dependency", &option_whatrequires },
    97 	{ OPTION_BOOL, "whatprovides", 0, NULL, "query/verify the package(s) which provide a dependency", &option_whatprovides },
    98 	{ OPTION_BOOL, "nomanifest", 0, NULL, "do not process non-package files as manifests", NULL },
    99 	{ }
   100 };
   101 
   102 static const struct option ftw_options[] = {
   103 	{ OPTION_BOOL, "comfollow", 0, NULL, "FTS_COMFOLLOW: follow command line symlinks", NULL },
   104 	{ OPTION_BOOL, "logical", 0, NULL, "FTS_LOGICAL: logical walk", NULL },
   105 	{ OPTION_BOOL, "nochdir", 0, NULL, "FTS_NOCHDIR: don't change directories", NULL },
   106 	{ OPTION_BOOL, "nostat", 0, NULL, "FTS_NOSTAT: don't get stat info", NULL },
   107 	{ OPTION_BOOL, "physical", 0, NULL, "FTS_PHYSICAL: physical walk", NULL },
   108 	{ OPTION_BOOL, "seedot", 0, NULL, "FTS_SEEDOT: return dot and dot-dot", NULL },
   109 	{ OPTION_BOOL, "xdev", 0, NULL, "FTS_XDEV: don't cross devices", NULL },
   110 	{ OPTION_BOOL, "whiteout", 0, NULL, "FTS_WHITEOUT: return whiteout information", NULL },
   111 	{ }
   112 };
   113 
   114 static const struct option signature_options[] = {
   115 	{ OPTION_BOOL, "addsign", 0, NULL, "sign package(s) (identical to --resign)", NULL, },
   116 	{ OPTION_BOOL, "checksig", 'K', NULL, "verify package signature(s)", NULL, },
   117 	{ OPTION_BOOL, "delsign", 0, NULL, "delete package signatures", NULL, },
   118 	{ OPTION_BOOL, "import", 0, NULL, "import an armored public key", NULL, },
   119 	{ OPTION_BOOL, "resign", 0, NULL, "sign package(s) (identical to --addsign)", NULL, },
   120 	{ OPTION_BOOL, "nodigest", 0, NULL, "don't verify package digest(s)", NULL, },
   121 	{ OPTION_BOOL, "nosignature", 0, NULL, "don't verify package signature(s)", NULL },
   122 	{ }
   123 };
   124 
   125 static int option_initdb;
   126 
   127 static const struct option database_options[] = {
   128 	{ OPTION_BOOL, "initdb", 0, NULL, "initialize database", &option_initdb },
   129 	{ OPTION_BOOL, "rebuilddb", 0, NULL, "rebuild database inverted lists from installed package headers", NULL },
   130 	{ }
   131 };
   132 
   133 static int option_erase, option_install, option_upgrade, option_justdb;
   134 static int option_test;
   135 
   136 static const struct option install_options[] = {
   137 	{ OPTION_BOOL, "aid", 0, NULL, "add suggested packages to transaction", NULL, },
   138 	{ OPTION_BOOL, "allfiles", 0, NULL, "install all files, even configurations which might otherwise be skipped", NULL, },
   139 	{ OPTION_BOOL, "allmatches", 0, NULL, "remove all packages which match <package> (normally an error is generated if <package> specified multiple packages)", NULL, },
   140 	{ OPTION_BOOL, "badreloc", 0, NULL, "relocate files in non-relocatable package", NULL },
   141 	{ OPTION_BOOL, "erase", 'e', "<package>", "erase (uninstall) package", &option_erase },
   142 	{ OPTION_BOOL, "excludedocs", 0, NULL, "do not install documentation", NULL, },
   143 	{ OPTION_BOOL, "excludepath", 0, "<path>", "skip files with leading component <path> ", NULL, },
   144 	{ OPTION_BOOL, "fileconflicts", 0, NULL, "detect file conflicts between packages", NULL, },
   145 	{ OPTION_BOOL, "force", 0, NULL, "short hand for --replacepkgs --replacefiles", NULL },
   146 	{ OPTION_BOOL, "freshen", 'F', "<packagefile>+", "upgrade package(s) if already installed", NULL },
   147 	{ OPTION_BOOL, "hash", 'h', NULL, "print hash marks as package installs (good with -v)", NULL },
   148 	{ OPTION_BOOL, "ignorearch", 0, NULL, "don't verify package architecture", NULL, },
   149 	{ OPTION_BOOL, "ignoreos", 0, NULL, "don't verify package operating system", NULL, },
   150 	{ OPTION_BOOL, "ignoresize", 0, NULL, "don't check disk space before installing", NULL },
   151 	{ OPTION_BOOL, "install", 'i', NULL, "install package(s)", &option_install },
   152 	{ OPTION_BOOL, "justdb", 0, NULL, "update the database, but do not modify the filesystem", &option_justdb, },
   153 	{ OPTION_BOOL, "nodeps", 0, NULL, "do not verify package dependencies", &option_nodeps, },
   154 	{ OPTION_BOOL, "nomd5", 0, NULL, "don't verify MD5 digest of files", NULL, },
   155 	{ OPTION_BOOL, "nocontexts", 0, NULL, "don't install file security contexts", NULL, },
   156 	{ OPTION_BOOL, "noorder", 0, NULL, "do not reorder package installation to satisfy dependencies", NULL, },
   157 	{ OPTION_BOOL, "nosuggest", 0, NULL, "do not suggest missing dependency resolution(s)", NULL, },
   158 	{ OPTION_BOOL, "noscripts", 0, NULL, "do not execute package scriptlet(s)", NULL, },
   159 	{ OPTION_BOOL, "notriggers", 0, NULL, "do not execute any scriptlet(s) triggered by this package", NULL, },
   160 	{ OPTION_BOOL, "oldpackage", 0, NULL, "upgrade to an old version of the package (--force on upgrades does this automatically)", NULL },
   161 	{ OPTION_BOOL, "percent", 0, NULL, "print percentages as package installs", NULL, },
   162 	{ OPTION_STRING, "prefix", 0, "<dir>", "relocate the package to <dir>, if relocatable", NULL, },
   163 	{ OPTION_STRING, "relocate", 0, "<old>=<new>", "relocate files from path <old> to <new>", NULL, },
   164 	{ OPTION_BOOL, "repackage", 0, NULL, "save erased package files by repackaging", NULL, },
   165 	{ OPTION_BOOL, "replacefiles", 0, NULL, "ignore file conflicts between packages", NULL, },
   166 	{ OPTION_BOOL, "replacepkgs", 0, NULL, "reinstall if the package is already present", NULL, },
   167 	{ OPTION_BOOL, "test", 0, NULL, "don't install, but tell if it would work or not", &option_test },
   168 	{ OPTION_BOOL, "upgrade", 'U', "<packagefile>+", "upgrade package(s)", &option_upgrade },
   169 	{ }
   170 };
   171 
   172 static int option_version;
   173 static const char *option_root = "install";
   174 
   175 static const struct option common_options[] = {
   176 	{ OPTION_STRING, "define", 'D', "MACRO EXPR", "define MACRO with value EXPR", NULL, },
   177 	{ OPTION_STRING, "eval", 'E', "EXPR", "print macro expansion of EXPR", NULL },
   178 	{ OPTION_STRING, "macros", 0, "<FILE:...>", "read <FILE:...> instead of default file(s)", NULL },
   179 	{ OPTION_BOOL, "nodigest", 0, NULL, "don't verify package digest(s)", NULL, },
   180 	{ OPTION_BOOL, "nosignature", 0, NULL, "don't verify package signature(s)", NULL, },
   181 	{ OPTION_STRING, "rcfile", 0, "<FILE:...>", "read <FILE:...> instead of default file(s)", NULL },
   182 	{ OPTION_STRING, "root", 'r', "ROOT", "use ROOT as top level directory (default: \"/\")", &option_root },
   183 	{ OPTION_BOOL, "querytags", 0, NULL, "display known query tags", NULL, },
   184 	{ OPTION_BOOL, "showrc", 0, NULL, "display final rpmrc and macro configuration", NULL, },
   185 	{ OPTION_BOOL, "quiet", 0, NULL, "provide less detailed output", NULL },
   186 	{ OPTION_BOOL, "verbose", 'v', NULL, "provide more detailed output", NULL },
   187 	{ OPTION_BOOL, "version", 0, NULL, "print the version of rpm being used", &option_version },
   188 	{ }
   189 };
   190 
   191 static int option_conflicts, option_obsoletes, option_requires;
   192 static int option_provides, option_info, option_changelog;
   193 
   194 static const struct option alias_options[] = {
   195 	{ OPTION_BOOL, "scripts", 0, NULL, "list install/erase scriptlets from package(s)", NULL, },
   196 	{ OPTION_BOOL, "setperms", 0, NULL, "set permissions of files in a package", NULL, },
   197 	{ OPTION_BOOL, "setugids", 0, NULL, "set user/group ownership of files in a package", NULL, },
   198 	{ OPTION_BOOL, "conflicts", 0, NULL, "list capabilities this package conflicts with", &option_conflicts, },
   199 	{ OPTION_BOOL, "obsoletes", 0, NULL, "list other packages removed by installing this package", &option_obsoletes, },
   200 	{ OPTION_BOOL, "provides", 0, NULL, "list capabilities that this package provides", &option_provides, },
   201 	{ OPTION_BOOL, "requires", 'R', NULL, "list capabilities required by package(s)", &option_requires, },
   202 	{ OPTION_BOOL, "info", 'i', NULL, "list descriptive information from package(s)", &option_info, },
   203 	{ OPTION_BOOL, "changelog", 0, NULL, "list change logs for this package", &option_changelog, },
   204 	{ OPTION_BOOL, "xml", 0, NULL, "list metadata in xml", NULL, },
   205 	{ OPTION_BOOL, "triggers", 0, NULL, "list trigger scriptlets from package(s)", NULL, },
   206 	{ OPTION_BOOL, "last", 0, NULL, "list package(s) by install time, most recent first", NULL, },
   207 	{ OPTION_BOOL, "dupes", 0, NULL, "list duplicated packages", NULL, },
   208 	{ OPTION_BOOL, "filesbypkg", 0, NULL, "list all files from each package", NULL, },
   209 	{ OPTION_BOOL, "fileclass", 0, NULL, "list file names with classes", NULL, },
   210 	{ OPTION_BOOL, "filecolor", 0, NULL, "list file names with colors", NULL, },
   211 	{ OPTION_BOOL, "filecontext", 0, NULL, "list file names with security context from header", NULL, },
   212 	{ OPTION_BOOL, "fscontext", 0, NULL, "list file names with security context from file system", NULL, },
   213 	{ OPTION_BOOL, "recontext", 0, NULL, "list file names with security context from policy RE", NULL, },
   214 	{ OPTION_BOOL, "fileprovide", 0, NULL, "list file names with provides", NULL, },
   215 	{ OPTION_BOOL, "filerequire", 0, NULL, "list file names with requires", NULL, },
   216 	{ OPTION_BOOL, "redhatprovides", 0, NULL, "find package name that contains a provided capability (needs rpmdb-redhat package installed)", NULL, },
   217 	{ OPTION_BOOL, "redhatrequires", 0, NULL, "find package name that contains a required capability (needs rpmdb-redhat package installed)", NULL, },
   218 	{ OPTION_STRING, "buildpolicy", 0, "<policy>", "set buildroot <policy> (e.g. compress man pages)", NULL, },
   219 	{ OPTION_BOOL, "with", 0, "<option>", "enable configure <option> for build", NULL, },
   220 	{ OPTION_BOOL, "without", 0, "<option>", "disable configure <option> for build", NULL },
   221 	{ }
   222 };
   223 
   224 static int option_help, option_usage;
   225 
   226 static const struct option help_options[] = {
   227 	{ OPTION_BOOL, "help", '?', NULL, "Show this help message", &option_help },
   228 	{ OPTION_BOOL, "usage", 0, NULL, "Display brief usage message", &option_usage},
   229 	{ }
   230 };
   231 
   232 static int option_query, option_verify;
   233 
   234 static const struct option rpm_options[] = {
   235 	{ OPTION_BOOL, "query", 'q', NULL, "Query rpm database", &option_query },
   236 	{ OPTION_BOOL, "verify", 'V', NULL, "Verify rpm database", &option_verify },
   237 	{ OPTION_GROUP, NULL, 0, NULL, "Query options (with -q or --query):", &query_options },
   238 	{ OPTION_GROUP, NULL, 0, NULL, "Verify options (with -V or --verify):", &verify_options },
   239 	{ OPTION_GROUP, NULL, 0, NULL, "File tree walk options (with --ftswalk):", &ftw_options },
   240 	{ OPTION_GROUP, NULL, 0, NULL, "Signature options:", &signature_options },
   241 	{ OPTION_GROUP, NULL, 0, NULL, "Database options:", &database_options },
   242 	{ OPTION_GROUP, NULL, 0, NULL, "Install/Upgrade/Erase options:", &install_options },
   243 	{ OPTION_GROUP, NULL, 0, NULL, "Common options for all rpm modes and executables:", &common_options },
   244 	{ OPTION_GROUP, NULL, 0, NULL, "Options implemented via popt alias/exec:", &alias_options },
   245 	{ OPTION_GROUP, NULL, 0, NULL, "Help options", &help_options },
   246 	{ }
   247 };
   248 
   249 static const char system_repo_filename[] = "system.rzdb";
   250 static const char *repo_filename = system_repo_filename;
   251 
   252 static void
   253 command_initdb(int argc, const char *argv[])
   254 {
   255 	razor_root_create(option_root);
   256 }
   257 
   258 static struct razor_property *
   259 add_property_packages(struct razor_set *set,
   260 		      struct razor_package_query *query,
   261 		      const char *ref_name,
   262 		      const char *ref_version,
   263 		      uint32_t ref_type)
   264 {
   265 	struct razor_property *property;
   266 	struct razor_property_iterator *pi;
   267 	struct razor_package_iterator *pkgi;
   268 	const char *name, *version;
   269 	uint32_t flags;
   270 
   271 	pi = razor_property_iterator_create(set, NULL);
   272 	while (razor_property_iterator_next(pi, &property, &name,
   273 					    &flags, &version)) {
   274 		if (strcmp(ref_name, name) != 0)
   275 			continue;
   276 		if (ref_version &&
   277 		    (flags & RAZOR_PROPERTY_RELATION_MASK) == RAZOR_PROPERTY_EQUAL &&
   278 		    strcmp(ref_version, version) != 0)
   279 			continue;
   280 		if ((flags & RAZOR_PROPERTY_TYPE_MASK) != ref_type)
   281 			continue;
   282 
   283 		pkgi = razor_package_iterator_create_for_property(set,
   284 								  property);
   285 		razor_package_query_add_iterator(query, pkgi);
   286 		razor_package_iterator_destroy(pkgi);
   287 	}
   288 	razor_property_iterator_destroy(pi);
   289 
   290 	return property;
   291 }
   292 
   293 static int
   294 strcmpp(const void *p1, const void *p2)
   295 {
   296 	return strcmp(*(char * const *) p1, *(char * const *) p2);
   297 }
   298 
   299 static void
   300 add_command_line_packages(struct razor_set *set,
   301 			  struct razor_package_query *query,
   302 			  int argc, const char **argv)
   303 {
   304 	struct razor_package *package;
   305 	struct razor_package_iterator *pi;
   306 	const char *name;
   307 	int i, cmp, errors;
   308 
   309 	qsort(argv, argc, sizeof(*argv), strcmpp);
   310 	i = 0;
   311 	errors = 0;
   312 
   313 	pi = razor_package_iterator_create(set);
   314 
   315 	while (razor_package_iterator_next(pi, &package,
   316 					   RAZOR_DETAIL_NAME, &name,
   317 					   RAZOR_DETAIL_LAST)) {
   318 		while (cmp = strcmp(argv[i], name), cmp < 0 && i < argc) {
   319 			fprintf(stderr, "error: package %s is not installed\n",
   320 				argv[i]);
   321 			errors++;
   322 			i++;
   323 		}
   324 
   325 		if (cmp == 0) {
   326 			razor_package_query_add_package(query, package);
   327 			i++;
   328 		}
   329 	}
   330 
   331 	razor_package_iterator_destroy(pi);
   332 
   333 	if (errors)
   334 		exit(1);
   335 }
   336 
   337 static struct razor_package_iterator *
   338 get_query_packages(struct razor_set *set, int argc, const char *argv[])
   339 {
   340 	struct razor_package_query *query;
   341 	struct razor_package_iterator *pi;
   342 	char *files;
   343 	int i;
   344 
   345 	if (option_all + option_whatprovides + option_whatrequires +
   346 	    option_file > 1) {
   347 		printf("only one type of query/verify "
   348 		       "may be performed at a time\n");
   349 		exit(1);
   350 	}
   351 
   352 	files = "install/var/lib/razor/system-files.rzdb";
   353 	if (option_file)
   354 		if (razor_set_open_files(set, files))
   355 			exit(1);
   356 
   357 	query = razor_package_query_create(set);
   358 
   359 	if (option_all) {
   360 		pi = razor_package_iterator_create(set);
   361 		razor_package_query_add_iterator(query, pi);
   362 		razor_package_iterator_destroy(pi);
   363 	} else if (option_whatrequires) {
   364 		for (i = 0; i < argc; i++)
   365 			add_property_packages(set, query,
   366 					      argv[i], NULL,
   367 					      RAZOR_PROPERTY_REQUIRES);
   368 	} else if (option_whatprovides) {
   369 		for (i = 0; i < argc; i++)
   370 			add_property_packages(set, query,
   371 					      argv[i], NULL,
   372 					      RAZOR_PROPERTY_PROVIDES);
   373 	} else if (option_file) {
   374 		for (i = 0; i < argc; i++) {
   375 			pi = razor_package_iterator_create_for_file(set,
   376 								    argv[i]);
   377 			razor_package_query_add_iterator(query, pi);
   378 			razor_package_iterator_destroy(pi);
   379 		}
   380 	} else if (argc > 0) {
   381 		add_command_line_packages(set, query, argc, argv);
   382 	} else {
   383 		printf("no arguments given for query/verify\n");
   384 		exit(1);
   385 	}
   386 
   387 	return razor_package_query_finish(query);
   388 }
   389 
   390 static void
   391 print_package_properties(struct razor_set *set,
   392 			 struct razor_package *package,
   393 			 uint32_t ref_type)
   394 {
   395 	struct razor_property *property;
   396 	struct razor_property_iterator *pi;
   397 	const char *name, *version;
   398 	uint32_t flags;
   399 
   400 	pi = razor_property_iterator_create(set, package);
   401 	while (razor_property_iterator_next(pi, &property,
   402 					    &name, &flags, &version)) {
   403 		if ((flags & RAZOR_PROPERTY_TYPE_MASK) != ref_type)
   404 			continue;
   405 		if (version[0] == '\0')
   406 			printf("%s\n", name);
   407 		else
   408 			printf("%s %s %s\n", name,
   409 			       razor_property_relation_to_string(property),
   410 			       version);
   411 	}
   412 	razor_property_iterator_destroy(pi);
   413 }
   414 
   415 static void
   416 print_package_info(struct razor_set *set, struct razor_package *package,
   417 		   const char *name, const char *version, const char *arch)
   418 {
   419 	const char *summary, *description, *url, *license;
   420 
   421 	razor_package_get_details (set, package,
   422 				   RAZOR_DETAIL_SUMMARY, &summary,
   423 				   RAZOR_DETAIL_DESCRIPTION, &description,
   424 				   RAZOR_DETAIL_URL, &url,
   425 				   RAZOR_DETAIL_LICENSE, &license,
   426 				   RAZOR_DETAIL_LAST);
   427 
   428 	printf("Name:        %s\n", name);
   429 	printf("Arch:        %s\n", arch);
   430 	printf("Version:     %s\n", version);
   431 	printf("URL:         %s\n", url);
   432 	printf("License:     %s\n", license);
   433 	printf("Summary:     %s\n", summary);
   434 	printf("Description:\n");
   435 	printf("%s\n", description);
   436 	printf("\n");
   437 }
   438 
   439 static void
   440 print_package_changelog(struct razor_set *set, struct razor_package *package)
   441 {
   442 	printf("FIXME: Package changelog not tracked.\n");
   443 }
   444 
   445 static struct razor_set *
   446 create_set_from_command_line(int argc, const char *argv[])
   447 {
   448 	struct razor_importer *importer;
   449 	struct razor_rpm *rpm;
   450 	int i;
   451 
   452 	importer = razor_importer_create();
   453 
   454 	for (i = 0; i < argc; i++) {
   455 		rpm = razor_rpm_open(argv[i]);
   456 		if (rpm == NULL)
   457 			continue;
   458 		if (razor_importer_add_rpm(importer, rpm))
   459 			printf("couldn't import %s\n", argv[i]);
   460 
   461 		razor_rpm_close(rpm);
   462 	}
   463 
   464 	return razor_importer_finish(importer);
   465 }
   466 
   467 static void
   468 command_query(int argc, const char *argv[])
   469 {
   470 	struct razor_set *set;
   471 	struct razor_package_iterator *pi;
   472 	struct razor_package *package;
   473 	const char *name, *version, *arch, *details, *files;
   474 
   475 	if (option_package) {
   476 		set = create_set_from_command_line(argc, argv);
   477 		argc = 0;
   478 		option_all = 1;
   479 	} else {
   480 		set = razor_root_open_read_only(option_root);
   481 
   482 		/* FIXME: We need to figure out how to do this right. */
   483 		details = "install/var/lib/razor/system-details.rzdb";
   484 		if (option_info)
   485 			if (razor_set_open_details(set, details))
   486 				return;
   487 		files = "install/var/lib/razor/system-files.rzdb";
   488 		if (option_list)
   489 			if (razor_set_open_files(set, files))
   490 				return;
   491 	}
   492 
   493 	pi = get_query_packages(set, argc, argv);
   494 
   495 	while (razor_package_iterator_next(pi, &package,
   496 					   RAZOR_DETAIL_NAME, &name,
   497 					   RAZOR_DETAIL_VERSION, &version,
   498 					   RAZOR_DETAIL_ARCH, &arch,
   499 					   RAZOR_DETAIL_LAST)) {
   500 		if (option_conflicts)
   501 			print_package_properties(set, package,
   502 						 RAZOR_PROPERTY_CONFLICTS);
   503 		if (option_obsoletes)
   504 			print_package_properties(set, package,
   505 						 RAZOR_PROPERTY_OBSOLETES);
   506 		if (option_requires)
   507 			print_package_properties(set, package,
   508 						 RAZOR_PROPERTY_REQUIRES);
   509 		if (option_provides)
   510 			print_package_properties(set, package,
   511 						 RAZOR_PROPERTY_PROVIDES);
   512 		if (option_info)
   513 			print_package_info(set, package, name, version, arch);
   514 		if (option_changelog)
   515 			print_package_changelog(set, package);
   516 		if (option_list)
   517 			razor_set_list_package_files(set, package);
   518 
   519 		if (option_conflicts + option_obsoletes +
   520 		    option_requires + option_provides +
   521 		    option_info + option_changelog + option_list == 0)
   522 			printf("%s-%s.%s\n", name, version, arch);
   523 	}
   524 
   525 	razor_package_iterator_destroy(pi);
   526 
   527 	razor_set_destroy(set);
   528 
   529 	return;
   530 }
   531 
   532 static void
   533 command_verify(int argc, const char *argv[])
   534 {
   535 	struct razor_set *set;
   536 	struct razor_package_iterator *pi;
   537 	struct razor_package *package;
   538 	const char *name, *version, *arch;
   539 
   540 	if (option_package) {
   541 		set = create_set_from_command_line(argc, argv);
   542 		argc = 0;
   543 		option_all = 1;
   544 	} else {
   545 		set = razor_root_open_read_only(option_root);
   546 	}
   547 
   548 	pi = get_query_packages(set, argc, argv);
   549 
   550 	while (razor_package_iterator_next(pi, &package,
   551 					   RAZOR_DETAIL_NAME, &name,
   552 					   RAZOR_DETAIL_VERSION, &version,
   553 					   RAZOR_DETAIL_ARCH, &arch,
   554 					   RAZOR_DETAIL_LAST)) {
   555 		printf("verify %s-%s.%s - not implemented\n",
   556 		       name, version, arch);
   557 	}
   558 
   559 	razor_package_iterator_destroy(pi);
   560 }
   561 
   562 static void
   563 update_package(enum razor_diff_action action,
   564 	       struct razor_package *package,
   565 	       const char *name,
   566 	       const char *version,
   567 	       const char *arch,
   568 	       void *data)
   569 {
   570 	if (action == RAZOR_DIFF_ACTION_ADD)
   571 		printf("install %s-%s.%s\n", name, version, arch);
   572 	if (action == RAZOR_DIFF_ACTION_REMOVE)
   573 		printf("remove %s-%s.%s\n", name, version, arch);
   574 }
   575 
   576 static void
   577 command_erase(int argc, const char *argv[])
   578 {
   579 	struct razor_set *set, *upstream, *next;
   580 	struct razor_transaction *trans;
   581 	struct razor_package_query *query;
   582 	struct razor_package_iterator *pi;
   583 	struct razor_package *package;
   584 
   585 	if (argc == 0) {
   586 		printf("no packages given for erase\n");
   587 		exit(1);
   588 	}
   589 
   590 	set = razor_set_open(repo_filename);
   591 	upstream = razor_set_create();
   592 
   593 	trans = razor_transaction_create(set, upstream);
   594 
   595 	query = razor_package_query_create(set);
   596 	add_command_line_packages(set, query, argc, argv);
   597 
   598 	pi = razor_package_query_finish(query);
   599 	while (razor_package_iterator_next(pi, &package, RAZOR_DETAIL_LAST))
   600 		razor_transaction_remove_package(trans, package);
   601 	razor_package_iterator_destroy(pi);
   602 
   603 	if (!option_nodeps && razor_transaction_describe(trans) > 0) {
   604 		printf("unsatisfied dependencies.\n");
   605 		exit(1);
   606 	}
   607 
   608 	if (option_test)
   609 		exit(0);
   610 
   611 	next = razor_transaction_finish(trans);
   612 
   613 	if (!option_justdb)
   614 		razor_set_diff(set, next, update_package, NULL);
   615 
   616 	razor_set_destroy(set);
   617 	razor_set_destroy(upstream);
   618 
   619 	razor_set_destroy(next);
   620 }
   621 
   622 static void
   623 command_install(int argc, const char *argv[])
   624 {
   625 	struct razor_set *set, *upstream, *next;
   626 	struct razor_transaction *trans;
   627 	struct razor_package_iterator *pi;
   628 	struct razor_package *package;
   629 
   630 	if (argc == 0) {
   631 		printf("no packages given for install\n");
   632 		exit(1);
   633 	}
   634 
   635 	set = razor_set_open(repo_filename);
   636 	upstream = create_set_from_command_line(argc, argv);
   637 
   638 	trans = razor_transaction_create(set, upstream);
   639 
   640 	pi = razor_package_iterator_create(upstream);
   641 	while (razor_package_iterator_next(pi, &package, RAZOR_DETAIL_LAST))
   642 		razor_transaction_install_package(trans, package);
   643 	razor_package_iterator_destroy(pi);
   644 
   645 	if (!option_nodeps && razor_transaction_describe(trans) > 0) {
   646 		printf("unsatisfied dependencies.\n");
   647 		exit(1);
   648 	}
   649 
   650 	if (option_test)
   651 		exit(0);
   652 
   653 	next = razor_transaction_finish(trans);
   654 
   655 	if (!option_justdb)
   656 		razor_set_diff(set, next, update_package, NULL);
   657 
   658 	razor_set_destroy(set);
   659 	razor_set_destroy(upstream);
   660 
   661 	razor_set_destroy(next);
   662 }
   663 
   664 static void
   665 command_update(int argc, const char *argv[])
   666 {
   667 	struct razor_set *set, *upstream, *next;
   668 	struct razor_transaction *trans;
   669 	struct razor_package_iterator *pi;
   670 	struct razor_package *package;
   671 
   672 	if (argc == 0) {
   673 		printf("no packages given for update\n");
   674 		exit(1);
   675 	}
   676 
   677 	set = razor_set_open(repo_filename);
   678 	upstream = create_set_from_command_line(argc, argv);
   679 
   680 	trans = razor_transaction_create(set, upstream);
   681 
   682 	pi = razor_package_iterator_create(upstream);
   683 	while (razor_package_iterator_next(pi, &package, RAZOR_DETAIL_LAST))
   684 		razor_transaction_update_package(trans, package);
   685 	razor_package_iterator_destroy(pi);
   686 
   687 	if (!option_nodeps && razor_transaction_describe(trans) > 0) {
   688 		printf("unsatisfied dependencies.\n");
   689 		exit(1);
   690 	}
   691 
   692 	if (option_test)
   693 		exit(0);
   694 
   695 	next = razor_transaction_finish(trans);
   696 
   697 	if (!option_justdb)
   698 		razor_set_diff(set, next, update_package, NULL);
   699 
   700 	razor_set_destroy(set);
   701 	razor_set_destroy(upstream);
   702 
   703 	razor_set_destroy(next);
   704 }
   705 
   706 static int
   707 for_each_option(const struct option *options,
   708 		const char *name, char short_name,
   709 		void (*fn)(const struct option *o,
   710 			   const char *name, char short_name,
   711 			   void *data), void *data)
   712 {
   713 	int i, count = 0;
   714 
   715 	for (i = 0; options[i].type != OPTION_LAST; i++) {
   716 		switch (options[i].type) {
   717 		case OPTION_GROUP:
   718 			count += for_each_option(options[i].data,
   719 						 name, short_name, fn, data);
   720 			break;
   721 
   722 		case OPTION_BOOL:
   723 		case OPTION_STRING:
   724 			if (name && strcmp(options[i].name, name) == 0) {
   725 				fn(&options[i], name, 0, data);
   726 				count++;
   727 				break;
   728 			}
   729 
   730 			if (short_name &&
   731 			    short_name == options[i].short_name) {
   732 				fn(&options[i], NULL, short_name, data);
   733 				count++;
   734 				break;
   735 			}
   736 			break;
   737 
   738 		case OPTION_LAST:
   739 			break;
   740 		}
   741 	}
   742 
   743 	return count;
   744 }
   745 
   746 static void
   747 handle_option(const struct option *o,
   748 	      const char *name, char short_name, void *data)
   749 {
   750 	if (o->data == NULL) {
   751 		if (name)
   752 			printf("option --%s not supported\n", name);
   753 		else
   754 			printf("option -%c not supported\n", short_name);
   755 		return;
   756 	}
   757 
   758 	switch (o->type) {
   759 	case OPTION_BOOL:
   760 		*(int *) o->data = 1;
   761 		break;
   762 
   763 	case OPTION_STRING:
   764 		*(const char **) o->data = name + strlen(o->name) + 1;
   765 		break;
   766 
   767 	case OPTION_LAST:
   768 	case OPTION_GROUP:
   769 		/* Shouldn't happen. */
   770 		break;
   771 	}
   772 }
   773 
   774 static int
   775 parse_options(const struct option *options, int argc, const char **argv)
   776 {
   777 	int i, j, k;
   778 
   779 	for (i = 1, j = 0; i < argc; i++) {
   780 		if (argv[i][0] != '-') {
   781 			argv[j++] = argv[i];
   782 			continue;
   783 		}
   784 
   785 		if (argv[i][1] == '-') {
   786 			if (for_each_option(options, &argv[i][2], 0,
   787 					    handle_option, NULL) == 0) {
   788 				printf("unknown option: %s\n", argv[i]);
   789 				exit(1);
   790 			}
   791 			continue;
   792 		}
   793 
   794 		for (k = 1; argv[i][k]; k++) {
   795 			if (for_each_option(options, NULL, argv[i][k],
   796 					    handle_option, NULL) == 0) {
   797 				printf("unknown option: -%c\n", argv[i][k]);
   798 				exit(1);
   799 			}
   800 		}
   801 	}
   802 
   803 	return j;
   804 }
   805 
   806 static void
   807 print_options_help(const struct option *options)
   808 {
   809 	int i;
   810 
   811 	for (i = 0; options[i].type != OPTION_LAST; i++) {
   812 		switch (options[i].type) {
   813 		case OPTION_GROUP:
   814 			printf("%s\n", options[i].description);
   815 			print_options_help(options[i].data);
   816 			printf("\n");
   817 			break;
   818 
   819 		case OPTION_BOOL:
   820 		case OPTION_STRING:
   821 			printf("  ");
   822 			if (options[i].short_name)
   823 				printf("-%c", options[i].short_name);
   824 			if (options[i].short_name && options[i].name)
   825 				printf(", ");
   826 			if (options[i].name)
   827 				printf("--%s", options[i].name);
   828 			if (options[i].arg_name)
   829 				printf("=%s", options[i].arg_name);
   830 			if (options[i].description)
   831 				printf("\t\t%s", options[i].description);
   832 			printf("\n");
   833 			break;
   834 
   835 		case OPTION_LAST:
   836 			break;
   837 		}
   838 	}
   839 }
   840 
   841 static void
   842 print_options_usage(const struct option *options)
   843 {
   844 	int i;
   845 
   846 	for (i = 0; options[i].type != OPTION_LAST; i++) {
   847 		switch (options[i].type) {
   848 		case OPTION_GROUP:
   849 			print_options_usage(options[i].data);
   850 			break;
   851 
   852 		case OPTION_BOOL:
   853 			printf("[");
   854 			if (options[i].short_name)
   855 				printf("-%c", options[i].short_name);
   856 			if (options[i].short_name && options[i].name)
   857 				printf("|");
   858 			if (options[i].name)
   859 				printf("--%s", options[i].name);
   860 			printf("] ");
   861 			break;
   862 
   863 		case OPTION_STRING:
   864 			printf("[");
   865 			if (options[i].short_name)
   866 				printf("-%c", options[i].short_name);
   867 			if (options[i].short_name && options[i].name)
   868 				printf("|");
   869 			if (options[i].name)
   870 				printf("--%s", options[i].name);
   871 			if (options[i].arg_name)
   872 				printf("=%s", options[i].arg_name);
   873 			printf("] ");
   874 			break;
   875 
   876 
   877 			break;
   878 
   879 		case OPTION_LAST:
   880 			break;
   881 		}
   882 	}
   883 }
   884 
   885 int
   886 main(int argc, const char *argv[])
   887 {
   888 	argc = parse_options(rpm_options, argc, argv);
   889 
   890 	if (option_version) {
   891 		printf("razor rpm version " VERSION "\n");
   892 		exit(0);
   893 	}
   894 
   895 	if (option_help) {
   896 		printf("Usage: rpm [OPTION...]\n");
   897 		print_options_help(rpm_options);
   898 		exit(0);
   899 	}
   900 
   901 	if (option_usage) {
   902 		printf("Usage: rpm [OPTION...]\n");
   903 		print_options_usage(rpm_options);
   904 		printf("\n");
   905 		exit(0);
   906 	}
   907 
   908 	if (option_initdb) {
   909 		command_initdb(argc, argv);
   910 	} else if (option_verify) {
   911 		command_verify(argc, argv);
   912 	} else if (option_query) {
   913 		command_query(argc, argv);
   914 	} else if (option_install) {
   915 		command_install(argc, argv);
   916 	} else if (option_upgrade) {
   917 		command_update(argc, argv);
   918 	} else if (option_erase) {
   919 		command_erase(argc, argv);
   920 	} else {
   921 		print_options_usage(rpm_options);
   922 		printf("\n");
   923 		exit(0);
   924 	}
   925 
   926 	return 0;
   927 }