librazor/razor.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Oct 09 17:27:41 2014 +0100 (2014-10-09)
changeset 455 df914f383f5c
parent 442 c4bcba8023a9
child 458 3f841a46eab5
permissions -rw-r--r--
Support downloading from local repository even without libcurl

Using the --url option of the razor executable, it is possible
to specify a yum repository on the local machine (eg., on installation
media) and import from there, eg.,:

C> razor --url file:///d:/ import-yum

This will be handled by libcurl if available but if not, an internal
copy routine will be used.

Note that if Microsoft's KTM implementation of atomic transactions is
used, then the current directory must support atomic transactions
(also improve error messages for this, and other, cases).
rhughes@241
     1
/*
rhughes@241
     2
 * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
rhughes@241
     3
 * Copyright (C) 2008  Red Hat, Inc
ali@418
     4
 * Copyright (C) 2009-2012  J. Ali Harlow <ali@juiblex.co.uk>
rhughes@241
     5
 *
rhughes@241
     6
 * This program is free software; you can redistribute it and/or modify
rhughes@241
     7
 * it under the terms of the GNU General Public License as published by
rhughes@241
     8
 * the Free Software Foundation; either version 2 of the License, or
rhughes@241
     9
 * (at your option) any later version.
rhughes@241
    10
 *
rhughes@241
    11
 * This program is distributed in the hope that it will be useful,
rhughes@241
    12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
rhughes@241
    13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
rhughes@241
    14
 * GNU General Public License for more details.
rhughes@241
    15
 *
rhughes@241
    16
 * You should have received a copy of the GNU General Public License along
rhughes@241
    17
 * with this program; if not, write to the Free Software Foundation, Inc.,
rhughes@241
    18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
rhughes@241
    19
 */
rhughes@241
    20
rhughes@241
    21
#define _GNU_SOURCE
rhughes@241
    22
ali@334
    23
#include "config.h"
ali@334
    24
rhughes@241
    25
#include <stdlib.h>
rhughes@241
    26
#include <stddef.h>
rhughes@241
    27
#include <stdint.h>
rhughes@241
    28
#include <stdio.h>
richard@302
    29
#include <stdarg.h>
rhughes@241
    30
#include <string.h>
rhughes@241
    31
#include <sys/types.h>
rhughes@241
    32
#include <sys/stat.h>
rhughes@241
    33
#include <unistd.h>
rhughes@241
    34
#include <fcntl.h>
rhughes@241
    35
#include <errno.h>
rhughes@241
    36
#include <ctype.h>
rhughes@241
    37
#include <fnmatch.h>
ali@369
    38
#include <limits.h>
richard@301
    39
#include <assert.h>
ali@388
    40
#ifdef MSWIN_API
ali@388
    41
#include <windows.h>
ali@388
    42
#endif
rhughes@241
    43
krh@253
    44
#include "razor-internal.h"
rhughes@241
    45
#include "razor.h"
rhughes@241
    46
ali@345
    47
#ifndef O_BINARY
ali@345
    48
#define O_BINARY	0
ali@345
    49
#endif
ali@345
    50
jbowes@318
    51
struct razor_set_section_index {
jbowes@318
    52
	const char *name;
jbowes@318
    53
	uint32_t offset;
krh@373
    54
	uint32_t flags;
jbowes@318
    55
};
jbowes@318
    56
krh@373
    57
#define MAIN(type, field) \
krh@373
    58
	{ type, offsetof(struct razor_set, field), RAZOR_SECTION_MAIN }
krh@373
    59
#define FILES(type, field) \
krh@373
    60
	{ type, offsetof(struct razor_set, field), RAZOR_SECTION_FILES }
krh@373
    61
#define DETAILS(type, field) \
krh@373
    62
	{ type, offsetof(struct razor_set, field), RAZOR_SECTION_DETAILS }
krh@373
    63
jbowes@318
    64
struct razor_set_section_index razor_sections[] = {
krh@373
    65
	MAIN(RAZOR_STRING_POOL, string_pool),
krh@373
    66
	MAIN(RAZOR_PACKAGES, packages),
krh@373
    67
	MAIN(RAZOR_PROPERTIES, properties),
krh@373
    68
	MAIN(RAZOR_PACKAGE_POOL, package_pool),
krh@373
    69
	MAIN(RAZOR_PROPERTY_POOL, property_pool),
krh@373
    70
	MAIN(RAZOR_PREFIX_POOL, prefix_pool),
krh@373
    71
	FILES(RAZOR_FILES, files),
krh@373
    72
	FILES(RAZOR_FILE_POOL, file_pool),
krh@373
    73
	FILES(RAZOR_FILE_STRING_POOL, file_string_pool),
krh@373
    74
	DETAILS(RAZOR_DETAILS_STRING_POOL, details_string_pool)
jbowes@258
    75
};
krh@262
    76
krh@269
    77
RAZOR_EXPORT struct razor_set *
ali@363
    78
razor_set_create_without_root(void)
ali@363
    79
{
ali@363
    80
	struct razor_set *set;
ali@363
    81
	char *empty;
ali@363
    82
ali@363
    83
	set = zalloc(sizeof *set);
ali@363
    84
ali@403
    85
	if (set) {
ali@403
    86
		empty = array_add(&set->string_pool, 1);
ali@403
    87
		*empty = '\0';
ali@363
    88
ali@403
    89
		set->lock_fd = -1;
ali@403
    90
ali@403
    91
		set->ref_count = 1;
ali@403
    92
ali@403
    93
		set->header_version = RAZOR_HEADER_VERSION;
ali@424
    94
ali@424
    95
		set->flags = RAZOR_SET_PRIVATE;
ali@403
    96
	}
ali@388
    97
ali@363
    98
	return set;
ali@363
    99
}
ali@363
   100
ali@363
   101
RAZOR_EXPORT struct razor_set *
rhughes@241
   102
razor_set_create(void)
rhughes@241
   103
{
rhughes@241
   104
	struct razor_set *set;
rhughes@241
   105
	struct razor_entry *e;
rhughes@241
   106
ali@363
   107
	set = razor_set_create_without_root();
rhughes@241
   108
rhughes@241
   109
	e = array_add(&set->files, sizeof *e);
rhughes@241
   110
	e->name = 0;
rhughes@241
   111
	e->flags = RAZOR_ENTRY_LAST;
rhughes@241
   112
	e->start = 0;
rhughes@241
   113
	list_set_empty(&e->packages);
rhughes@241
   114
rhughes@241
   115
	return set;
rhughes@241
   116
}
rhughes@241
   117
ali@403
   118
RAZOR_EXPORT uint32_t
ali@403
   119
razor_set_get_header_version(struct razor_set *set)
ali@403
   120
{
ali@403
   121
	return set->header_version;
ali@403
   122
}
ali@403
   123
ali@403
   124
RAZOR_EXPORT int
ali@403
   125
razor_set_set_header_version(struct razor_set *set, uint32_t header_version)
ali@403
   126
{
ali@403
   127
	if (header_version<RAZOR_HEADER_VERSION_MIN ||
ali@403
   128
	    header_version>RAZOR_HEADER_VERSION)
ali@403
   129
		return -1;
ali@403
   130
	else {
ali@403
   131
		set->header_version = header_version;
ali@403
   132
		return 0;
ali@403
   133
	}
ali@403
   134
}
ali@403
   135
krh@373
   136
struct razor_mapped_file {
krh@373
   137
	struct razor_set_header *header;
krh@373
   138
	size_t size;
krh@373
   139
	struct razor_mapped_file *next;
krh@373
   140
};
krh@373
   141
krh@373
   142
RAZOR_EXPORT int
ali@424
   143
razor_set_bind_sections(struct razor_set *set, const char *filename,
ali@424
   144
			enum razor_set_flags flags, struct razor_error **error)
