yum.c
author Dan Winship <danw@gnome.org>
Fri Feb 29 12:45:08 2008 -0500 (2008-02-29)
changeset 138 49deac048d07
child 143 59a9513fac54
permissions -rw-r--r--
implement file dependencies for installs

removes are trickier because there are no backlinks from the files array
the properties array, so there's currently no way to efficiently determine
what packages are affected by the removal of a particular file
     1 #define _GNU_SOURCE
     2 
     3 #include <string.h>
     4 #include <stdio.h>
     5 #include <sys/stat.h>
     6 #include <sys/mman.h>
     7 #include <unistd.h>
     8 #include <fcntl.h>
     9 #include <errno.h>
    10 
    11 #include <expat.h>
    12 #include <zlib.h>
    13 #include "razor.h"
    14 
    15 /* Import a yum filelist as a razor package set. */
    16 
    17 enum {
    18 	YUM_STATE_BEGIN,
    19 	YUM_STATE_PACKAGE_NAME,
    20 	YUM_STATE_CHECKSUM,
    21 	YUM_STATE_REQUIRES,
    22 	YUM_STATE_PROVIDES,
    23 	YUM_STATE_OBSOLETES,
    24 	YUM_STATE_CONFLICTS,
    25 	YUM_STATE_FILE
    26 };
    27 
    28 struct yum_context {
    29 	XML_Parser primary_parser;
    30 	XML_Parser filelists_parser;
    31 	XML_Parser current_parser;
    32 
    33 	struct razor_importer *importer;
    34 	struct import_property_context *current_property_context;
    35 	char name[256], buffer[512], *p;
    36 	char pkgid[128];
    37 	int state;
    38 };
    39 
    40 static enum razor_version_relation
    41 yum_to_razor_flags (const char *flags)
    42 {
    43 	/* FIXME? */
    44 	if (!flags)
    45 		return RAZOR_VERSION_EQUAL;
    46 
    47 	if (flags[0] == 'L') {
    48 		if (flags[1] == 'T')
    49 			return RAZOR_VERSION_LESS;
    50 		else
    51 			return RAZOR_VERSION_LESS_OR_EQUAL;
    52 	} else if (flags[0] == 'G') {
    53 		if (flags[1] == 'T')
    54 			return RAZOR_VERSION_GREATER;
    55 		else
    56 			return RAZOR_VERSION_GREATER_OR_EQUAL;
    57 	} else
    58 		return RAZOR_VERSION_EQUAL;
    59 }
    60 
    61 static void
    62 yum_primary_start_element(void *data, const char *name, const char **atts)
    63 {
    64 	struct yum_context *ctx = data;
    65 	const char *n, *version, *release, *flags;
    66 	char buffer[128];
    67 	int i;
    68 
    69 	if (strcmp(name, "name") == 0) {
    70 		ctx->state = YUM_STATE_PACKAGE_NAME;
    71 		ctx->p = ctx->name;
    72 	} else if (strcmp(name, "version") == 0) {
    73 		version = NULL;
    74 		release = NULL;
    75 		for (i = 0; atts[i]; i += 2) {
    76 			if (strcmp(atts[i], "ver") == 0)
    77 				version = atts[i + 1];
    78 			else if (strcmp(atts[i], "rel") == 0)
    79 				release = atts[i + 1];
    80 		}
    81 		if (version == NULL || release == NULL) {
    82 			fprintf(stderr, "invalid version tag, "
    83 				"missing version or  release attribute\n");
    84 			return;
    85 		}
    86 
    87 		snprintf(buffer, sizeof buffer, "%s-%s", version, release);
    88 		razor_importer_begin_package(ctx->importer, ctx->name, buffer);
    89 	} else if (strcmp(name, "checksum") == 0) {
    90 		ctx->p = ctx->pkgid;
    91 		ctx->state = YUM_STATE_CHECKSUM;
    92 	} else if (strcmp(name, "rpm:requires") == 0) {
    93 		ctx->state = YUM_STATE_REQUIRES;
    94 	} else if (strcmp(name, "rpm:provides") == 0) {
    95 		ctx->state = YUM_STATE_PROVIDES;
    96 	} else if (strcmp(name, "rpm:obsoletes") == 0) {
    97 		ctx->state = YUM_STATE_OBSOLETES;
    98 	} else if (strcmp(name, "rpm:conflicts") == 0) {
    99 		ctx->state = YUM_STATE_CONFLICTS;
   100 	} else if (strcmp(name, "rpm:entry") == 0 &&
   101 		   ctx->state != YUM_STATE_BEGIN) {
   102 		n = NULL;
   103 		version = NULL;
   104 		release = NULL;
   105 		flags = NULL;
   106 		for (i = 0; atts[i]; i += 2) {
   107 			if (strcmp(atts[i], "name") == 0)
   108 				n = atts[i + 1];
   109 			else if (strcmp(atts[i], "ver") == 0)
   110 				version = atts[i + 1];
   111 			else if (strcmp(atts[i], "rel") == 0)
   112 				release = atts[i + 1];
   113 			else if (strcmp(atts[i], "flags") == 0)
   114 				flags = atts[i + 1];
   115 		}
   116 
   117 		if (n == NULL) {
   118 			fprintf(stderr, "invalid rpm:entry, "
   119 				"missing name or version attributes\n");
   120 			return;
   121 		}
   122 
   123 		if (version && release)
   124 			snprintf(buffer, sizeof buffer,
   125 				 "%s-%s", version, release);
   126 		else if (version)
   127 			strcpy(buffer, version);
   128 		else
   129 			buffer[0] = '\0';
   130 			
   131 		switch (ctx->state) {
   132 		case YUM_STATE_REQUIRES:
   133 			razor_importer_add_property(ctx->importer, n,
   134 						    yum_to_razor_flags (flags),
   135 						    buffer,
   136 						    RAZOR_PROPERTY_REQUIRES);
   137 			break;
   138 		case YUM_STATE_PROVIDES:
   139 			razor_importer_add_property(ctx->importer, n,
   140 						    yum_to_razor_flags (flags),
   141 						    buffer,
   142 						    RAZOR_PROPERTY_PROVIDES);
   143 			break;
   144 		case YUM_STATE_OBSOLETES:
   145 			razor_importer_add_property(ctx->importer, n,
   146 						    yum_to_razor_flags (flags),
   147 						    buffer,
   148 						    RAZOR_PROPERTY_OBSOLETES);
   149 			break;
   150 		case YUM_STATE_CONFLICTS:
   151 			razor_importer_add_property(ctx->importer, n,
   152 						    yum_to_razor_flags (flags),
   153 						    buffer,
   154 						    RAZOR_PROPERTY_CONFLICTS);
   155 			break;
   156 		}
   157 	}
   158 }
   159 
   160 static void
   161 yum_primary_end_element (void *data, const char *name)
   162 {
   163 	struct yum_context *ctx = data;
   164 
   165 	switch (ctx->state) {
   166 	case YUM_STATE_PACKAGE_NAME:
   167 	case YUM_STATE_CHECKSUM:
   168 	case YUM_STATE_FILE:
   169 		ctx->state = YUM_STATE_BEGIN;
   170 		break;
   171 	}
   172 
   173 	if (strcmp(name, "package") == 0) {
   174 		XML_StopParser(ctx->current_parser, XML_TRUE);
   175 		ctx->current_parser = ctx->filelists_parser;
   176 	}
   177 }
   178 
   179 static void
   180 yum_character_data (void *data, const XML_Char *s, int len)
   181 {
   182 	struct yum_context *ctx = data;
   183 
   184 	switch (ctx->state) {
   185 	case YUM_STATE_PACKAGE_NAME:
   186 	case YUM_STATE_CHECKSUM:
   187 	case YUM_STATE_FILE:
   188 		memcpy(ctx->p, s, len);
   189 		ctx->p += len;
   190 		*ctx->p = '\0';
   191 		break;
   192 	}
   193 }
   194 
   195 static void
   196 yum_filelists_start_element(void *data, const char *name, const char **atts)
   197 {
   198 	struct yum_context *ctx = data;
   199 	const char *pkg, *pkgid;
   200 	int i;
   201 
   202 	if (strcmp(name, "package") == 0) {
   203 		pkg = NULL;
   204 		pkgid = NULL;
   205 		for (i = 0; atts[i]; i += 2) {
   206 			if (strcmp(atts[i], "name") == 0)
   207 				pkg = atts[i + 1];
   208 			else if (strcmp(atts[i], "pkgid") == 0)
   209 				pkgid = atts[i + 1];
   210 		}
   211 		if (strcmp(pkgid, ctx->pkgid) != 0)
   212 			fprintf(stderr, "primary.xml and filelists.xml "
   213 				"mismatch for %s: %s vs %s",
   214 				pkg, pkgid, ctx->pkgid);
   215 	} else if (strcmp(name, "file") == 0) {
   216 		ctx->state = YUM_STATE_FILE;
   217 		ctx->p = ctx->buffer;
   218 	}
   219 }
   220 
   221 
   222 static void
   223 yum_filelists_end_element (void *data, const char *name)
   224 {
   225 	struct yum_context *ctx = data;
   226 
   227 	ctx->state = YUM_STATE_BEGIN;
   228 	if (strcmp(name, "package") == 0) {
   229 		XML_StopParser(ctx->current_parser, XML_TRUE);
   230 		ctx->current_parser = ctx->primary_parser;
   231 		razor_importer_finish_package(ctx->importer);
   232 	} else if (strcmp(name, "file") == 0)
   233 		razor_importer_add_file(ctx->importer, ctx->buffer);
   234 
   235 }
   236 
   237 #define XML_BUFFER_SIZE 4096
   238 
   239 struct razor_set *
   240 razor_set_create_from_yum(void)
   241 {
   242 	struct yum_context ctx;
   243 	void *buf;
   244 	int len, ret;
   245 	gzFile primary, filelists;
   246 	XML_ParsingStatus status;
   247 
   248 	ctx.importer = razor_importer_new();	
   249 	ctx.state = YUM_STATE_BEGIN;
   250 
   251 	ctx.primary_parser = XML_ParserCreate(NULL);
   252 	XML_SetUserData(ctx.primary_parser, &ctx);
   253 	XML_SetElementHandler(ctx.primary_parser,
   254 			      yum_primary_start_element,
   255 			      yum_primary_end_element);
   256 	XML_SetCharacterDataHandler(ctx.primary_parser,
   257 				    yum_character_data);
   258 
   259 	ctx.filelists_parser = XML_ParserCreate(NULL);
   260 	XML_SetUserData(ctx.filelists_parser, &ctx);
   261 	XML_SetElementHandler(ctx.filelists_parser,
   262 			      yum_filelists_start_element,
   263 			      yum_filelists_end_element);
   264 	XML_SetCharacterDataHandler(ctx.filelists_parser,
   265 				    yum_character_data);
   266 
   267 	primary = gzopen("primary.xml.gz", "rb");
   268 	if (primary == NULL)
   269 		return NULL;
   270 	filelists = gzopen("filelists.xml.gz", "rb");
   271 	if (filelists == NULL)
   272 		return NULL;
   273 
   274 	ctx.current_parser = ctx.primary_parser;
   275 
   276 	do {
   277 		XML_GetParsingStatus(ctx.current_parser, &status);
   278 		switch (status.parsing) {
   279 		case XML_SUSPENDED:
   280 			ret = XML_ResumeParser(ctx.current_parser);
   281 			break;
   282 		case XML_PARSING:
   283 		case XML_INITIALIZED:
   284 			buf = XML_GetBuffer(ctx.current_parser,
   285 					    XML_BUFFER_SIZE);
   286 			if (ctx.current_parser == ctx.primary_parser)
   287 				len = gzread(primary, buf, XML_BUFFER_SIZE);
   288 			else
   289 				len = gzread(filelists, buf, XML_BUFFER_SIZE);
   290 			if (len < 0) {
   291 				fprintf(stderr,
   292 					"couldn't read input: %s\n",
   293 					strerror(errno));
   294 				return NULL;
   295 			}
   296 
   297 			XML_ParseBuffer(ctx.current_parser, len, len == 0);
   298 			break;
   299 		case XML_FINISHED:
   300 			break;
   301 		}
   302 	} while (status.parsing != XML_FINISHED);
   303 
   304 
   305 	XML_ParserFree(ctx.primary_parser);
   306 	XML_ParserFree(ctx.filelists_parser);
   307 
   308 	gzclose(primary);
   309 	gzclose(filelists);
   310 
   311 	return razor_importer_finish(ctx.importer);
   312 }