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