rhughes@241
   145
{
jbowes@318
   146
	struct razor_set_section *s, *sections;
krh@373
   147
	struct razor_mapped_file *file;
ali@403
   148
	const char *pool, *reason;
rhughes@241
   149
	struct array *array;
ali@447
   150
	int i, j, code;
richard@301
   151
krh@373
   152
	file = zalloc(sizeof *file);
ali@403
   153
	if (file == NULL) {
ali@447
   154
		razor_set_error(error, RAZOR_POSIX_ERROR, ENOMEM, NULL,
ali@447
   155
				"Not enough memory");
jbowes@288
   156
		return -1;
ali@403
   157
	}
jbowes@258
   158
ali@424
   159
	file->header = razor_file_get_contents(filename, &file->size,
ali@424
   160
					       flags & RAZOR_SET_PRIVATE,
ali@424
   161
					       error);
krh@373
   162
	if (!file->header) {
krh@373
   163
		free(file);
krh@373
   164
		return -1;
krh@373
   165
	}
jbowes@318
   166
ali@447
   167
	if (file->size < sizeof *file->header) {
ali@447
   168
		code = RAZOR_GENERAL_ERROR_DATABASE_CORRUPTED;
ali@403
   169
		reason = "Premature EOF";
ali@447
   170
	} else if (file->header->magic != RAZOR_MAGIC) {
ali@447
   171
		code = RAZOR_GENERAL_ERROR_DATABASE_CORRUPTED;
ali@403
   172
		reason = "Bad magic number";
ali@447
   173
	} else if (file->header->version < RAZOR_HEADER_VERSION_MIN ||
ali@447
   174
		   file->header->version > RAZOR_HEADER_VERSION) {
ali@447
   175
		code = RAZOR_GENERAL_ERROR_DATABASE_INCOMPATIBLE;
ali@403
   176
		reason = "Incompatible file version";
ali@447
   177
	} else
ali@403
   178
		reason = NULL;
ali@403
   179
ali@403
   180
	if (reason) {
ali@447
   181
		razor_set_error(error, RAZOR_GENERAL_ERROR, code, filename,
ali@447
   182
				reason);
ali@390
   183
		razor_file_free_contents(file->header, file->size);
ali@390
   184
		free(file);
ali@390
   185
		return -1;
ali@390
   186
	}
ali@390
   187
ali@424
   188
	set->flags = flags & RAZOR_SET_PRIVATE;
ali@424
   189
ali@403
   190
	set->header_version = file->header->version;
ali@403
   191
ali@388
   192
	if (set->mapped_files == NULL) {
ali@388
   193
		for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
ali@388
   194
			array = (void *) set + razor_sections[i].offset;
ali@388
   195
			array_release(array);
ali@388
   196
		}
ali@388
   197
	}
ali@388
   198
krh@373
   199
	file->next = set->mapped_files;
krh@373
   200
	set->mapped_files = file;
krh@373
   201
krh@373
   202
	sections = (void *) file->header + sizeof *file->header;
krh@373
   203
	pool = (void *) sections +
krh@373
   204
		file->header->num_sections * sizeof *sections;
krh@373
   205
krh@373
   206
	for (i = 0; i < file->header->num_sections; i++) {
jbowes@318
   207
		s = sections + i;
krh@373
   208
		for (j = 0; j < ARRAY_SIZE(razor_sections); j++)
krh@373
   209
			if (!strcmp(razor_sections[j].name, &pool[s->name]))
jbowes@318
   210
				break;
krh@373
   211
		if (j == ARRAY_SIZE(razor_sections))
jbowes@258
   212
			continue;
krh@373
   213
		array = (void *) set + razor_sections[j].offset;
krh@373
   214
		array->data = (void *) file->header + s->offset;
jbowes@258
   215
		array->size = s->size;
jbowes@258
   216
		array->alloc = s->size;
jbowes@258
   217
	}
jbowes@288
   218
jbowes@288
   219
	return 0;
jbowes@258
   220
}
jbowes@258
   221
jbowes@318
   222
RAZOR_EXPORT struct razor_set *
ali@424
   223
razor_set_open(const char *filename, enum razor_set_flags flags,
ali@424
   224
	       struct razor_error **error)
jbowes@318
   225
{
jbowes@318
   226
	struct razor_set *set;
jbowes@318
   227
jbowes@318
   228
	set = zalloc(sizeof *set);
ali@403
   229
	if (!set) {
ali@447
   230
		razor_set_error(error, RAZOR_POSIX_ERROR, ENOMEM, NULL,
ali@447
   231
				"Not enough memory");
ali@403
   232
		return NULL;
ali@403
   233
	}
ali@403
   234
ali@388
   235
	set->lock_fd = -1;
ali@406
   236
	set->ref_count = 1;
ali@424
   237
	if (razor_set_bind_sections(set, filename, flags, error)) {
jbowes@318
   238
		free(set);
jbowes@318
   239
		return NULL;
jbowes@318
   240
	}
jbowes@318
   241
	return set;
jbowes@318
   242
}
jbowes@318
   243
ali@388
   244
int
ali@388
   245
razor_set_aquire_lock(struct razor_set *set, const char *path, int exclusive)
ali@388
   246
{
ali@388
   247
	int fd;
ali@388
   248
	assert(set != NULL);
ali@388
   249
ali@388
   250
	if (path) {
ali@388
   251
		fd = open(path, O_CREAT | O_RDWR | O_TRUNC | O_BINARY, 0666);
ali@388
   252
		if (fd < 0)
ali@388
   253
			return -1;
ali@388
   254
	} else {
ali@388
   255
		fd = -1;
ali@388
   256
	}
ali@388
   257
ali@388
   258
#ifdef MSWIN_API
ali@388
   259
	DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
ali@388
   260
	OVERLAPPED lock = {0};
ali@388
   261
ali@388
   262
	if (exclusive)
ali@388
   263
		flags |= LOCKFILE_EXCLUSIVE_LOCK;
ali@403
   264
	if (fd >= 0 && !LockFileEx((HANDLE)_get_osfhandle(fd), flags, 0, 1, 0,
ali@403
   265
				   &lock)) {
ali@388
   266
		close(fd);
ali@388
   267
		return -1;
ali@388
   268
	}
ali@388
   269
	if (set->lock_fd >= 0)
ali@403
   270
		(void)UnlockFile((HANDLE)_get_osfhandle(set->lock_fd), 0, 0, 1,
ali@403
   271
				 0);
ali@388
   272
#else
ali@388
   273
	struct flock lock = {0};
ali@388
   274
ali@388
   275
	lock.l_type = exclusive ? F_WRLCK : F_RDLCK;
ali@388
   276
	lock.l_whence = SEEK_SET;
ali@388
   277
	lock.l_start = 0;
ali@388
   278
	lock.l_len = 0;
ali@388
   279
	if (fd >= 0 && fcntl(fd, F_SETLK, &lock) < 0) {
ali@388
   280
		close(fd);
ali@388
   281
		return -1;
ali@388
   282
	}
ali@388
   283
	if (set->lock_fd >= 0) {
ali@388
   284
		lock.l_type = F_UNLCK;
ali@388
   285
		(void)fcntl(set->lock_fd, F_SETLK, &lock);
ali@388
   286
	}
ali@388
   287
#endif
ali@388
   288
ali@388
   289
	if (set->lock_fd >= 0)
ali@388
   290
		close(set->lock_fd);
ali@388
   291
	set->lock_fd = fd;
ali@388
   292
ali@388
   293
	return 0;
ali@388
   294
}
ali@388
   295
ali@403
   296
static void
rhughes@241
   297
razor_set_destroy(struct razor_set *set)
rhughes@241
   298
{
krh@373
   299
	struct razor_mapped_file *file, *next;
krh@373
   300
	struct array *array;
rhughes@241
   301
	int i;
rhughes@241
   302
richard@301
   303
	assert (set != NULL);
richard@301
   304
krh@373
   305
	if (set->mapped_files == NULL) {
krh@373
   306
		for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
krh@373
   307
			array = (void *) set + razor_sections[i].offset;
krh@373
   308
			array_release(array);
krh@373
   309
		}
rhughes@241
   310
	} else {
krh@373
   311
		for (file = set->mapped_files; file != NULL; file = next) {
krh@373
   312
			next = file->next;
krh@373
   313
			razor_file_free_contents(file->header, file->size);
krh@373
   314
			free(file);
jbowes@258
   315
		}
jbowes@258
   316
	}
jbowes@258
   317
ali@388
   318
	razor_set_aquire_lock(set, NULL, 0);
rhughes@241
   319
	free(set);
rhughes@241
   320
}
rhughes@241
   321
ali@403
   322
RAZOR_EXPORT void
ali@403
   323
razor_set_unref(struct razor_set *set)
ali@403
   324
{
ali@403
   325
	if (set && !--set->ref_count)
ali@403
   326
		razor_set_destroy(set);
ali@403
   327
}
ali@403
   328
ali@403
   329
RAZOR_EXPORT struct razor_set *
ali@403
   330
razor_set_ref(struct razor_set *set)
ali@403
   331
{
ali@403
   332
	if (set)
ali@403
   333
		set->ref_count++;
ali@403
   334
	return set;
ali@403
   335
}
ali@403
   336
ali@403
   337
RAZOR_EXPORT void
ali@403
   338
