src/import-yum.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Feb 09 20:45:27 2012 +0000 (2012-02-09)
changeset 418 33b825d3128d
parent 394 afe520f454bd
child 438 fab0b8a61dcb
permissions -rw-r--r--
Add transaction barriers
These allow packages to be installed and removed which have scripts
that depend on each other when atomic transactions are involved.
Note that yum supports pre, but not other requires flags. post will
need similar support to the post scripts themselves pulling in the
requires flags from the rpms. Likewise preun and postun will need
similar handling to those scrips since the requires flags will need
to be stored in the razor database.
     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 <stdint.h>
    25 #include <sys/stat.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_SUMMARY,
    41 	YUM_STATE_DESCRIPTION,
    42 	YUM_STATE_URL,
    43 	YUM_STATE_LICENSE,
    44 	YUM_STATE_CHECKSUM,
    45 	YUM_STATE_REQUIRES,
    46 	YUM_STATE_PROVIDES,
    47 	YUM_STATE_OBSOLETES,
    48 	YUM_STATE_CONFLICTS,
    49 	YUM_STATE_FILE
    50 };
    51 
    52 struct yum_context {
    53 	XML_Parser primary_parser;
    54 	XML_Parser filelists_parser;
    55 	XML_Parser current_parser;
    56 
    57 	struct razor_importer *importer;
    58 	struct import_property_context *current_property_context;
    59 	char name[256], arch[64], summary[512], description[4096];
    60 	char url[256], license[64], buffer[512], *p;
    61 	char pkgid[128];
    62 	uint32_t property_type;
    63 	int state;
    64 
    65 	int total, current;
    66 };
    67 
    68 static uint32_t
    69 yum_to_razor_relation (const char *flags)
    70 {
    71 	if (flags[0] == 'L') {
    72 		if (flags[1] == 'T')
    73 			return RAZOR_PROPERTY_LESS;
    74 		else
    75 			return RAZOR_PROPERTY_LESS | RAZOR_PROPERTY_EQUAL;
    76 	} else if (flags[0] == 'G') {
    77 		if (flags[1] == 'T')
    78 			return RAZOR_PROPERTY_GREATER;
    79 		else
    80 			return RAZOR_PROPERTY_GREATER | RAZOR_PROPERTY_EQUAL;
    81 	} else
    82 		return RAZOR_PROPERTY_EQUAL;
    83 }
    84 
    85 static void
    86 yum_primary_start_element(void *data, const char *name, const char **atts)
    87 {
    88 	struct yum_context *ctx = data;
    89 	const char *n, *epoch, *version, *release;
    90 	char buffer[128];
    91 	uint32_t pre, relation, flags;
    92 	int i;
    93 
    94 	if (strcmp(name, "metadata") == 0) {
    95 		for (i = 0; atts[i]; i += 2) {
    96 			if (strcmp(atts[i], "packages") == 0)
    97 				ctx->total = atoi(atts[i + 1]);
    98 		}
    99 	} else if (strcmp(name, "name") == 0) {
   100 		ctx->state = YUM_STATE_PACKAGE_NAME;
   101 		ctx->p = ctx->name;
   102 	} else if (strcmp(name, "arch") == 0) {
   103 		ctx->state = YUM_STATE_PACKAGE_ARCH;
   104 		ctx->p = ctx->arch;
   105 	} else if (strcmp(name, "version") == 0) {
   106 		epoch = NULL;
   107 		version = NULL;
   108 		release = NULL;
   109 		for (i = 0; atts[i]; i += 2) {
   110 			if (strcmp(atts[i], "epoch") == 0)
   111 				epoch = atts[i + 1];
   112 			else if (strcmp(atts[i], "ver") == 0)
   113 				version = atts[i + 1];
   114 			else if (strcmp(atts[i], "rel") == 0)
   115 				release = atts[i + 1];
   116 		}
   117 		if (version == NULL || release == NULL) {
   118 			fprintf(stderr, "invalid version tag, "
   119 				"missing version or  release attribute\n");
   120 			return;
   121 		}
   122 
   123 		razor_build_evr(buffer, sizeof buffer, epoch, version, release);
   124 		razor_importer_begin_package(ctx->importer,
   125 					     ctx->name, buffer, ctx->arch);
   126 	} else if (strcmp(name, "summary") == 0) {
   127 		ctx->p = ctx->summary;
   128 		ctx->state = YUM_STATE_SUMMARY;
   129 	} else if (strcmp(name, "description") == 0) {
   130 		ctx->p = ctx->description;
   131 		ctx->state = YUM_STATE_DESCRIPTION;
   132 	} else if (strcmp(name, "url") == 0) {
   133 		ctx->p = ctx->url;
   134 		ctx->state = YUM_STATE_URL;
   135 	} else if (strcmp(name, "checksum") == 0) {
   136 		ctx->p = ctx->pkgid;
   137 		ctx->state = YUM_STATE_CHECKSUM;
   138 	} else if (strcmp(name, "rpm:license") == 0) {
   139 		ctx->p = ctx->license;
   140 		ctx->state = YUM_STATE_LICENSE;
   141 	} else if (strcmp(name, "rpm:requires") == 0) {
   142 		ctx->state = YUM_STATE_REQUIRES;
   143 		ctx->property_type = RAZOR_PROPERTY_REQUIRES;
   144 	} else if (strcmp(name, "rpm:provides") == 0) {
   145 		ctx->state = YUM_STATE_PROVIDES;
   146 		ctx->property_type = RAZOR_PROPERTY_PROVIDES;
   147 	} else if (strcmp(name, "rpm:obsoletes") == 0) {
   148 		ctx->state = YUM_STATE_OBSOLETES;
   149 		ctx->property_type = RAZOR_PROPERTY_OBSOLETES;
   150 	} else if (strcmp(name, "rpm:conflicts") == 0) {
   151 		ctx->state = YUM_STATE_CONFLICTS;
   152 		ctx->property_type = RAZOR_PROPERTY_CONFLICTS;
   153 	} else if (strcmp(name, "rpm:entry") == 0 &&
   154 		   ctx->state != YUM_STATE_BEGIN) {
   155 		n = NULL;
   156 		epoch = NULL;
   157 		version = NULL;
   158 		release = NULL;
   159 		relation = RAZOR_PROPERTY_EQUAL;
   160 		pre = 0;
   161 		for (i = 0; atts[i]; i += 2) {
   162 			if (strcmp(atts[i], "name") == 0)
   163 				n = atts[i + 1];
   164 			else if (strcmp(atts[i], "epoch") == 0)
   165 				epoch = atts[i + 1];
   166 			else if (strcmp(atts[i], "ver") == 0)
   167 				version = atts[i + 1];
   168 			else if (strcmp(atts[i], "rel") == 0)
   169 				release = atts[i + 1];
   170 			else if (strcmp(atts[i], "flags") == 0)
   171 				relation = yum_to_razor_relation(atts[i + 1]);
   172 			else if (strcmp(atts[i], "pre") == 0)
   173 				pre = RAZOR_PROPERTY_PRE;
   174 		}
   175 
   176 		if (n == NULL) {
   177 			fprintf(stderr, "invalid rpm:entry, "
   178 				"missing name or version attributes\n");
   179 			return;
   180 		}
   181 
   182 		razor_build_evr(buffer, sizeof buffer, epoch, version, release);
   183 		flags = ctx->property_type | relation | pre;
   184 		razor_importer_add_property(ctx->importer, n, flags, buffer);
   185 	}
   186 }
   187 
   188 static void
   189 yum_primary_end_element (void *data, const char *name)
   190 {
   191 	struct yum_context *ctx = data;
   192 
   193 	switch (ctx->state) {
   194 	case YUM_STATE_PACKAGE_NAME:
   195 	case YUM_STATE_PACKAGE_ARCH:
   196 	case YUM_STATE_SUMMARY:
   197 	case YUM_STATE_DESCRIPTION:
   198 	case YUM_STATE_URL:
   199 	case YUM_STATE_LICENSE:
   200 	case YUM_STATE_CHECKSUM:
   201 	case YUM_STATE_FILE:
   202 		ctx->state = YUM_STATE_BEGIN;
   203 		break;
   204 	}
   205 
   206 	if (strcmp(name, "package") == 0) {
   207 		razor_importer_add_details(ctx->importer, ctx->summary,
   208 					   ctx->description, ctx->url,
   209 					   ctx->license);
   210 
   211 		XML_StopParser(ctx->current_parser, XML_TRUE);
   212 		ctx->current_parser = ctx->filelists_parser;
   213 
   214 		printf("\rimporting %d/%d", ++ctx->current, ctx->total);
   215 		fflush(stdout);
   216 	}
   217 }
   218 
   219 static void
   220 yum_character_data (void *data, const XML_Char *s, int len)
   221 {
   222 	struct yum_context *ctx = data;
   223 
   224 	switch (ctx->state) {
   225 	case YUM_STATE_PACKAGE_NAME:
   226 	case YUM_STATE_PACKAGE_ARCH:
   227 	case YUM_STATE_SUMMARY:
   228 	case YUM_STATE_DESCRIPTION:
   229 	case YUM_STATE_URL:
   230 	case YUM_STATE_LICENSE:
   231 	case YUM_STATE_CHECKSUM:
   232 	case YUM_STATE_FILE:
   233 		memcpy(ctx->p, s, len);
   234 		ctx->p += len;
   235 		*ctx->p = '\0';
   236 		break;
   237 	}
   238 }
   239 
   240 static void
   241 yum_filelists_start_element(void *data, const char *name, const char **atts)
   242 {
   243 	struct yum_context *ctx = data;
   244 	const char *pkg, *pkgid;
   245 	int i;
   246 
   247 	if (strcmp(name, "package") == 0) {
   248 		pkg = NULL;
   249 		pkgid = NULL;
   250 		for (i = 0; atts[i]; i += 2) {
   251 			if (strcmp(atts[i], "name") == 0)
   252 				pkg = atts[i + 1];
   253 			else if (strcmp(atts[i], "pkgid") == 0)
   254 				pkgid = atts[i + 1];
   255 		}
   256 		if (strcmp(pkgid, ctx->pkgid) != 0)
   257 			fprintf(stderr, "primary.xml and filelists.xml "
   258 				"mismatch for %s: %s vs %s",
   259 				pkg, pkgid, ctx->pkgid);
   260 	} else if (strcmp(name, "file") == 0) {
   261 		ctx->state = YUM_STATE_FILE;
   262 		ctx->p = ctx->buffer;
   263 	}
   264 }
   265 
   266 
   267 static void
   268 yum_filelists_end_element (void *data, const char *name)
   269 {
   270 	struct yum_context *ctx = data;
   271 
   272 	ctx->state = YUM_STATE_BEGIN;
   273 	if (strcmp(name, "package") == 0) {
   274 		XML_StopParser(ctx->current_parser, XML_TRUE);
   275 		ctx->current_parser = ctx->primary_parser;
   276 		razor_importer_finish_package(ctx->importer);
   277 	} else if (strcmp(name, "file") == 0)
   278 		razor_importer_add_file(ctx->importer, ctx->buffer);
   279 
   280 }
   281 
   282 #define XML_BUFFER_SIZE 4096
   283 
   284 struct razor_set *
   285 razor_set_create_from_yum(void)
   286 {
   287 	struct yum_context ctx={0};
   288 	void *buf;
   289 	int len, ret;
   290 	gzFile primary, filelists;
   291 	XML_ParsingStatus status;
   292 
   293 	ctx.importer = razor_importer_create();
   294 	ctx.state = YUM_STATE_BEGIN;
   295 
   296 	ctx.primary_parser = XML_ParserCreate(NULL);
   297 	XML_SetUserData(ctx.primary_parser, &ctx);
   298 	XML_SetElementHandler(ctx.primary_parser,
   299 			      yum_primary_start_element,
   300 			      yum_primary_end_element);
   301 	XML_SetCharacterDataHandler(ctx.primary_parser,
   302 				    yum_character_data);
   303 
   304 	ctx.filelists_parser = XML_ParserCreate(NULL);
   305 	XML_SetUserData(ctx.filelists_parser, &ctx);
   306 	XML_SetElementHandler(ctx.filelists_parser,
   307 			      yum_filelists_start_element,
   308 			      yum_filelists_end_element);
   309 	XML_SetCharacterDataHandler(ctx.filelists_parser,
   310 				    yum_character_data);
   311 
   312 	primary = gzopen("primary.xml.gz", "rb");
   313 	if (primary == NULL)
   314 		return NULL;
   315 	filelists = gzopen("filelists.xml.gz", "rb");
   316 	if (filelists == NULL)
   317 		return NULL;
   318 
   319 	ctx.current_parser = ctx.primary_parser;
   320 
   321 	ctx.current = 0;
   322 
   323 	do {
   324 		XML_GetParsingStatus(ctx.current_parser, &status);
   325 		switch (status.parsing) {
   326 		case XML_SUSPENDED:
   327 			ret = XML_ResumeParser(ctx.current_parser);
   328 			break;
   329 		case XML_PARSING:
   330 		case XML_INITIALIZED:
   331 			buf = XML_GetBuffer(ctx.current_parser,
   332 					    XML_BUFFER_SIZE);
   333 			if (ctx.current_parser == ctx.primary_parser)
   334 				len = gzread(primary, buf, XML_BUFFER_SIZE);
   335 			else
   336 				len = gzread(filelists, buf, XML_BUFFER_SIZE);
   337 			if (len < 0) {
   338 				fprintf(stderr,
   339 					"couldn't read input: %s\n",
   340 					strerror(errno));
   341 				return NULL;
   342 			}
   343 
   344 			XML_ParseBuffer(ctx.current_parser, len, len == 0);
   345 			break;
   346 		case XML_FINISHED:
   347 			break;
   348 		}
   349 	} while (status.parsing != XML_FINISHED);
   350 
   351 
   352 	XML_ParserFree(ctx.primary_parser);
   353 	XML_ParserFree(ctx.filelists_parser);
   354 
   355 	gzclose(primary);
   356 	gzclose(filelists);
   357 
   358 	printf ("\nsaving\n");
   359 	return razor_importer_finish(ctx.importer);
   360 }