yum.c
author Kristian H?gsberg <krh@redhat.com>
Mon Jun 09 12:47:37 2008 -0400 (2008-06-09)
changeset 230 c1e2aed8dd07
parent 192 55b177b689c0
child 224 5803b6151d02
permissions -rw-r--r--
Rewrite depsolver to use a series of passes over all packages.

The big change is that we follow one step of the depedency chain for
each package to resolve in each iteration, and repeat until there are
no more possible moves. In contrast the old depsolver would try to
follow the dependency chain completely for one package at a time.

This new approach is simpler and faster, and at the same time more
roboust. Instead of knowing how one newly installed package may
affect other packages (obsoleting, pulling in new packages etc), the
new algorithm just looks at the total list of requires, provides,
obsoletes and conflicts after installing new packages.
krh@212
     1
/*
krh@212
     2
 * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
krh@212
     3
 * Copyright (C) 2008  Red Hat, Inc
krh@212
     4
 *
krh@212
     5
 * This program is free software; you can redistribute it and/or modify
krh@212
     6
 * it under the terms of the GNU General Public License as published by
krh@212
     7
 * the Free Software Foundation; either version 2 of the License, or
krh@212
     8
 * (at your option) any later version.
krh@212
     9
 *
krh@212
    10
 * This program is distributed in the hope that it will be useful,
krh@212
    11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
krh@212
    12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
krh@212
    13
 * GNU General Public License for more details.
krh@212
    14
 *
krh@212
    15
 * You should have received a copy of the GNU General Public License along
krh@212
    16
 * with this program; if not, write to the Free Software Foundation, Inc.,
krh@212
    17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
krh@212
    18
 */
krh@212
    19
danw@109
    20
#define _GNU_SOURCE
danw@109
    21
danw@109
    22
#include <string.h>
danw@109
    23
#include <stdio.h>
danw@109
    24
#include <sys/stat.h>
danw@109
    25
#include <sys/mman.h>
danw@109
    26
#include <unistd.h>
danw@109
    27
#include <fcntl.h>
danw@109
    28
#include <errno.h>
danw@109
    29
danw@109
    30
#include <expat.h>
danw@109
    31
#include <zlib.h>
danw@109
    32
#include "razor.h"
danw@109
    33
danw@109
    34
/* Import a yum filelist as a razor package set. */
danw@109
    35
danw@109
    36
enum {
danw@109
    37
	YUM_STATE_BEGIN,
danw@109
    38
	YUM_STATE_PACKAGE_NAME,
krh@192
    39
	YUM_STATE_PACKAGE_ARCH,
danw@109
    40
	YUM_STATE_CHECKSUM,
danw@109
    41
	YUM_STATE_REQUIRES,
danw@109
    42
	YUM_STATE_PROVIDES,
danw@109
    43
	YUM_STATE_OBSOLETES,
danw@109
    44
	YUM_STATE_CONFLICTS,
danw@109
    45
	YUM_STATE_FILE
danw@109
    46
};
danw@109
    47
danw@109
    48
struct yum_context {
danw@109
    49
	XML_Parser primary_parser;
danw@109
    50
	XML_Parser filelists_parser;
danw@109
    51
	XML_Parser current_parser;
danw@109
    52
danw@109
    53
	struct razor_importer *importer;
danw@109
    54
	struct import_property_context *current_property_context;
krh@192
    55
	char name[256], arch[64], buffer[512], *p;
danw@109
    56
	char pkgid[128];
danw@109
    57
	int state;
danw@109
    58
};
danw@109
    59
danw@109
    60
static enum razor_version_relation
danw@109
    61
yum_to_razor_flags (const char *flags)
danw@109
    62
{
danw@109
    63
	/* FIXME? */
danw@109
    64
	if (!flags)
danw@109
    65
		return RAZOR_VERSION_EQUAL;
danw@109
    66
danw@109
    67
	if (flags[0] == 'L') {
danw@109
    68
		if (flags[1] == 'T')
danw@109
    69
			return RAZOR_VERSION_LESS;
danw@109
    70
		else
danw@109
    71
			return RAZOR_VERSION_LESS_OR_EQUAL;
danw@109
    72
	} else if (flags[0] == 'G') {
danw@109
    73
		if (flags[1] == 'T')
danw@109
    74
			return RAZOR_VERSION_GREATER;
danw@109
    75
		else
danw@109
    76
			return RAZOR_VERSION_GREATER_OR_EQUAL;
danw@109
    77
	} else
danw@109
    78
		return RAZOR_VERSION_EQUAL;
danw@109
    79
}
danw@109
    80
danw@109
    81
static void
danw@109
    82