razor_set_write_to_handle(struct razor_set *set, struct razor_atomic *atomic,
ali@403
   339
			  int handle, uint32_t section_mask)
rhughes@241
   340
{
jbowes@318
   341
	struct razor_set_header header;
krh@373
   342
	struct razor_set_section sections[ARRAY_SIZE(razor_sections)];
jbowes@318
   343
	struct hashtable table;
krh@373
   344
	struct array pool, *arrays[ARRAY_SIZE(razor_sections)];
rhughes@241
   345
	uint32_t offset;
krh@373
   346
	int count, i, j;
krh@373
   347
	static const char padding[4];
jbowes@318
   348
jbowes@318
   349
	array_init(&pool);
jbowes@318
   350
	hashtable_init(&table, &pool);
jbowes@318
   351
krh@373
   352
	j = 0;
krh@373
   353
	for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
krh@373
   354
		if ((razor_sections[i].flags & section_mask) == 0)
krh@373
   355
			continue;
jbowes@318
   356
krh@373
   357
		arrays[j] = (void *) set + razor_sections[i].offset;
krh@373
   358
		sections[j].name =
krh@373
   359
			hashtable_tokenize(&table, razor_sections[i].name);
krh@373
   360
		j++;
krh@373
   361
	}
rhughes@241
   362
krh@373
   363
	count = j;
krh@373
   364
	header.magic = RAZOR_MAGIC;
ali@403
   365
	header.version = set->header_version;
krh@373
   366
	header.num_sections = count;
krh@373
   367
	offset = sizeof header + count * sizeof *sections + ALIGN(pool.size, 4);
krh@373
   368
krh@373
   369
	for (i = 0; i < count; i++) {
krh@373
   370
		sections[i].offset = offset;
krh@373
   371
		sections[i].size = arrays[i]->size;
krh@373
   372
		offset += ALIGN(arrays[i]->size, 4);
rhughes@241
   373
	}
rhughes@241
   374
ali@403
   375
	razor_atomic_write(atomic, handle, &header, sizeof header);
ali@403
   376
	razor_atomic_write(atomic, handle, sections, count * sizeof *sections);
ali@403
   377
	razor_atomic_write(atomic, handle, pool.data, pool.size);
ali@403
   378
	razor_atomic_write(atomic, handle, padding, PADDING(pool.size, 4));
rhughes@241
   379
krh@373
   380
	for (i = 0; i < count; i++) {
ali@403
   381
		razor_atomic_write(atomic, handle, arrays[i]->data,
ali@403
   382
				   arrays[i]->size);
ali@403
   383
		razor_atomic_write(atomic, handle, padding,
ali@403
   384
				   PADDING(arrays[i]->size, 4));
rhughes@241
   385
	}
rhughes@241
   386
krh@373
   387
	array_release(&pool);
krh@373
   388
	hashtable_release(&table);
rhughes@241
   389
}
rhughes@241
   390
krh@269
   391
RAZOR_EXPORT int
ali@403
   392
razor_set_write(struct razor_set *set, struct razor_atomic *atomic,
ali@403
   393
		const char *filename, uint32_t sections)
rhughes@241
   394
{
ali@403
   395
	int h;
rhughes@241
   396
ali@403
   397
	h = razor_atomic_create_file(atomic, filename,
ali@403
   398
				     S_IRWXU | S_IRWXG | S_IRWXO);
ali@403
   399
	if (h < 0)
rhughes@241
   400
		return -1;
rhughes@241
   401
ali@403
   402
	razor_set_write_to_handle(set, atomic, h, sections);
rhughes@241
   403
ali@403
   404
	return razor_atomic_close(atomic, h);
rhughes@241
   405
}
krh@269
   406
krh@269
   407
RAZOR_EXPORT void
rhughes@241
   408
razor_build_evr(char *evr_buf, int size, const char *epoch,
rhughes@241
   409
		const char *version, const char *release)
rhughes@241
   410
{
rhughes@241
   411
	int len;
rhughes@241
   412
rhughes@241
   413
	if (!version || !*version) {
rhughes@241
   414
		*evr_buf = '\0';
rhughes@241
   415
		return;
rhughes@241
   416
	}
rhughes@241
   417
rhughes@241
   418
	if (epoch && *epoch && strcmp(epoch, "0") != 0) {
rhughes@241
   419
		len = snprintf(evr_buf, size, "%s:", epoch);
rhughes@241
   420
		evr_buf += len;
rhughes@241
   421
		size -= len;
rhughes@241
   422
	}
rhughes@241
   423
	len = snprintf(evr_buf, size, "%s", version);
rhughes@241
   424
	evr_buf += len;
rhughes@241
   425
	size -= len;
rhughes@241
   426
	if (release && *release)
rhughes@241
   427
		snprintf(evr_buf, size, "-%s", release);
rhughes@241
   428
}
rhughes@241
   429
krh@269
   430
RAZOR_EXPORT int
krh@248
   431
razor_versioncmp(const char *s1, const char *s2)
rhughes@241
   432
{
rhughes@241
   433
	const char *p1, *p2;
rhughes@241
   434
	long n1, n2;
rhughes@241
   435
	int res;
rhughes@241
   436
richard@301
   437
	assert (s1 != NULL);
richard@301
   438
	assert (s2 != NULL);
richard@301
   439
rhughes@241
   440
	n1 = strtol(s1, (char **) &p1, 10);
rhughes@241
   441
	n2 = strtol(s2, (char **) &p2, 10);
rhughes@241
   442
rhughes@241
   443
	/* Epoch; if one but not the other has an epoch set, default
rhughes@241
   444
	 * the epoch-less version to 0. */
rhughes@241
   445
	res = (*p1 == ':') - (*p2 == ':');
rhughes@241
   446
	if (res < 0) {
rhughes@241
   447
		n1 = 0;
rhughes@241
   448
		p1 = s1;
rhughes@241
   449
		p2++;
rhughes@241
   450
	} else if (res > 0) {
rhughes@241
   451
		p1++;
rhughes@241
   452
		n2 = 0;
rhughes@241
   453
		p2 = s2;
rhughes@241
   454
	}
rhughes@241
   455
rhughes@241
   456
	if (n1 != n2)
rhughes@241
   457
		return n1 - n2;
rhughes@241
   458
	while (*p1 && *p2) {
rhughes@241
   459
		if (*p1 != *p2)
rhughes@241
   460
			return *p1 - *p2;
rhughes@241
   461
		p1++;
rhughes@241
   462
		p2++;
rhughes@241
   463
		if (isdigit(*p1) && isdigit(*p2))
krh@248
   464
			return razor_versioncmp(p1, p2);
rhughes@241
   465
	}
rhughes@241
   466
rhughes@241
   467
	return *p1 - *p2;
rhughes@241
   468
}
rhughes@241
   469
richard@302
   470
static const char *
ali@372
   471
razor_package_get_details_string(struct razor_set *set,
ali@372
   472
				 struct razor_package *package,
ali@372
   473
				 enum razor_detail_type type)
richard@302
   474
{
richard@302
   475
	const char *pool;
richard@302
   476
richard@302
   477
	switch (type) {
richard@302
   478
	case RAZOR_DETAIL_NAME:
richard@302
   479
		pool = set->string_pool.data;
richard@302
   480
		return &pool[package->name];
richard@302
   481
richard@302
   482
	case RAZOR_DETAIL_VERSION:
richard@302
   483
		pool = set->string_pool.data;
richard@302
   484
		return &pool[package->version];
richard@302
   485
richard@302
   486
	case RAZOR_DETAIL_ARCH:
richard@302
   487
		pool = set->string_pool.data;
richard@302
   488
		return &pool[package->arch];
richard@302
   489
richard@302
   490
	case RAZOR_DETAIL_SUMMARY:
ali@395
   491
		if (!set->details_string_pool.size)
ali@395
   492
			return "";
richard@302
   493
		pool = set->details_string_pool.data;
richard@302
   494
		return &pool[package->summary];
richard@302
   495
richard@302
   496
	case RAZOR_DETAIL_DESCRIPTION:
ali@395
   497
		if (!set->details_string_pool.size)
ali@395
   498
			return "";
richard@302
   499
		pool = set->details_string_pool.data;
richard@302
   500
		return &pool[package->description];
richard@302
   501
richard@302
   502
	case RAZOR_DETAIL_URL:
ali@395
   503
		if (!set->details_string_pool.size)
ali@395
   504
			return "";
richard@302
   505
		pool = set->details_string_pool.data;
richard@302
   506
		return &pool[package->url];
richard@302
   507
richard@302
   508
	case RAZOR_DETAIL_LICENSE:
ali@395
   509
		if (!set->details_string_pool.size)
ali@395
   510
			return "";
richard@302
   511
		pool = set->details_string_pool.data;
richard@302
   512
		return &pool[package->license];
richard@302
   513
ali@369
   514
	case RAZOR_DETAIL_PREUNPROG:
ali@369
   515
		pool = set->string_pool.data;
ali@369
   516
		return &pool[package->preun.program];
ali@369
   517
ali@369
   518
	case RAZOR_DETAIL_PREUN:
ali@369
   519
		pool = set->string_pool.data;
ali@369
   520
		return &pool[package->preun.body];
ali@369
   521
ali@369
   522
	case RAZOR_DETAIL_POSTUNPROG:
ali@369
   523
		pool = set->string_pool.data;
ali@369
   524
		return &pool[package->postun.program];
ali@369
   525
ali@369
   526
	case RAZOR_DETAIL_POSTUN:
ali@369
   527
		pool = set->string_pool.data;
ali@369
   528
		return &pool[package->postun.body];
ali@369
   529
richard@302
   530
	default:
richard@302
   531
		fprintf(stderr, "type %u not found\n", type);
richard@302
   532
		return NULL;
richard@302
   533
	}
richard@302
   534
}
richard@302
   535
