librazor/razor.c
author J. Ali Harlow <ali@juiblex.co.uk>
Tue Jul 07 22:50:22 2009 +0100 (2009-07-07)
changeset 376 d15a16347c77
parent 375 c903635ae422
child 377 5549419824b4
permissions -rw-r--r--
Pass installation-count to scripts
     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 <stdint.h>
    28 #include <stdio.h>
    29 #include <stdarg.h>
    30 #include <string.h>
    31 #include <sys/types.h>
    32 #include <sys/stat.h>
    33 #include <unistd.h>
    34 #include <fcntl.h>
    35 #include <errno.h>
    36 #include <ctype.h>
    37 #include <fnmatch.h>
    38 #include <limits.h>
    39 #include <assert.h>
    40 
    41 #include "razor-internal.h"
    42 #include "razor.h"
    43 
    44 #ifndef O_BINARY
    45 #define O_BINARY	0
    46 #endif
    47 
    48 void *
    49 zalloc(size_t size)
    50 {
    51 	void *p;
    52 
    53 	p = malloc(size);
    54 	memset(p, 0, size);
    55 
    56 	return p;
    57 }
    58 
    59 struct razor_set_section_index {
    60 	const char *name;
    61 	uint32_t offset;
    62 	uint32_t flags;
    63 };
    64 
    65 #define MAIN(type, field) \
    66 	{ type, offsetof(struct razor_set, field), RAZOR_SECTION_MAIN }
    67 #define FILES(type, field) \
    68 	{ type, offsetof(struct razor_set, field), RAZOR_SECTION_FILES }
    69 #define DETAILS(type, field) \
    70 	{ type, offsetof(struct razor_set, field), RAZOR_SECTION_DETAILS }
    71 
    72 struct razor_set_section_index razor_sections[] = {
    73 	MAIN(RAZOR_STRING_POOL, string_pool),
    74 	MAIN(RAZOR_PACKAGES, packages),
    75 	MAIN(RAZOR_PROPERTIES, properties),
    76 	MAIN(RAZOR_PACKAGE_POOL, package_pool),
    77 	MAIN(RAZOR_PROPERTY_POOL, property_pool),
    78 	MAIN(RAZOR_PREFIX_POOL, prefix_pool),
    79 	FILES(RAZOR_FILES, files),
    80 	FILES(RAZOR_FILE_POOL, file_pool),
    81 	FILES(RAZOR_FILE_STRING_POOL, file_string_pool),
    82 	DETAILS(RAZOR_DETAILS_STRING_POOL, details_string_pool)
    83 };
    84 
    85 RAZOR_EXPORT struct razor_set *
    86 razor_set_create_without_root(void)
    87 {
    88 	struct razor_set *set;
    89 	char *empty;
    90 
    91 	set = zalloc(sizeof *set);
    92 
    93 	empty = array_add(&set->string_pool, 1);
    94 	*empty = '\0';
    95 
    96 	return set;
    97 }
    98 
    99 RAZOR_EXPORT struct razor_set *
   100 razor_set_create(void)
   101 {
   102 	struct razor_set *set;
   103 	struct razor_entry *e;
   104 
   105 	set = razor_set_create_without_root();
   106 
   107 	e = array_add(&set->files, sizeof *e);
   108 	e->name = 0;
   109 	e->flags = RAZOR_ENTRY_LAST;
   110 	e->start = 0;
   111 	list_set_empty(&e->packages);
   112 
   113 	return set;
   114 }
   115 
   116 struct razor_mapped_file {
   117 	struct razor_set_header *header;
   118 	size_t size;
   119 	struct razor_mapped_file *next;
   120 };
   121 
   122 RAZOR_EXPORT int
   123 razor_set_bind_sections(struct razor_set *set, const char *filename)
   124 {
   125 	struct razor_set_section *s, *sections;
   126 	struct razor_mapped_file *file;
   127 	const char *pool;
   128 	struct array *array;
   129 	int i, j;
   130 
   131 	file = zalloc(sizeof *file);
   132 	if (file == NULL)
   133 		return -1;
   134 
   135 	file->header = razor_file_get_contents(filename, &file->size);
   136 	if (!file->header) {
   137 		free(file);
   138 		return -1;
   139 	}
   140 
   141 	file->next = set->mapped_files;
   142 	set->mapped_files = file;
   143 
   144 	sections = (void *) file->header + sizeof *file->header;
   145 	pool = (void *) sections +
   146 		file->header->num_sections * sizeof *sections;
   147 
   148 	for (i = 0; i < file->header->num_sections; i++) {
   149 		s = sections + i;
   150 		for (j = 0; j < ARRAY_SIZE(razor_sections); j++)
   151 			if (!strcmp(razor_sections[j].name, &pool[s->name]))
   152 				break;
   153 		if (j == ARRAY_SIZE(razor_sections))
   154 			continue;
   155 		array = (void *) set + razor_sections[j].offset;
   156 		array->data = (void *) file->header + s->offset;
   157 		array->size = s->size;
   158 		array->alloc = s->size;
   159 	}
   160 
   161 	return 0;
   162 }
   163 
   164 RAZOR_EXPORT struct razor_set *
   165 razor_set_open(const char *filename)
   166 {
   167 	struct razor_set *set;
   168 
   169 	set = zalloc(sizeof *set);
   170 	if (razor_set_bind_sections(set, filename)){
   171 		free(set);
   172 		return NULL;
   173 	}
   174 	return set;
   175 }
   176 
   177 RAZOR_EXPORT void
   178 razor_set_destroy(struct razor_set *set)
   179 {
   180 	struct razor_mapped_file *file, *next;
   181 	struct array *array;
   182 	int i;
   183 
   184 	assert (set != NULL);
   185 
   186 	if (set->mapped_files == NULL) {
   187 		for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   188 			array = (void *) set + razor_sections[i].offset;
   189 			array_release(array);
   190 		}
   191 	} else {
   192 		for (file = set->mapped_files; file != NULL; file = next) {
   193 			next = file->next;
   194 			razor_file_free_contents(file->header, file->size);
   195 			free(file);
   196 		}
   197 	}
   198 
   199 	free(set);
   200 }
   201 
   202 RAZOR_EXPORT int
   203 razor_set_write_to_fd(struct razor_set *set, int fd, uint32_t section_mask)
   204 {
   205 	struct razor_set_header header;
   206 	struct razor_set_section sections[ARRAY_SIZE(razor_sections)];
   207 	struct hashtable table;
   208 	struct array pool, *arrays[ARRAY_SIZE(razor_sections)];
   209 	uint32_t offset;
   210 	int count, i, j;
   211 	static const char padding[4];
   212 
   213 	array_init(&pool);
   214 	hashtable_init(&table, &pool);
   215 
   216 	j = 0;
   217 	for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   218 		if ((razor_sections[i].flags & section_mask) == 0)
   219 			continue;
   220 
   221 		arrays[j] = (void *) set + razor_sections[i].offset;
   222 		sections[j].name =
   223 			hashtable_tokenize(&table, razor_sections[i].name);
   224 		j++;
   225 	}
   226 
   227 	count = j;
   228 	header.magic = RAZOR_MAGIC;
   229 	header.version = RAZOR_VERSION;
   230 	header.num_sections = count;
   231 	offset = sizeof header + count * sizeof *sections + ALIGN(pool.size, 4);
   232 
   233 	for (i = 0; i < count; i++) {
   234 		sections[i].offset = offset;
   235 		sections[i].size = arrays[i]->size;
   236 		offset += ALIGN(arrays[i]->size, 4);
   237 	}
   238 
   239 	razor_write(fd, &header, sizeof header);
   240 	razor_write(fd, sections, count * sizeof *sections);
   241 	razor_write(fd, pool.data, pool.size);
   242 	razor_write(fd, padding, PADDING(pool.size, 4));
   243 
   244 	for (i = 0; i < count; i++) {
   245 		razor_write(fd, arrays[i]->data, arrays[i]->size);
   246 		razor_write(fd, padding, PADDING(arrays[i]->size, 4));
   247 	}
   248 
   249 	array_release(&pool);
   250 	hashtable_release(&table);
   251 
   252 	return 0;
   253 }
   254 
   255 RAZOR_EXPORT int
   256 razor_set_write(struct razor_set *set, const char *filename, uint32_t sections)
   257 {
   258 	int fd, status;
   259 
   260 	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0666);
   261 	if (fd < 0)
   262 		return -1;
   263 
   264 	status = razor_set_write_to_fd(set, fd, sections);
   265 	if (status) {
   266 	    close(fd);
   267 	    return status;
   268 	}
   269 
   270 	return close(fd);
   271 }
   272 
   273 RAZOR_EXPORT void
   274 razor_build_evr(char *evr_buf, int size, const char *epoch,
   275 		const char *version, const char *release)
   276 {
   277 	int len;
   278 
   279 	if (!version || !*version) {
   280 		*evr_buf = '\0';
   281 		return;
   282 	}
   283 
   284 	if (epoch && *epoch && strcmp(epoch, "0") != 0) {
   285 		len = snprintf(evr_buf, size, "%s:", epoch);
   286 		evr_buf += len;
   287 		size -= len;
   288 	}
   289 	len = snprintf(evr_buf, size, "%s", version);
   290 	evr_buf += len;
   291 	size -= len;
   292 	if (release && *release)
   293 		snprintf(evr_buf, size, "-%s", release);
   294 }
   295 
   296 RAZOR_EXPORT int
   297 razor_versioncmp(const char *s1, const char *s2)
   298 {
   299 	const char *p1, *p2;
   300 	long n1, n2;
   301 	int res;
   302 
   303 	assert (s1 != NULL);
   304 	assert (s2 != NULL);
   305 
   306 	n1 = strtol(s1, (char **) &p1, 10);
   307 	n2 = strtol(s2, (char **) &p2, 10);
   308 
   309 	/* Epoch; if one but not the other has an epoch set, default
   310 	 * the epoch-less version to 0. */
   311 	res = (*p1 == ':') - (*p2 == ':');
   312 	if (res < 0) {
   313 		n1 = 0;
   314 		p1 = s1;
   315 		p2++;
   316 	} else if (res > 0) {
   317 		p1++;
   318 		n2 = 0;
   319 		p2 = s2;
   320 	}
   321 
   322 	if (n1 != n2)
   323 		return n1 - n2;
   324 	while (*p1 && *p2) {
   325 		if (*p1 != *p2)
   326 			return *p1 - *p2;
   327 		p1++;
   328 		p2++;
   329 		if (isdigit(*p1) && isdigit(*p2))
   330 			return razor_versioncmp(p1, p2);
   331 	}
   332 
   333 	return *p1 - *p2;
   334 }
   335 
   336 static const char *
   337 razor_package_get_details_string(struct razor_set *set,
   338 				 struct razor_package *package,
   339 				 enum razor_detail_type type)
   340 {
   341 	const char *pool;
   342 
   343 	switch (type) {
   344 	case RAZOR_DETAIL_NAME:
   345 		pool = set->string_pool.data;
   346 		return &pool[package->name];
   347 
   348 	case RAZOR_DETAIL_VERSION:
   349 		pool = set->string_pool.data;
   350 		return &pool[package->version];
   351 
   352 	case RAZOR_DETAIL_ARCH:
   353 		pool = set->string_pool.data;
   354 		return &pool[package->arch];
   355 
   356 	case RAZOR_DETAIL_SUMMARY:
   357 		pool = set->details_string_pool.data;
   358 		return &pool[package->summary];
   359 
   360 	case RAZOR_DETAIL_DESCRIPTION:
   361 		pool = set->details_string_pool.data;
   362 		return &pool[package->description];
   363 
   364 	case RAZOR_DETAIL_URL:
   365 		pool = set->details_string_pool.data;
   366 		return &pool[package->url];
   367 
   368 	case RAZOR_DETAIL_LICENSE:
   369 		pool = set->details_string_pool.data;
   370 		return &pool[package->license];
   371 
   372 	case RAZOR_DETAIL_PREUNPROG:
   373 		pool = set->string_pool.data;
   374 		return &pool[package->preun.program];
   375 
   376 	case RAZOR_DETAIL_PREUN:
   377 		pool = set->string_pool.data;
   378 		return &pool[package->preun.body];
   379 
   380 	case RAZOR_DETAIL_POSTUNPROG:
   381 		pool = set->string_pool.data;
   382 		return &pool[package->postun.program];
   383 
   384 	case RAZOR_DETAIL_POSTUN:
   385 		pool = set->string_pool.data;
   386 		return &pool[package->postun.body];
   387 
   388 	default:
   389 		fprintf(stderr, "type %u not found\n", type);
   390 		return NULL;
   391 	}
   392 }
   393 
   394 static const char *const *
   395 razor_package_get_details_array(struct razor_set *set,
   396 				struct razor_package *package,
   397 				enum razor_detail_type type)
   398 {
   399 	switch (type) {
   400 	case RAZOR_DETAIL_PREFIXES:
   401 		/* We don't track prefixes in packages. Install prefixes
   402 		 * are tracked, but we don't provide an API to get them.
   403 		 */
   404 		return NULL;
   405 
   406 	default:
   407 		fprintf(stderr, "type %u not found\n", type);
   408 		return NULL;
   409 	}
   410 }
   411 
   412 /**
   413  * razor_package_get_details_varg:
   414  * @set: a %razor_set
   415  * @package: a %razor_package
   416  * @args: a va_list of arguments to set
   417  **/
   418 void
   419 razor_package_get_details_varg(struct razor_set *set,
   420 			       struct razor_package *package,
   421 			       va_list args)
   422 {
   423 	int i;
   424 	enum razor_detail_type type;
   425 	const char **string;
   426 	const char *const **array;
   427 
   428 	for (i = 0;; i += 2) {
   429 		type = va_arg(args, enum razor_detail_type);
   430 		if (type == RAZOR_DETAIL_LAST)
   431 			break;
   432 		if (type == RAZOR_DETAIL_PREFIXES) {
   433 			array = va_arg(args, const char *const **);
   434 			*array = razor_package_get_details_array(set, package,
   435 								 type);
   436 		} else {
   437 			string = va_arg(args, const char **);
   438 			*string = razor_package_get_details_string(set, package,
   439 								   type);
   440 		}
   441 	}
   442 
   443 }
   444 
   445 /**
   446  * razor_package_get_details:
   447  * @set: a %razor_set
   448  * @package: a %razor_package
   449  *
   450  * Gets details about a package using a varg interface
   451  * The vararg must be terminated with %RAZOR_DETAIL_LAST.
   452  *
   453  * Example: razor_package_get_details (set, package,
   454  *				       RAZOR_DETAIL_URL, &url,
   455  *				       RAZOR_DETAIL_LAST);
   456  **/
   457 RAZOR_EXPORT void
   458 razor_package_get_details(struct razor_set *set, struct razor_package *package, ...)
   459 {
   460 	va_list args;
   461 
   462 	assert (set != NULL);
   463 	assert (package != NULL);
   464 
   465 	va_start(args, NULL);
   466 	razor_package_get_details_varg (set, package, args);
   467 	va_end (args);
   468 }
   469 
   470 /**
   471  * razor_package_remove:
   472  * @set: a %razor_set
   473  * @package: a %razor_package
   474  * @root: the root into which the package is currently installed
   475  * @install_count: the value to pass to uninstall scripts
   476  *
   477  * Removes an installed package.
   478  **/
   479 RAZOR_EXPORT int
   480 razor_package_remove(struct razor_set *set, struct razor_package *package,
   481 		     const char *root, int install_count)
   482 {
   483 	struct razor_file_iterator *fi;
   484 	struct razor_package_iterator *pi;
   485 	struct razor_package *p;
   486 	char buffer[PATH_MAX];
   487 	const char *name, *program, *script;
   488 	int retval = 0, i, count;
   489 	struct environment env;
   490 	struct list *link;
   491 	const char *prefix;
   492 
   493 	environment_init(&env);
   494 	link = list_first(&package->install_prefixes, &set->prefix_pool);
   495 	for (i = 0; link; i++) {
   496 		prefix = (const char *)set->string_pool.data + link->data;
   497 		sprintf(buffer, "RPM_INSTALL_PREFIX%d", i);
   498 		environment_add_variable(&env, buffer, prefix);
   499 		link = list_next(link);
   500 	}
   501 
   502 	razor_package_get_details(set, package,
   503 				  RAZOR_DETAIL_PREUNPROG, &program,
   504 				  RAZOR_DETAIL_PREUN, &script,
   505 				  RAZOR_DETAIL_LAST);
   506 
   507 	environment_set(&env);
   508 	retval = razor_run_script(root, RAZOR_PROPERTY_PREUN, program, script,
   509 				  install_count);
   510 	environment_unset(&env);
   511 
   512 	if (retval) {
   513 		environment_release(&env);
   514 		return -1;
   515 	}
   516 
   517 	fi = razor_file_iterator_create(set, package);
   518 
   519 	while (!retval && razor_file_iterator_next(fi, &name)) {
   520 		pi = razor_package_iterator_create_for_file(set, name);
   521 		count = 0;
   522 		while (razor_package_iterator_next(pi, &p, RAZOR_DETAIL_LAST))
   523 			count++;
   524 		razor_package_iterator_destroy(pi);
   525 		if (count <= 1) {
   526 			snprintf(buffer, sizeof buffer, "%s%s", root, name);
   527 			retval = remove(buffer);
   528 		}
   529 	}
   530 
   531 	razor_file_iterator_destroy(fi);
   532 
   533 	if (retval) {
   534 		environment_release(&env);
   535 		return retval;
   536 	}
   537 
   538 	razor_package_get_details(set, package,
   539 				  RAZOR_DETAIL_POSTUNPROG, &program,
   540 				  RAZOR_DETAIL_POSTUN, &script,
   541 				  RAZOR_DETAIL_LAST);
   542 
   543 	environment_set(&env);
   544 	retval = razor_run_script(root, RAZOR_PROPERTY_POSTUN, program, script,
   545 				  install_count);
   546 	environment_unset(&env);
   547 
   548 	environment_release(&env);
   549 
   550 	return retval;
   551 }
   552 
   553 RAZOR_EXPORT const char *
   554 razor_property_relation_to_string(struct razor_property *p)
   555 {
   556 	assert (p != NULL);
   557 
   558 	switch (p->flags & RAZOR_PROPERTY_RELATION_MASK) {
   559 	case RAZOR_PROPERTY_LESS:
   560 		return "<";
   561 
   562 	case RAZOR_PROPERTY_LESS | RAZOR_PROPERTY_EQUAL:
   563 		return "<=";
   564 
   565 	case RAZOR_PROPERTY_EQUAL:
   566 		return "=";
   567 
   568 	case RAZOR_PROPERTY_GREATER | RAZOR_PROPERTY_EQUAL:
   569 		return ">=";
   570 
   571 	case RAZOR_PROPERTY_GREATER:
   572 		return ">";
   573 
   574 	default:
   575 		return "?";
   576 	}
   577 }
   578 
   579 RAZOR_EXPORT const char *
   580 razor_property_type_to_string(struct razor_property *p)
   581 {
   582 	assert (p != NULL);
   583 
   584 	switch (p->flags & RAZOR_PROPERTY_TYPE_MASK) {
   585 	case RAZOR_PROPERTY_REQUIRES:
   586 		return "requires";
   587 	case RAZOR_PROPERTY_PROVIDES:
   588 		return "provides";
   589 	case RAZOR_PROPERTY_CONFLICTS:
   590 		return "conflicts";
   591 	case RAZOR_PROPERTY_OBSOLETES:
   592 		return "obsoletes";
   593 	default:
   594 		return NULL;
   595 	}
   596 }
   597 
   598 RAZOR_EXPORT struct razor_entry *
   599 razor_set_find_entry(struct razor_set *set,
   600 		     struct razor_entry *dir, const char *pattern)
   601 {
   602 	struct razor_entry *e, *subdir;
   603 	const char *n, *pool = set->file_string_pool.data;
   604 	int len;
   605 
   606 	assert (set != NULL);
   607 	assert (dir != NULL);
   608 	assert (pattern != NULL);
   609 
   610 	e = dir;
   611 	do {
   612 		n = pool + e->name;
   613 		if (strcmp(pattern, n) == 0)
   614 			return e;
   615 		len = strlen(n);
   616 		if (e->start != 0 && strncmp(pattern, n, len) == 0 &&
   617 		    pattern[len] == '/') {
   618 			subdir = (struct razor_entry *) set->files.data +
   619 				 e->start;
   620 			return razor_set_find_entry(set, subdir,
   621 						    pattern + len + 1);
   622 		}
   623 	} while (!((e++)->flags & RAZOR_ENTRY_LAST));
   624 
   625 	return NULL;
   626 }
   627 
   628 static void
   629 list_dir(struct razor_set *set, struct razor_entry *dir,
   630 	 char *prefix, const char *pattern)
   631 {
   632 	struct razor_entry *e, *subdir;
   633 	const char *n, *pool = set->file_string_pool.data;
   634 
   635 	e = dir;
   636 	do {
   637 		n = pool + e->name;
   638 		if (pattern && pattern[0] && fnmatch(pattern, n, 0) != 0)
   639 			continue;
   640 		printf("%s/%s\n", prefix, n);
   641 		if (e->start) {
   642 			char *sub = prefix + strlen (prefix);
   643 			*sub = '/';
   644 			strcpy (sub + 1, n);
   645 			subdir = (struct razor_entry *) set->files.data +
   646 				 e->start;
   647 			list_dir(set, subdir, prefix, pattern);
   648 			*sub = '\0';
   649 		}
   650 	} while (!((e++)->flags & RAZOR_ENTRY_LAST));
   651 }
   652 
   653 RAZOR_EXPORT void
   654 razor_set_list_files(struct razor_set *set, const char *pattern)
   655 {
   656 	struct razor_entry *root, *e;
   657 	char buffer[512], *p, *base;
   658 
   659 	assert (set != NULL);
   660 
   661 	root = (struct razor_entry *) set->files.data;
   662 
   663 	if (pattern == NULL) {
   664 		p = set->file_string_pool.data;
   665 		e = root;
   666 		do {
   667 			if (e->start) {
   668 				strcpy(buffer, p + e->name);
   669 				list_dir(set, root + e->start, buffer, NULL);
   670 			}
   671 		} while (!((e++)->flags & RAZOR_ENTRY_LAST));
   672 		return;
   673 	}
   674 
   675 	strcpy(buffer, pattern);
   676 	e = razor_set_find_entry(set, root, buffer);
   677 	if (e && e->start) {
   678 		base = NULL;
   679 	} else {
   680 		p = strrchr(buffer, '/');
   681 		if (p) {
   682 			*p = '\0';
   683 			base = p + 1;
   684 		} else {
   685 			base = NULL;
   686 		}
   687 	}
   688 	e = razor_set_find_entry(set, root, buffer);
   689 	if (e && e->start)
   690 		list_dir(set, root + e->start, buffer, base);
   691 }
   692 
   693 RAZOR_EXPORT void
   694 razor_set_list_package_files(struct razor_set *set,
   695 			     struct razor_package *package)
   696 {
   697 	struct razor_file_iterator *fi;
   698 	const char *name;
   699 
   700 	assert (set != NULL);
   701 	assert (package != NULL);
   702 
   703 	fi = razor_file_iterator_create(set, package);
   704 
   705 	while (razor_file_iterator_next(fi, &name))
   706 		printf("%s\n", name);
   707 
   708 	razor_file_iterator_destroy(fi);
   709 }
   710 
   711 RAZOR_EXPORT void
   712 razor_set_diff(struct razor_set *set, struct razor_set *upstream,
   713 	       razor_diff_callback_t callback, void *data)
   714 {
   715  	struct razor_package_iterator *pi1, *pi2;
   716  	struct razor_package *p1, *p2;
   717 	const char *name1, *name2, *version1, *version2, *arch1, *arch2;
   718 	int res;
   719 
   720 	assert (set != NULL);
   721 	assert (upstream != NULL);
   722 
   723 	pi1 = razor_package_iterator_create(set);
   724 	pi2 = razor_package_iterator_create(upstream);
   725 
   726 	razor_package_iterator_next(pi1, &p1,
   727 				    RAZOR_DETAIL_NAME, &name1,
   728 				    RAZOR_DETAIL_VERSION, &version1,
   729 				    RAZOR_DETAIL_ARCH, &arch1,
   730 				    RAZOR_DETAIL_LAST);
   731 	razor_package_iterator_next(pi2, &p2,
   732 				    RAZOR_DETAIL_NAME, &name2,
   733 				    RAZOR_DETAIL_VERSION, &version2,
   734 				    RAZOR_DETAIL_ARCH, &arch2,
   735 				    RAZOR_DETAIL_LAST);
   736 
   737 	while (p1 || p2) {
   738 		if (p1 && p2) {
   739 			res = strcmp(name1, name2);
   740 			if (res == 0)
   741 				res = razor_versioncmp(version1, version2);
   742 		} else {
   743 			res = 0;
   744 		}
   745 
   746 		if (p2 == NULL || res < 0)
   747 			callback(RAZOR_DIFF_ACTION_REMOVE,
   748 				 p1, name1, version1, arch1, data);
   749 		else if (p1 == NULL || res > 0)
   750 			callback(RAZOR_DIFF_ACTION_ADD,
   751 				 p2, name2, version2, arch2, data);
   752 
   753 		if (p1 != NULL && res <= 0)
   754 			razor_package_iterator_next(pi1, &p1,
   755 						    RAZOR_DETAIL_NAME, &name1,
   756 						    RAZOR_DETAIL_VERSION, &version1,
   757 						    RAZOR_DETAIL_ARCH, &arch1,
   758 						    RAZOR_DETAIL_LAST);
   759 		if (p2 != NULL && res >= 0)
   760 			razor_package_iterator_next(pi2, &p2,
   761 						    RAZOR_DETAIL_NAME, &name2,
   762 						    RAZOR_DETAIL_VERSION, &version2,
   763 						    RAZOR_DETAIL_ARCH, &arch2,
   764 						    RAZOR_DETAIL_LAST);
   765 	}
   766 
   767 	razor_package_iterator_destroy(pi1);
   768 	razor_package_iterator_destroy(pi2);
   769 }
   770 
   771 struct install_action {
   772 	enum razor_install_action action;
   773 	struct razor_package *package;
   774 };
   775 
   776 struct razor_install_iterator {
   777 	struct razor_set *set;
   778 	struct razor_set *next;
   779 	struct array actions;
   780 	struct deque *order;
   781 };
   782 
   783 static void
   784 add_action(enum razor_diff_action action,
   785 	   struct razor_package *package,
   786 	   const char *name,
   787 	   const char *version,
   788 	   const char *arch,
   789 	   void *data)
   790 {
   791 	struct razor_install_iterator *ii = data;
   792 	struct install_action *a;
   793 
   794 	a = array_add(&ii->actions, sizeof *a);
   795 	a->package = package;
   796 
   797 	switch (action) {
   798 	case RAZOR_DIFF_ACTION_ADD:
   799 		a->action = RAZOR_INSTALL_ACTION_ADD;
   800 		break;
   801 	case RAZOR_DIFF_ACTION_REMOVE:
   802 		a->action = RAZOR_INSTALL_ACTION_REMOVE;
   803 		break;
   804 	}
   805 }
   806 
   807 RAZOR_EXPORT struct razor_install_iterator *
   808 razor_set_create_install_iterator(struct razor_set *set,
   809 				  struct razor_set *next)
   810 {
   811 	struct razor_install_iterator *ii;
   812 	struct razor_property *prop;
   813 	/* A graph of the actions to be perfomed where
   814 	 * A->B means action A should follow action B.
   815 	 */
   816 	struct graph follows;
   817 	struct install_action *actions, *ai, *aj;
   818 	int i, j, count, vertex_added;
   819 	struct list *link;
   820 	struct razor_set *rs;
   821 
   822 	assert (set != NULL);
   823 	assert (next != NULL);
   824 
   825 	ii = zalloc(sizeof *ii);
   826 	ii->set = set;
   827 	ii->next = next;
   828 	
   829 	razor_set_diff(set, next, add_action, ii);
   830 
   831 	actions = ii->actions.data;
   832 	count = ii->actions.size / sizeof (struct install_action);
   833 
   834 	graph_init(&follows);
   835 
   836 	for(i = 0; i < count; i++) {
   837 		ai = actions + i;
   838 		rs = ai->action == RAZOR_INSTALL_ACTION_ADD ? next : set;
   839 		vertex_added = 0;
   840 		link = list_first(&ai->package->properties, &rs->property_pool);
   841 		for(; link; link = list_next(link)) {
   842 			prop = rs->properties.data;
   843 			prop += link->data;
   844 			switch(prop->flags & RAZOR_PROPERTY_TYPE_MASK) {
   845 			case RAZOR_PROPERTY_REQUIRES:
   846 			case RAZOR_PROPERTY_CONFLICTS:
   847 				for(j = 0; j < count; j++) {
   848 					if (j == i)
   849 						continue;
   850 					aj = actions + j;
   851 					if (aj->package->name == prop->name) {
   852 						if (ai->action ==
   853 						    RAZOR_INSTALL_ACTION_ADD)
   854 							graph_add_edge(&follows,
   855 								       i, j);
   856 						else
   857 							graph_add_edge(&follows,
   858 								       j, i);
   859 						vertex_added++;
   860 					}
   861 				}
   862 				break;
   863 			}
   864 		}
   865 		if (ai->action == RAZOR_INSTALL_ACTION_ADD) {
   866 			for(j = 0; j < count; j++) {
   867 				if (j == i)
   868 					continue;
   869 				aj = actions + j;
   870 				if (aj->package == ai->package &&
   871 				    aj->action == RAZOR_INSTALL_ACTION_REMOVE) {
   872 					graph_add_edge(&follows, i, j);
   873 					vertex_added++;
   874 				}
   875 			}
   876 		}
   877 		if (!vertex_added)
   878 			graph_add_edge(&follows, i, i);
   879 	}
   880 
   881 	ii->order = graph_sort(&follows);
   882 	graph_release(&follows);
   883 
   884 	return ii;
   885 }
   886 
   887 RAZOR_EXPORT int
   888 razor_install_iterator_next(struct razor_install_iterator *ii,
   889 			    struct razor_set **set,
   890 			    struct razor_package **package,
   891 			    enum razor_install_action *action,
   892 			    int *count)
   893 {
   894 	struct install_action *a;
   895 	if (deque_empty(ii->order))
   896 		return 0;
   897 
   898 	a = (struct install_action *)ii->actions.data + deque_pop(ii->order);
   899 	switch (a->action) {
   900 	case RAZOR_INSTALL_ACTION_ADD:
   901 		*set = ii->next;
   902 		break;
   903 	case RAZOR_INSTALL_ACTION_REMOVE:
   904 		*set = ii->set;
   905 		break;
   906 	}
   907 
   908 	*package = a->package;
   909 	*action = a->action;
   910 	*count = 0;
   911 
   912 	return 1;
   913 }
   914 
   915 RAZOR_EXPORT void
   916 razor_install_iterator_destroy(struct razor_install_iterator *ii)
   917 {
   918 	array_release(&ii->actions);
   919 	deque_free(ii->order);
   920 	free(ii);
   921 }