yum_primary_start_element(void *data, const char *name, const char **atts)
danw@109
    83
{
danw@109
    84
	struct yum_context *ctx = data;
danw@143
    85
	const char *n, *epoch, *version, *release, *flags;
danw@109
    86
	char buffer[128];
danw@109
    87
	int i;
danw@109
    88
danw@109
    89
	if (strcmp(name, "name") == 0) {
danw@109
    90
		ctx->state = YUM_STATE_PACKAGE_NAME;
danw@109
    91
		ctx->p = ctx->name;
krh@192
    92
	} else if (strcmp(name, "arch") == 0) {
krh@192
    93
		ctx->state = YUM_STATE_PACKAGE_ARCH;
krh@192
    94
		ctx->p = ctx->arch;
danw@109
    95
	} else if (strcmp(name, "version") == 0) {
danw@143
    96
		epoch = NULL;
danw@109
    97
		version = NULL;
danw@109
    98
		release = NULL;
danw@109
    99
		for (i = 0; atts[i]; i += 2) {
danw@143
   100
			if (strcmp(atts[i], "epoch") == 0)
danw@143
   101
				epoch = atts[i + 1];
danw@143
   102
			else if (strcmp(atts[i], "ver") == 0)
danw@109
   103
				version = atts[i + 1];
danw@109
   104
			else if (strcmp(atts[i], "rel") == 0)
danw@109
   105
				release = atts[i + 1];
danw@109
   106
		}
danw@109
   107
		if (version == NULL || release == NULL) {
danw@109
   108
			fprintf(stderr, "invalid version tag, "
danw@109
   109
				"missing version or  release attribute\n");
danw@109
   110
			return;
danw@109
   111
		}
danw@109
   112
danw@143
   113
		razor_build_evr(buffer, sizeof buffer, epoch, version, release);
krh@192
   114
		razor_importer_begin_package(ctx->importer,
krh@192
   115
					     ctx->name, buffer, ctx->arch);
danw@109
   116
	} else if (strcmp(name, "checksum") == 0) {
danw@109
   117
		ctx->p = ctx->pkgid;
danw@109
   118
		ctx->state = YUM_STATE_CHECKSUM;
danw@109
   119
	} else if (strcmp(name, "rpm:requires") == 0) {
danw@109
   120
		ctx->state = YUM_STATE_REQUIRES;
danw@109
   121
	} else if (strcmp(name, "rpm:provides") == 0) {
danw@109
   122
		ctx->state = YUM_STATE_PROVIDES;
danw@109
   123
	} else if (strcmp(name, "rpm:obsoletes") == 0) {
danw@109
   124
		ctx->state = YUM_STATE_OBSOLETES;
danw@109
   125
	} else if (strcmp(name, "rpm:conflicts") == 0) {
danw@109
   126
		ctx->state = YUM_STATE_CONFLICTS;
danw@109
   127
	} else if (strcmp(name, "rpm:entry") == 0 &&
danw@109
   128
		   ctx->state != YUM_STATE_BEGIN) {
danw@109
   129
		n = NULL;
danw@143
   130
		epoch = NULL;
danw@109
   131
		version = NULL;
danw@109
   132
		release = NULL;
danw@109
   133
		flags = NULL;
danw@109
   134
		for (i = 0; atts[i]; i += 2) {
danw@109
   135
			if (strcmp(atts[i], "name") == 0)
danw@109
   136
				n = atts[i + 1];
danw@143
   137
			else if (strcmp(atts[i], "epoch") == 0)
danw@143
   138
				epoch = atts[i + 1];
danw@109
   139
			else if (strcmp(atts[i], "ver") == 0)
danw@109
   140
				version = atts[i + 1];
danw@109
   141
			else if (strcmp(atts[i], "rel") == 0)
danw@109
   142
				release = atts[i + 1];
danw@109
   143
			else if (strcmp(atts[i], "flags") == 0)
danw@109
   144
				flags = atts[i + 1];
danw@109
   145
		}
danw@109
   146
danw@109
   147
		if (n == NULL) {
danw@109
   148
			fprintf(stderr, "invalid rpm:entry, "
danw@109
   149
				"missing name or version attributes\n");
danw@109
   150
			return;
danw@109
   151
		}
danw@109
   152
danw@143
   153
		razor_build_evr(buffer, sizeof buffer, epoch, version, release);
danw@109
   154
		switch (ctx->state) {
danw@109
   155
		case YUM_STATE_REQUIRES:
danw@109
   156
			razor_importer_add_property(ctx->importer, n,
danw@109
   157
						    yum_to_razor_flags (flags),
danw@109
   158
						    buffer,
danw@109
   159
						    RAZOR_PROPERTY_REQUIRES);
danw@109
   160
			break;
danw@109
   161
		case YUM_STATE_PROVIDES:
danw@109
   162
			razor_importer_add_property(ctx->importer, n,
danw@109
   163
						    yum_to_razor_flags (flags),
danw@109
   164
						    buffer,
danw@109
   165
						    RAZOR_PROPERTY_PROVIDES);
danw@109
   166
			break;
danw@109
   167
		case YUM_STATE_OBSOLETES:
danw@109
   168
			razor_importer_add_property(ctx->importer, n,
danw@109
   169
						    yum_to_razor_flags (flags),
danw@109
   170
						    buffer,
danw@109
   171
						    RAZOR_PROPERTY_OBSOLETES);
danw@109
   172
			break;
danw@109
   173
		case YUM_STATE_CONFLICTS:
danw@109
   174
			razor_importer_add_property(ctx->importer, n,
danw@109
   175
						    yum_to_razor_flags (flags),
danw@109
   176
						    buffer,
danw@109
   177
						    RAZOR_PROPERTY_CONFLICTS);
danw@109
   178
			break;
danw@109
   179
		}
danw@109
   180
	}
danw@109
   181
}
danw@109
   182