ali@372
   536
static const char *const *
ali@372
   537
razor_package_get_details_array(struct razor_set *set,
ali@372
   538
				struct razor_package *package,
ali@372
   539
				enum razor_detail_type type)
ali@372
   540
{
ali@372
   541
	switch (type) {
ali@372
   542
	case RAZOR_DETAIL_PREFIXES:
ali@372
   543
		/* We don't track prefixes in packages. Install prefixes
ali@372
   544
		 * are tracked, but we don't provide an API to get them.
ali@372
   545
		 */
ali@372
   546
		return NULL;
ali@372
   547
ali@372
   548
	default:
ali@372
   549
		fprintf(stderr, "type %u not found\n", type);
ali@372
   550
		return NULL;
ali@372
   551
	}
ali@372
   552
}
ali@372
   553
richard@302
   554
/**
richard@302
   555
 * razor_package_get_details_varg:
richard@302
   556
 * @set: a %razor_set
richard@302
   557
 * @package: a %razor_package
richard@302
   558
 * @args: a va_list of arguments to set
richard@302
   559
 **/
richard@302
   560
void
richard@302
   561
razor_package_get_details_varg(struct razor_set *set,
richard@302
   562
			       struct razor_package *package,
richard@302
   563
			       va_list args)
richard@302
   564
{
richard@302
   565
	int i;
richard@302
   566
	enum razor_detail_type type;
ali@372
   567
	const char **string;
ali@372
   568
	const char *const **array;
richard@302
   569
richard@302
   570
	for (i = 0;; i += 2) {
richard@302
   571
		type = va_arg(args, enum razor_detail_type);
richard@307
   572
		if (type == RAZOR_DETAIL_LAST)
richard@302
   573
			break;
ali@372
   574
		if (type == RAZOR_DETAIL_PREFIXES) {
ali@372
   575
			array = va_arg(args, const char *const **);
ali@372
   576
			*array = razor_package_get_details_array(set, package,
ali@372
   577
								 type);
ali@372
   578
		} else {
ali@372
   579
			string = va_arg(args, const char **);
ali@372
   580
			*string = razor_package_get_details_string(set, package,
ali@372
   581
								   type);
ali@372
   582
		}
richard@302
   583
	}
richard@302
   584
richard@302
   585
}
richard@302
   586
richard@302
   587
/**
richard@302
   588
 * razor_package_get_details:
richard@302
   589
 * @set: a %razor_set
richard@302
   590
 * @package: a %razor_package
richard@302
   591
 *
richard@302
   592
 * Gets details about a package using a varg interface
krh@308
   593
 * The vararg must be terminated with %RAZOR_DETAIL_LAST.
richard@302
   594
 *
richard@307
   595
 * Example: razor_package_get_details (set, package,
richard@307
   596
 *				       RAZOR_DETAIL_URL, &url,
richard@307
   597
 *				       RAZOR_DETAIL_LAST);
richard@302
   598
 **/
krh@269
   599
RAZOR_EXPORT void
richard@302
   600
razor_package_get_details(struct razor_set *set, struct razor_package *package, ...)
jbowes@258
   601
{
richard@302
   602
	va_list args;
jbowes@258
   603
richard@301
   604
	assert (set != NULL);
richard@301
   605
	assert (package != NULL);
richard@301
   606
richard@302
   607
	va_start(args, NULL);
richard@302
   608
	razor_package_get_details_varg (set, package, args);
richard@302
   609
	va_end (args);
jbowes@258
   610
}
jbowes@258
   611
ali@369
   612
/**
ali@369
   613
 * razor_package_remove:
ali@382
   614
 * @prev: The %razor_set before the current transaction
ali@382
   615
 * @next: The %razor_set after the current transaction is applied
ali@369
   616
 * @package: a %razor_package
ali@369
   617
 * @root: the root into which the package is currently installed
ali@376
   618
 * @install_count: the value to pass to uninstall scripts
ali@403
   619
 * @stage: Limit the removal to just the scripts or the files
ali@369
   620
 *
ali@369
   621
 * Removes an installed package.
ali@369
   622
 **/
ali@369
   623
RAZOR_EXPORT int
ali@382
   624
razor_package_remove(struct razor_set *prev, struct razor_set *next,
ali@403
   625
		     struct razor_atomic *atomic, struct razor_package *package,
ali@403
   626
		     const char *root, int install_count,
ali@403
   627
		     enum razor_stage_type stage)
ali@369
   628
{
ali@369
   629
	struct razor_file_iterator *fi;
ali@369
   630
	struct razor_package_iterator *pi;
ali@369
   631
	struct razor_package *p;
ali@411
   632
	char *buffer, buf[32];
ali@369
   633
	const char *name, *program, *script;
ali@422
   634
	int i, count, retval = 0;
ali@372
   635
	struct environment env;
ali@372
   636
	struct list *link;
ali@372
   637
	const char *prefix;
ali@369
   638
ali@403
   639
	if (stage & RAZOR_STAGE_SCRIPTS) {
ali@403
   640
		environment_init(&env);
ali@403
   641
		link = list_first(&package->install_prefixes,
ali@403
   642
				  &prev->prefix_pool);
ali@403
   643
		for (i = 0; link; i++) {
ali@403
   644
			prefix = (const char *)prev->string_pool.data +
ali@403
   645
				 link->data;
ali@411
   646
			sprintf(buf, "RPM_INSTALL_PREFIX%d", i);
ali@411
   647
			environment_add_variable(&env, buf, prefix);
ali@403
   648
			link = list_next(link);
ali@403
   649
		}
ali@403
   650
		environment_set(&env);
ali@375
   651
	}
ali@375
   652
ali@403
   653
	if (stage & RAZOR_STAGE_SCRIPTS_PRE) {
ali@403
   654
		razor_package_get_details(prev, package,
ali@403
   655
					  RAZOR_DETAIL_PREUNPROG, &program,
ali@403
   656
					  RAZOR_DETAIL_PREUN, &script,
ali@403
   657
					  RAZOR_DETAIL_LAST);
ali@369
   658
ali@422
   659
		retval = razor_run_script(root, RAZOR_PROPERTY_PREUN, program,
ali@422
   660
					  script, install_count);
ali@403
   661
	}
ali@375
   662
ali@422
   663
	if (!retval && (stage & RAZOR_STAGE_FILES)) {
ali@403
   664
		fi = razor_file_iterator_create(prev, package, 1);
ali@369
   665
ali@403
   666
		while (razor_file_iterator_next(fi, &name)) {
ali@403
   667
			pi = razor_package_iterator_create_for_file(next, name);
ali@403
   668
			count = 0;
ali@403
   669
			while (razor_package_iterator_next(pi, &p,
ali@403
   670
							   RAZOR_DETAIL_LAST))
ali@403
   671
				count++;
ali@403
   672
			razor_package_iterator_destroy(pi);
ali@403
   673
			if (count <= 0) {
ali@403
   674
				buffer = razor_concat(root, name, NULL);
ali@403
   675
				razor_atomic_remove(atomic, buffer);
ali@403
   676
				free(buffer);
ali@377
   677
			}
ali@369
   678
		}
ali@403
   679
ali@403
   680
		razor_file_iterator_destroy(fi);
ali@422
   681
ali@422
   682
		retval = razor_atomic_in_error_state(atomic);
ali@369
   683
	}
ali@369
   684
ali@422
   685
	if (!retval && (stage & RAZOR_STAGE_SCRIPTS_POST)) {
ali@403
   686
		razor_package_get_details(prev, package,
ali@403
   687
					  RAZOR_DETAIL_POSTUNPROG, &program,
ali@403
   688
					  RAZOR_DETAIL_POSTUN, &script,
ali@403
   689
					  RAZOR_DETAIL_LAST);
ali@369
   690
ali@422
   691
		retval |= razor_run_script(root, RAZOR_PROPERTY_POSTUN, program,
ali@422
   692
					   script, install_count);
ali@403
   693
	}
ali@369
   694
ali@403
   695
	if (stage & RAZOR_STAGE_SCRIPTS) {
ali@403
   696
		environment_unset(&env);
ali@403
   697
		environment_release(&env);
ali@403
   698
	}
ali@377
   699
ali@422
   700
	return retval;
ali@369
   701
}
ali@369
   702
