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