danw@109
   183
static void
danw@109
   184
yum_primary_end_element (void *data, const char *name)
danw@109
   185
{
danw@109
   186
	struct yum_context *ctx = data;
danw@109
   187
danw@109
   188
	switch (ctx->state) {
danw@109
   189
	case YUM_STATE_PACKAGE_NAME:
krh@192
   190
	case YUM_STATE_PACKAGE_ARCH:
danw@109
   191
	case YUM_STATE_CHECKSUM:
danw@109
   192
	case YUM_STATE_FILE:
danw@109
   193
		ctx->state = YUM_STATE_BEGIN;
danw@109
   194
		break;
danw@109
   195
	}
danw@109
   196
danw@109
   197
	if (strcmp(name, "package") == 0) {
danw@109
   198
		XML_StopParser(ctx->current_parser, XML_TRUE);
danw@109
   199
		ctx->current_parser = ctx->filelists_parser;
danw@109
   200
	}
danw@109
   201
}
danw@109
   202
danw@109
   203
static void
danw@109
   204
yum_character_data (void *data, const XML_Char *s, int len)
danw@109
   205
{
danw@109
   206
	struct yum_context *ctx = data;
danw@109
   207
danw@109
   208
	switch (ctx->state) {
danw@109
   209
	case YUM_STATE_PACKAGE_NAME:
krh@192
   210
	case YUM_STATE_PACKAGE_ARCH:
danw@109
   211
	case YUM_STATE_CHECKSUM:
danw@109
   212
	case YUM_STATE_FILE:
danw@109
   213
		memcpy(ctx->p, s, len);
danw@109
   214
		ctx->p += len;
danw@109
   215
		*ctx->p = '\0';
danw@109
   216
		break;
danw@109
   217
	}
danw@109
   218
}
danw@109
   219
danw@109
   220
static void
danw@109
   221
yum_filelists_start_element(void *data, const char *name, const char **atts)
danw@109
   222
{
danw@109
   223
	struct yum_context *ctx = data;
danw@109
   224
	const char *pkg, *pkgid;
danw@109
   225
	int i;
danw@109
   226
danw@109
   227
	if (strcmp(name, "package") == 0) {
danw@109
   228
		pkg = NULL;
danw@109
   229
		pkgid = NULL;
danw@109
   230
		for (i = 0; atts[i]; i += 2) {
danw@109
   231
			if (strcmp(atts[i], "name") == 0)
danw@109
   232
				pkg = atts[i + 1];
danw@109
   233
			else if (strcmp(atts[i], "pkgid") == 0)
danw@109
   234
				pkgid = atts[i + 1];
danw@109
   235
		}
danw@109
   236
		if (strcmp(pkgid, ctx->pkgid) != 0)
danw@109
   237
			fprintf(stderr, "primary.xml and filelists.xml "
danw@109
   238
				"mismatch for %s: %s vs %s",
danw@109
   239
				pkg, pkgid, ctx->pkgid);
danw@109
   240
	} else if (strcmp(name, "file") == 0) {
danw@109
   241
		ctx->state = YUM_STATE_FILE;
danw@109
   242
		ctx->p = ctx->buffer;
danw@109
   243
	}
danw@109
   244
}
danw@109
   245
danw@109
   246
danw@109
   247
static void
danw@109
   248