krh@270
   703
RAZOR_EXPORT const char *
krh@270
   704
razor_property_relation_to_string(struct razor_property *p)
krh@270
   705
{
richard@301
   706
	assert (p != NULL);
richard@301
   707
krh@270
   708
	switch (p->flags & RAZOR_PROPERTY_RELATION_MASK) {
krh@270
   709
	case RAZOR_PROPERTY_LESS:
krh@270
   710
		return "<";
krh@270
   711
krh@270
   712
	case RAZOR_PROPERTY_LESS | RAZOR_PROPERTY_EQUAL:
krh@270
   713
		return "<=";
krh@270
   714
krh@270
   715
	case RAZOR_PROPERTY_EQUAL:
krh@270
   716
		return "=";
krh@270
   717
krh@270
   718
	case RAZOR_PROPERTY_GREATER | RAZOR_PROPERTY_EQUAL:
krh@270
   719
		return ">=";
krh@270
   720
krh@270
   721
	case RAZOR_PROPERTY_GREATER:
krh@270
   722
		return ">";
krh@270
   723
krh@270
   724
	default:
krh@270
   725
		return "?";
krh@270
   726
	}
krh@270
   727
}
krh@270
   728
krh@270
   729
RAZOR_EXPORT const char *
krh@270
   730
razor_property_type_to_string(struct razor_property *p)
krh@270
   731
{
richard@301
   732
	assert (p != NULL);
richard@301
   733
krh@270
   734
	switch (p->flags & RAZOR_PROPERTY_TYPE_MASK) {
krh@270
   735
	case RAZOR_PROPERTY_REQUIRES:
krh@270
   736
		return "requires";
krh@270
   737
	case RAZOR_PROPERTY_PROVIDES:
krh@270
   738
		return "provides";
krh@270
   739
	case RAZOR_PROPERTY_CONFLICTS:
krh@270
   740
		return "conflicts";
krh@270
   741
	case RAZOR_PROPERTY_OBSOLETES:
krh@270
   742
		return "obsoletes";
krh@270
   743
	default:
krh@270
   744
		return NULL;
krh@270
   745
	}
krh@270
   746
}
krh@270
   747
krh@269
   748
RAZOR_EXPORT struct razor_entry *
krh@248
   749
razor_set_find_entry(struct razor_set *set,
krh@248
   750
		     struct razor_entry *dir, const char *pattern)
rhughes@241
   751
{
ali@359
   752
	struct razor_entry *e, *subdir;
jbowes@264
   753
	const char *n, *pool = set->file_string_pool.data;
rhughes@241
   754
	int len;
rhughes@241
   755
richard@301
   756
	assert (set != NULL);
richard@301
   757
	assert (pattern != NULL);
richard@301
   758
ali@383
   759
	if (dir == NULL)
ali@383
   760
		return NULL;
ali@383
   761
ali@359
   762
	e = dir;
rhughes@241
   763
	do {
rhughes@241
   764
		n = pool + e->name;
ali@359
   765
		if (strcmp(pattern, n) == 0)
rhughes@241
   766
			return e;
rhughes@241
   767
		len = strlen(n);
ali@359
   768
		if (e->start != 0 && strncmp(pattern, n, len) == 0 &&
ali@359
   769
		    pattern[len] == '/') {
ali@359
   770
			subdir = (struct razor_entry *) set->files.data +
ali@359
   771
				 e->start;
ali@359
   772
			return razor_set_find_entry(set, subdir,
ali@359
   773
						    pattern + len + 1);
rhughes@241
   774
		}
rhughes@241
   775
	} while (!((e++)->flags & RAZOR_ENTRY_LAST));
rhughes@241
   776
rhughes@241
   777
	return NULL;
rhughes@241
   778
}
rhughes@241
   779
rhughes@241
   780
static void
rhughes@241
   781
list_dir(struct razor_set *set, struct razor_entry *dir,
rhughes@241
   782
	 char *prefix, const char *pattern)
rhughes@241
   783
{
ali@359
   784
	struct razor_entry *e, *subdir;
jbowes@274
   785
	const char *n, *pool = set->file_string_pool.data;
rhughes@241
   786
ali@359
   787
	e = dir;
rhughes@241
   788
	do {
rhughes@241
   789
		n = pool + e->name;
rhughes@241
   790
		if (pattern && pattern[0] && fnmatch(pattern, n, 0) != 0)
rhughes@241
   791
			continue;
rhughes@241
   792
		printf("%s/%s\n", prefix, n);
rhughes@241
   793
		if (e->start) {
rhughes@241
   794
			char *sub = prefix + strlen (prefix);
rhughes@241
   795
			*sub = '/';
rhughes@241
   796
			strcpy (sub + 1, n);
ali@359
   797
			subdir = (struct razor_entry *) set->files.data +
ali@359
   798
				 e->start;
ali@359
   799
			list_dir(set, subdir, prefix, pattern);
rhughes@241
   800
			*sub = '\0';
rhughes@241
   801
		}
rhughes@241
   802
	} while (!((e++)->flags & RAZOR_ENTRY_LAST));
rhughes@241
   803
}
rhughes@241
   804
krh@269
   805
RAZOR_EXPORT void
rhughes@241
   806
razor_set_list_files(struct razor_set *set, const char *pattern)
rhughes@241
   807
{
ali@359
   808
	struct razor_entry *root, *e;
rhughes@241
   809
	char buffer[512], *p, *base;
rhughes@241
   810
richard@301
   811
	assert (set != NULL);
richard@301
   812
ali@359
   813
	root = (struct razor_entry *) set->files.data;
ali@359
   814
ali@359
   815
	if (pattern == NULL) {
ali@359
   816
		p = set->file_string_pool.data;
ali@359
   817
		e = root;
ali@359
   818
		do {
ali@359
   819
			if (e->start) {
ali@359
   820
				strcpy(buffer, p + e->name);
ali@359
   821
				list_dir(set, root + e->start, buffer, NULL);
ali@359
   822
			}
ali@359
   823
		} while (!((e++)->flags & RAZOR_ENTRY_LAST));
rhughes@241
   824
		return;
rhughes@241
   825
	}
rhughes@241
   826
rhughes@241
   827
	strcpy(buffer, pattern);
ali@359
   828
	e = razor_set_find_entry(set, root, buffer);
ali@359
   829
	if (e && e->start) {
rhughes@241
   830
		base = NULL;
rhughes@241
   831
	} else {
rhughes@241
   832
		p = strrchr(buffer, '/');
rhughes@241
   833
		if (p) {
rhughes@241
   834
			*p = '\0';
rhughes@241
   835
			base = p + 1;
rhughes@241
   836
		} else {
rhughes@241
   837
			base = NULL;
rhughes@241
   838
		}
rhughes@241
   839
	}
ali@359
   840
	e = razor_set_find_entry(set, root, buffer);
ali@359
   841
	if (e && e->start)
ali@359
   842
		list_dir(set, root + e->start, buffer, base);
rhughes@241
   843
}
rhughes@241
   844
krh@269
   845
RAZOR_EXPORT void
krh@306
   846
razor_set_list_package_files(struct razor_set *set,
krh@306
   847
			     struct razor_package *package)
rhughes@241
   848
{
ali@351
   849
	struct razor_file_iterator *fi;
ali@351
   850
	const char *name;
rhughes@241
   851
richard@301
   852
	assert (set != NULL);
krh@306
   853
	assert (package != NULL);
rhughes@241
   854
ali@377
   855
	fi = razor_file_iterator_create(set, package, 0);
ali@351
   856
ali@351
   857
	while (razor_file_iterator_next(fi, &name))
ali@351
   858
		printf("%s\n", name);
ali@351
   859
ali@351
   860
	razor_file_iterator_destroy(fi);
rhughes@241
   861
}
rhughes@241
   862
ali@387
   863
/*
ali@387
   864
 * Package data can potentially come from two places. The so-called
ali@387
   865
 * metadata (eg., from comps.xml) and from an RPM file. We consider
ali@387
   866
 * a package which has additional data from an RPM file as "fixed".
ali@387
   867
 * If a package needs fixing, then razor_transaction_fixup_package()
ali@387
   868
 * will do so. When considering what packages to add and to remove
ali@387
   869
 * we need to take this into account since we always want to add
ali@387
   870
 * unfixed packages (otherwise we have a potential conflict between
ali@387
   871
 * the existing package data and that present in the RPM).
ali@387
   872
 */
ali@387
   873
static int
ali@387
   874
razor_package_is_fixed(struct razor_set *set, struct razor_package *p)
ali@387
   875
{
ali@387
   876
	const char *preunprog, *preun, *postunprog, *postun;
ali@387
   877
ali@387
   878
	if (!p)
ali@387
   879
		return 0;
ali@387
   880
	razor_package_get_details(set, p,
ali@387
   881
				  RAZOR_DETAIL_PREUNPROG, &preunprog,
ali@387
   882
				  RAZOR_DETAIL_PREUN, &preun,
ali@387
   883
				  RAZOR_DETAIL_POSTUNPROG, &postunprog,
ali@387
   884
				  RAZOR_DETAIL_POSTUN, &postun,
ali@387
   885
				  RAZOR_DETAIL_LAST);
ali@387
   886
	return *preunprog || *preun || *postunprog || *postun;
ali@387
   887
}
ali@387
   888
krh@269
   889
RAZOR_EXPORT void
rhughes@241
   890
razor_set_diff(struct razor_set *set, struct razor_set *upstream,
krh@253
   891
	       razor_diff_callback_t callback, void *data)
rhughes@241
   892
{
rhughes@241
   893
 	struct razor_package_iterator *pi1, *pi2;
rhughes@241
   894
 	struct razor_package *p1, *p2;
rhughes@241
   895
	const char *name1, *name2, *version1, *version2, *arch1, *arch2;
ali@387
   896
	int res, is_fixed1, is_fixed2;
rhughes@241
   897
richard@301
   898
	assert (set != NULL);
richard@301
   899
	assert (upstream != NULL);
richard@301
   900
rhughes@241
   901
	pi1 = razor_package_iterator_create(set);
rhughes@241
   902
	pi2 = razor_package_iterator_create(upstream);
rhughes@241
   903
richard@302
   904
	razor_package_iterator_next(pi1, &p1,
richard@302
   905
				    RAZOR_DETAIL_NAME, &name1,
richard@302
   906
				    RAZOR_DETAIL_VERSION, &version1,
richard@302
   907
				    RAZOR_DETAIL_ARCH, &arch1,
richard@307
   908
				    RAZOR_DETAIL_LAST);
ali@387
   909
	is_fixed1 = razor_package_is_fixed(set, p1);
richard@302
   910
	razor_package_iterator_next(pi2, &p2,
richard@302
   911
				    RAZOR_DETAIL_NAME, &name2,
richard@302
   912
				    RAZOR_DETAIL_VERSION, &version2,
richard@302
   913
				    RAZOR_DETAIL_ARCH, &arch2,
richard@307
   914
				    RAZOR_DETAIL_LAST);
ali@387
   915
	is_fixed2 = razor_package_is_fixed(upstream, p2);
rhughes@241
   916
rhughes@241
   917
	while (p1 || p2) {
rhughes@241
   918
		if (p1 && p2) {
rhughes@241
   919
			res = strcmp(name1, name2);
rhughes@241
   920
			if (res == 0)
krh@248
   921
				res = razor_versioncmp(version1, version2);
ali@387
   922
			if (res == 0)
ali@387
   923
				res = is_fixed1 - is_fixed2;
rhughes@241
   924
		} else {
rhughes@241
   925
			res = 0;
rhughes@241
   926
		}
rhughes@241
   927
rhughes@241
   928
		if (p2 == NULL || res < 0)
krh@253
   929
			callback(RAZOR_DIFF_ACTION_REMOVE,
krh@253
   930
				 p1, name1, version1, arch1, data);
rhughes@241
   931
		else if (p1 == NULL || res > 0)
krh@253
   932
			callback(RAZOR_DIFF_ACTION_ADD,
krh@253
   933
				 p2, name2, version2, arch2, data);
rhughes@241
   934
ali@387
   935
		if (p1 != NULL && res <= 0) {
rhughes@241
   936
			razor_package_iterator_next(pi1, &p1,
richard@302
   937
						    RAZOR_DETAIL_NAME, &name1,
richard@302
   938
						    RAZOR_DETAIL_VERSION, &version1,
richard@302
   939
						    RAZOR_DETAIL_ARCH, &arch1,
richard@307
   940
						    RAZOR_DETAIL_LAST);
ali@387
   941
			is_fixed1 = razor_package_is_fixed(set, p1);
ali@387
   942
		}
ali@387
   943
		if (p2 != NULL && res >= 0) {
rhughes@241
   944
			razor_package_iterator_next(pi2, &p2,
richard@302
   945
						    RAZOR_DETAIL_NAME, &name2,
richard@302
   946
						    RAZOR_DETAIL_VERSION, &version2,
richard@302
   947
						    RAZOR_DETAIL_ARCH, &arch2,
richard@307
   948
						    RAZOR_DETAIL_LAST);
ali@387
   949
			is_fixed2 = razor_package_is_fixed(upstream, p2);
ali@387
   950
		}
rhughes@241
   951
	}
rhughes@241
   952
rhughes@241
   953
	razor_package_iterator_destroy(pi1);
rhughes@241
   954
	razor_package_iterator_destroy(pi2);
rhughes@241
   955
}
krh@254
   956
krh@316
   957
struct install_action {
krh@316
   958
	enum razor_install_action action;
krh@316
   959
	struct razor_package *package;
krh@316
   960
};
krh@316
   961
krh@316
   962
struct razor_install_iterator {
krh@316
   963
	struct razor_set *set;
krh@316
   964
	struct razor_set *next;
krh@316
   965
	struct array actions;
ali@403
   966
	struct deque *order, *left;
krh@316
   967
};
krh@316
   968
krh@254
   969
static void
krh@316
   970
add_action(enum razor_diff_action action,
krh@316
   971
	   struct razor_package *package,
krh@316
   972
	   const char *name,
krh@316
   973
	   const char *version,
krh@316
   974
	   const char *arch,
krh@316
   975
	   void *data)
krh@254
   976
{
krh@316
   977
	struct razor_install_iterator *ii = data;
krh@316
   978
	struct install_action *a;
krh@316
   979
krh@316
   980
	a = array_add(&ii->actions, sizeof *a);
krh@316
   981
	a->package = package;
krh@316
   982
krh@316
   983
	switch (action) {
krh@316
   984
	case RAZOR_DIFF_ACTION_ADD:
krh@316
   985
		a->action = RAZOR_INSTALL_ACTION_ADD;
krh@316
   986
		break;
krh@316
   987
	case RAZOR_DIFF_ACTION_REMOVE:
krh@316
   988
		a->action = RAZOR_INSTALL_ACTION_REMOVE;
krh@316
   989
		break;
krh@316
   990
	}
krh@254
   991
}
krh@254
   992
ali@418
   993
/*
ali@418
   994
 * Does <package> have a requirement for <script> which is
ali@418
   995
 * satisfied by <provider> ?
ali@418
   996
 * Note: We already know that <provider> is to be added as part of this install
ali@418
   997
 * so there is no need to check the relation.
ali@418
   998
 */
ali@418
   999
static int
ali@418
  1000
package_script_requires(struct razor_set *set, struct razor_package *package,
ali@418
  1001
			enum razor_property_flags script,
ali@418
  1002
			struct razor_package *provider)