yum_filelists_end_element (void *data, const char *name)
danw@109
   249
{
danw@109
   250
	struct yum_context *ctx = data;
danw@109
   251
danw@109
   252
	ctx->state = YUM_STATE_BEGIN;
danw@109
   253
	if (strcmp(name, "package") == 0) {
danw@109
   254
		XML_StopParser(ctx->current_parser, XML_TRUE);
danw@109
   255
		ctx->current_parser = ctx->primary_parser;
danw@109
   256
		razor_importer_finish_package(ctx->importer);
danw@109
   257
	} else if (strcmp(name, "file") == 0)
danw@109
   258
		razor_importer_add_file(ctx->importer, ctx->buffer);
danw@109
   259
danw@109
   260
}
danw@109
   261
danw@109
   262
#define XML_BUFFER_SIZE 4096
danw@109
   263
danw@109
   264
struct razor_set *
danw@109
   265
razor_set_create_from_yum(void)
danw@109
   266
{
danw@109
   267
	struct yum_context ctx;
danw@109
   268
	void *buf;
danw@109
   269
	int len, ret;
danw@109
   270
	gzFile primary, filelists;
danw@109
   271
	XML_ParsingStatus status;
danw@109
   272
danw@109
   273
	ctx.importer = razor_importer_new();	
danw@109
   274
	ctx.state = YUM_STATE_BEGIN;
danw@109
   275
danw@109
   276
	ctx.primary_parser = XML_ParserCreate(NULL);
danw@109
   277
	XML_SetUserData(ctx.primary_parser, &ctx);
danw@109
   278
	XML_SetElementHandler(ctx.primary_parser,
danw@109
   279
			      yum_primary_start_element,
danw@109
   280
			      yum_primary_end_element);
danw@109
   281
	XML_SetCharacterDataHandler(ctx.primary_parser,
danw@109
   282
				    yum_character_data);
danw@109
   283
danw@109
   284
	ctx.filelists_parser = XML_ParserCreate(NULL);
danw@109
   285
	XML_SetUserData(ctx.filelists_parser, &ctx);
danw@109
   286
	XML_SetElementHandler(ctx.filelists_parser,
danw@109
   287
			      yum_filelists_start_element,
danw@109
   288
			      yum_filelists_end_element);
danw@109
   289
	XML_SetCharacterDataHandler(ctx.filelists_parser,
danw@109
   290
				    yum_character_data);
danw@109
   291
danw@109
   292
	primary = gzopen("primary.xml.gz", "rb");
danw@109
   293
	if (primary == NULL)
danw@109
   294
		return NULL;
danw@109
   295
	filelists = gzopen("filelists.xml.gz", "rb");
danw@109
   296
	if (filelists == NULL)
danw@109
   297
		return NULL;
danw@109
   298
danw@109
   299
	ctx.current_parser = ctx.primary_parser;
danw@109
   300
danw@109
   301
	do {
danw@109
   302
		XML_GetParsingStatus(ctx.current_parser, &status);
danw@109
   303
		switch (status.parsing) {
danw@109
   304
		case XML_SUSPENDED:
danw@109
   305
			ret = XML_ResumeParser(ctx.current_parser);
danw@109
   306
			break;
danw@109
   307
		case XML_PARSING:
danw@109
   308
		case XML_INITIALIZED:
danw@109
   309
			buf = XML_GetBuffer(ctx.current_parser,
danw@109
   310
					    XML_BUFFER_SIZE);
danw@109
   311
			if (ctx.current_parser == ctx.primary_parser)
danw@109
   312
				len = gzread(primary, buf, XML_BUFFER_SIZE);
danw@109
   313
			else
danw@109
   314
				len = gzread(filelists, buf, XML_BUFFER_SIZE);
danw@109
   315
			if (len < 0) {
danw@109
   316
				fprintf(stderr,
danw@109
   317
					"couldn't read input: %s\n",
danw@109
   318
					strerror(errno));
danw@109
   319
				return NULL;
danw@109
   320
			}
danw@109
   321
danw@109
   322
			XML_ParseBuffer(ctx.current_parser, len, len == 0);
danw@109
   323
			break;
danw@109
   324
		case XML_FINISHED:
danw@109
   325
			break;
danw@109
   326
		}
danw@109
   327
	} while (status.parsing != XML_FINISHED);
danw@109
   328
danw@109
   329
danw@109
   330
	XML_ParserFree(ctx.primary_parser);
danw@109
   331
	XML_ParserFree(ctx.filelists_parser);
danw@109
   332
danw@109
   333
	gzclose(primary);
danw@109
   334
	gzclose(filelists);
danw@109
   335
danw@109
   336
	return razor_importer_finish(ctx.importer);
danw@109
   337
}