ali@418
  1003
{
ali@418
  1004
	struct list *link;
ali@418
  1005
	struct razor_property *prop;
ali@418
  1006
ali@418
  1007
	link = list_first(&package->properties, &set->property_pool);
ali@418
  1008
	for(; link; link = list_next(link)) {
ali@418
  1009
		prop = set->properties.data;
ali@418
  1010
		prop += link->data;
ali@418
  1011
		if ((prop->flags & RAZOR_PROPERTY_SCRIPT_MASK) & script &&
ali@418
  1012
		    (prop->flags & RAZOR_PROPERTY_TYPE_MASK) == RAZOR_PROPERTY_REQUIRES &&
ali@418
  1013
		    provider->name == prop->name)
ali@418
  1014
			return 1;
ali@418
  1015
	}
ali@418
  1016
	return 0;
ali@418
  1017
}
ali@418
  1018
krh@316
  1019
RAZOR_EXPORT struct razor_install_iterator *
krh@316
  1020
razor_set_create_install_iterator(struct razor_set *set,
krh@316
  1021
				  struct razor_set *next)
krh@254
  1022
{
krh@316
  1023
	struct razor_install_iterator *ii;
ali@367
  1024
	struct razor_property *prop;
ali@367
  1025
	/* A graph of the actions to be perfomed where
ali@367
  1026
	 * A->B means action A should follow action B.
ali@367
  1027
	 */
ali@367
  1028
	struct graph follows;
ali@418
  1029
	struct install_action *actions, *ai, *aj, *an;
ali@367
  1030
	int i, j, count, vertex_added;
ali@367
  1031
	struct list *link;
ali@367
  1032
	struct razor_set *rs;
ali@418
  1033
	struct deque *order;
ali@418
  1034
	int barrier_needed;
krh@254
  1035
richard@301
  1036
	assert (set != NULL);
richard@301
  1037
	assert (next != NULL);
richard@301
  1038
krh@316
  1039
	ii = zalloc(sizeof *ii);
krh@316
  1040
	ii->set = set;
krh@316
  1041
	ii->next = next;
krh@316
  1042
	
krh@316
  1043
	razor_set_diff(set, next, add_action, ii);
krh@254
  1044
ali@367
  1045
	actions = ii->actions.data;
ali@367
  1046
	count = ii->actions.size / sizeof (struct install_action);
krh@254
  1047
ali@367
  1048
	graph_init(&follows);
ali@367
  1049
ali@367
  1050
	for(i = 0; i < count; i++) {
ali@367
  1051
		ai = actions + i;
ali@367
  1052
		rs = ai->action == RAZOR_INSTALL_ACTION_ADD ? next : set;
ali@367
  1053
		vertex_added = 0;
ali@367
  1054
		link = list_first(&ai->package->properties, &rs->property_pool);
ali@367
  1055
		for(; link; link = list_next(link)) {
ali@367
  1056
			prop = rs->properties.data;
ali@367
  1057
			prop += link->data;
ali@367
  1058
			switch(prop->flags & RAZOR_PROPERTY_TYPE_MASK) {
ali@367
  1059
			case RAZOR_PROPERTY_REQUIRES:
ali@367
  1060
			case RAZOR_PROPERTY_CONFLICTS:
ali@367
  1061
				for(j = 0; j < count; j++) {
ali@367
  1062
					if (j == i)
ali@367
  1063
						continue;
ali@367
  1064
					aj = actions + j;
ali@367
  1065
					if (aj->package->name == prop->name) {
ali@367
  1066
						if (ai->action ==
ali@367
  1067
						    RAZOR_INSTALL_ACTION_ADD)
ali@367
  1068
							graph_add_edge(&follows,
ali@367
  1069
								       i, j);
ali@367
  1070
						else
ali@367
  1071
							graph_add_edge(&follows,
ali@367
  1072
								       j, i);
ali@367
  1073
						vertex_added++;
ali@367
  1074
					}
ali@367
  1075
				}
ali@367
  1076
				break;
ali@367
  1077
			}
ali@367
  1078
		}
ali@367
  1079
		if (ai->action == RAZOR_INSTALL_ACTION_ADD) {
ali@367
  1080
			for(j = 0; j < count; j++) {
ali@367
  1081
				if (j == i)
ali@367
  1082
					continue;
ali@367
  1083
				aj = actions + j;
ali@367
  1084
				if (aj->package == ai->package &&
ali@367
  1085
				    aj->action == RAZOR_INSTALL_ACTION_REMOVE) {
ali@367
  1086
					graph_add_edge(&follows, i, j);
ali@367
  1087
					vertex_added++;
ali@367
  1088
				}
ali@367
  1089
			}
ali@367
  1090
		}
ali@367
  1091
		if (!vertex_added)
ali@367
  1092
			graph_add_edge(&follows, i, i);
ali@367
  1093
	}
ali@367
  1094
ali@418
  1095
	/*
ali@418
  1096
	 * Because files are installed and removed using razor_atomic,
ali@418
  1097
	 * but scripts are run with no regard for these, we need some
ali@418
  1098
	 * means for synchronisation between the two. We support this
ali@418
  1099
	 * via transaction barriers which are points where
ali@418
  1100
	 * razor_atomic_commit() should be called so that scripts can
ali@418
  1101
	 * rely on the files they need being present.
ali@418
  1102
	 *
ali@418
  1103
	 * Rules:
ali@418
  1104
	 *   1)	Transaction barriers never occur within a dependency
ali@418
  1105
	 *	loop. Since we can't guarantee any ordering of actions
ali@418
  1106
	 *	within such a loop, they make no sense.
ali@418
  1107
	 *   2) Otherwise the following table applies:
ali@418
  1108
	 *	Action I    Action J	Barrier between I and J iff
ali@418
  1109
	 *	ADD P	    ADD Q	%pre(Q) requires P
ali@418
  1110
	 *	ADD P	    REMOVE Q	%preun(Q) requires P
ali@418
  1111
	 *	REMOVE P    ADD Q	never
ali@418
  1112
	 *	REMOVE P    REMOVE Q	%postun(P) requires Q
ali@418
  1113
	 *
ali@418
  1114
	 * FIXME:
ali@418
  1115
	 *	This should take account of conflicts somehow.
ali@418
  1116
	 */
ali@418
  1117
	order = graph_sort(&follows);
ali@418
  1118
	ii->order = deque_new(0);
ali@418
  1119
	if (!deque_empty(order)) {
ali@418
  1120
		j = deque_pop(order);
ali@418
  1121
		deque_unshift(ii->order, j);
ali@418
  1122
	}
ali@418
  1123
	while (!deque_empty(order)) {
ali@418
  1124
		i = j;
ali@418
  1125
		j = deque_pop(order);
ali@418
  1126
		ai = actions + i;
ali@418
  1127
		aj = actions + j;
ali@418
  1128
		if (graph_is_connected(&follows, i, j) &&
ali@418
  1129
		    graph_is_connected(&follows, j, i))
ali@418
  1130
			barrier_needed = 0;
ali@418
  1131
		else if (ai->action == RAZOR_INSTALL_ACTION_ADD &&
ali@418
  1132
			 aj->action == RAZOR_INSTALL_ACTION_ADD &&
ali@418
  1133
			 package_script_requires(next, aj->package,
ali@418
  1134
						 RAZOR_PROPERTY_PRE,
ali@418
  1135
						 ai->package))
ali@418
  1136
			barrier_needed = 1;
ali@418
  1137
		else if (ai->action == RAZOR_INSTALL_ACTION_ADD &&
ali@418
  1138
			 aj->action == RAZOR_INSTALL_ACTION_REMOVE &&
ali@418
  1139
			 package_script_requires(set, aj->package,
ali@418
  1140
						 RAZOR_PROPERTY_PREUN,
ali@418
  1141
						 ai->package))
ali@418
  1142
			barrier_needed = 1;
ali@418
  1143
		else if (ai->action == RAZOR_INSTALL_ACTION_REMOVE &&
ali@418
  1144
			 aj->action == RAZOR_INSTALL_ACTION_REMOVE &&
ali@418
  1145
			 package_script_requires(set, ai->package,
ali@418
  1146
						 RAZOR_PROPERTY_POSTUN,
ali@418
  1147
						 aj->package))
ali@418
  1148
			barrier_needed = 1;
ali@418
  1149
		else
ali@418
  1150
			barrier_needed = 0;
ali@418
  1151
		if (barrier_needed) {
ali@418
  1152
			an = array_add(&ii->actions, sizeof *an);
ali@418
  1153
			actions = ii->actions.data;
ali@418
  1154
			an->package = NULL;
ali@418
  1155
			an->action = RAZOR_INSTALL_ACTION_COMMIT;
ali@418
  1156
			deque_unshift(ii->order, an - actions);
ali@418
  1157
		}
ali@418
  1158
		deque_unshift(ii->order, j);
ali@418
  1159
	}
ali@418
  1160
	deque_free(order);
ali@418
  1161
ali@403
  1162
	ii->left = deque_dup(ii->order);
ali@367
  1163
	graph_release(&follows);
krh@254
  1164
krh@316
  1165
	return ii;
krh@254
  1166
}
krh@254
  1167
krh@316
  1168
RAZOR_EXPORT int
krh@316
  1169
razor_install_iterator_next(struct razor_install_iterator *ii,
krh@316
  1170
			    struct razor_package **package,
krh@316
  1171
			    enum razor_install_action *action,
krh@316
  1172
			    int *count)
krh@254
  1173
{
ali@367
  1174
	struct install_action *a;
ali@382
  1175
	struct razor_package_iterator *pi;
ali@382
  1176
	struct razor_package *pkg;
ali@382
  1177
	const char *removing, *name;
ali@382
  1178
ali@403
  1179
	if (deque_empty(ii->left))
krh@316
  1180
		return 0;
krh@254
  1181
ali@403
  1182
	a = (struct install_action *)ii->actions.data + deque_pop(ii->left);
ali@367
  1183
	*package = a->package;
ali@367
  1184
	*action = a->action;
krh@316
  1185
	*count = 0;
krh@254
  1186
ali@382
  1187
	if (a->action == RAZOR_INSTALL_ACTION_REMOVE) {
ali@382
  1188
		razor_package_get_details(ii->set, a->package,
ali@382
  1189
					  RAZOR_DETAIL_NAME, &removing,
ali@382
  1190
					  RAZOR_DETAIL_LAST);
ali@382
  1191
ali@382
  1192
		pi = razor_package_iterator_create(ii->next);
ali@382
  1193
		while (razor_package_iterator_next(pi, &pkg,
ali@382
  1194
						   RAZOR_DETAIL_NAME, &name,
ali@382
  1195
						   RAZOR_DETAIL_LAST)) {
ali@382
  1196
			if (!strcmp(name, removing))
ali@382
  1197
				(*count)++;
ali@382
  1198
		}
ali@382
  1199
		razor_package_iterator_destroy(pi);
ali@418
  1200
	} else if (a->action == RAZOR_INSTALL_ACTION_ADD)
ali@403
  1201
		*count = 1;
ali@382
  1202
krh@316
  1203
	return 1;
krh@316
  1204
}
krh@254
  1205
ali@418
  1206
static int
ali@418
  1207
action_is_included(struct razor_install_iterator *ii, struct deque *done,
ali@418
  1208
		   struct razor_package *package,
ali@418
  1209
		   enum razor_install_action action)
ali@418
  1210
{
ali@418
  1211
	struct deque *t;
ali@418
  1212
	struct install_action *a;
ali@418
  1213
	int retval;
ali@418
  1214
ali@418
  1215
	t = deque_dup(done);
ali@418
  1216
ali@418
  1217
	while(!deque_empty(t)) {
ali@418
  1218
		a = (struct install_action *)ii->actions.data + deque_pop(t);
ali@418
  1219
		if (a->package == package && a->action == action)
ali@418
  1220
			break;
ali@418
  1221
	}
ali@418
  1222
	retval = !deque_empty(t);
ali@418
  1223
ali@418
  1224
	deque_free(t);
ali@418
  1225
ali@418
  1226
	return retval;
ali@418
  1227
}
ali@418
  1228
ali@418
  1229
RAZOR_EXPORT struct razor_set *
ali@418
  1230
razor_install_iterator_commit_set(struct razor_install_iterator *ii)
ali@418
  1231
{
ali@418
  1232
	struct razor_merger *merger;
ali@418
  1233
	struct razor_set *set;
ali@442
  1234
	struct razor_package *n, *nend, *s, *send;
ali@418
  1235
	struct deque *done;
ali@418
  1236
	size_t pos;
ali@418
  1237
	char *npool, *spool;
ali@418
  1238
	int cmp;
ali@418
  1239
ali@418
  1240
	done = deque_dup(ii->order);
ali@418
  1241
	pos = razor_install_iterator_tell(ii);
ali@418
  1242
	while(deque_length(done) > pos)
ali@418
  1243
		deque_shift(done);
ali@418
  1244
ali@418
  1245
	s = ii->set->packages.data;
ali@418
  1246
	send = ii->set->packages.data + ii->set->packages.size;
ali@418
  1247
	spool = ii->set->string_pool.data;
ali@418
  1248
ali@418
  1249
	n = ii->next->packages.data;
ali@418
  1250
	nend = ii->next->packages.data + ii->next->packages.size;
ali@418
  1251
	npool = ii->next->string_pool.data;
ali@418
  1252
ali@418
  1253
	merger = razor_merger_create(ii->set, ii->next);
ali@418
  1254
	while (s < send || n < nend) {
ali@418
  1255
		if (s < send && n < nend)
ali@418
  1256
			cmp = strcmp(&spool[s->name], &npool[n->name]);
ali@418
  1257
		else if (s < send)
ali@418
  1258
			cmp = -1;
ali@418
  1259
		else
ali@418
  1260
			cmp = 1;
ali@418
  1261
ali@418
  1262
		if (cmp < 0) {
ali@418
  1263
			if (!action_is_included(ii, done, s,
ali@418
  1264
						RAZOR_INSTALL_ACTION_REMOVE))
ali@418
  1265
				razor_merger_add_package(merger, s);
ali@418
  1266
			s++;
ali@418
  1267
		} else if (cmp == 0) {
ali@418
  1268
			if (!action_is_included(ii, done, s,
ali@418
  1269
						RAZOR_INSTALL_ACTION_REMOVE))
ali@418
  1270
				razor_merger_add_package(merger, s);
ali@418
  1271
			if (action_is_included(ii, done, n,
ali@418
  1272
					       RAZOR_INSTALL_ACTION_ADD))
ali@418
  1273
				razor_merger_add_package(merger, n);
ali@418
  1274
ali@418
  1275
			s++;
ali@418
  1276
			n++;
ali@418
  1277
		} else {
ali@418
  1278
			if (action_is_included(ii, done, n,
ali@418
  1279
					       RAZOR_INSTALL_ACTION_ADD))
ali@418
  1280
				razor_merger_add_package(merger, n);
ali@418
  1281
			n++;
ali@418
  1282
		}
ali@418
  1283
	}
ali@418
  1284
ali@418
  1285
	set = razor_merger_commit(merger);
ali@418
  1286
	razor_merger_destroy(merger);
ali@418
  1287
ali@418
  1288
	return set;
ali@418
  1289
}
ali@418
  1290
krh@316
  1291
RAZOR_EXPORT void
ali@403
  1292
razor_install_iterator_rewind(struct razor_install_iterator *ii)
ali@403
  1293
{
ali@403
  1294
	deque_free(ii->left);
ali@403
  1295
	ii->left=deque_dup(ii->order);
ali@403
  1296
}
ali@403
  1297
ali@418
  1298
RAZOR_EXPORT size_t
ali@418
  1299
razor_install_iterator_tell(struct razor_install_iterator *ii)
ali@418
  1300
{
ali@418
  1301
	return deque_length(ii->order) - deque_length(ii->left);
ali@418
  1302
}
ali@418
  1303
ali@418
  1304
RAZOR_EXPORT size_t
ali@418
  1305
razor_install_iterator_seek(struct razor_install_iterator *ii, size_t pos)
ali@418
  1306
{
ali@418
  1307
	size_t current_pos;
ali@418
  1308
ali@418
  1309
	if (pos > deque_length(ii->order))
ali@418
  1310
		pos = deque_length(ii->order);
ali@418
  1311
ali@418
  1312
	current_pos = razor_install_iterator_tell(ii);
ali@418
  1313
ali@418
  1314
	if (pos < current_pos) {
ali@418
  1315
		razor_install_iterator_rewind(ii);
ali@418
  1316
		current_pos = 0;
ali@418
  1317
	}
ali@418
  1318
ali@418
  1319
	while(current_pos < pos) {
ali@418
  1320
		(void) deque_pop(ii->left);
ali@418
  1321
		current_pos++;
ali@418
  1322
	}
ali@418
  1323
ali@418
  1324
	return current_pos;
ali@418
  1325
}
ali@418
  1326
ali@403
  1327
RAZOR_EXPORT void
krh@316
  1328
razor_install_iterator_destroy(struct razor_install_iterator *ii)
krh@316
  1329
{
krh@316
  1330
	array_release(&ii->actions);
ali@367
  1331
	deque_free(ii->order);
ali@403
  1332
	deque_free(ii->left);
krh@316
  1333
	free(ii);
krh@254
  1334
}