plover/import-yum.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Jul 16 19:54:45 2020 +0100 (2020-07-16)
changeset 99 0121592e2512
parent 71 bd272d15bea4
child 109 2947214c450e
permissions -rw-r--r--
Fix most compiler warnings
     1 /*
     2  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
     3  * Copyright (C) 2008  Red Hat, Inc
     4  * Copyright (C) 2009, 2011, 2014, 2016  J. Ali Harlow <ali@juiblex.co.uk>
     5  *
     6  * This program is free software; you can redistribute it and/or modify
     7  * it under the terms of the GNU General Public License as published by
     8  * the Free Software Foundation; either version 2 of the License, or
     9  * (at your option) any later version.
    10  *
    11  * This program is distributed in the hope that it will be useful,
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  * GNU General Public License for more details.
    15  *
    16  * You should have received a copy of the GNU General Public License along
    17  * with this program; if not, write to the Free Software Foundation, Inc.,
    18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    19  */
    20 
    21 #define _GNU_SOURCE
    22 
    23 #include <string.h>
    24 #include <stdio.h>
    25 #include <stdint.h>
    26 #include <sys/stat.h>
    27 #include <unistd.h>
    28 #include <fcntl.h>
    29 #include <errno.h>
    30 
    31 #include <glib.h>
    32 #include <expat.h>
    33 #include <zlib.h>
    34 #include <razor.h>
    35 #include "plover/plover.h"
    36 #include "plover/uri-handler.h"
    37 #include "plover/inputstream.h"
    38 
    39 /* Import a yum filelist as a razor package set. */
    40 
    41 enum {
    42 	YUM_STATE_BEGIN,
    43 	YUM_STATE_PACKAGE_NAME,
    44 	YUM_STATE_PACKAGE_ARCH,
    45 	YUM_STATE_SUMMARY,
    46 	YUM_STATE_DESCRIPTION,
    47 	YUM_STATE_URL,
    48 	YUM_STATE_LICENSE,
    49 	YUM_STATE_CHECKSUM,
    50 	YUM_STATE_REQUIRES,
    51 	YUM_STATE_PROVIDES,
    52 	YUM_STATE_OBSOLETES,
    53 	YUM_STATE_CONFLICTS,
    54 	YUM_STATE_SKIPPING_PACKAGE,
    55 	YUM_STATE_FILE
    56 };
    57 
    58 struct yum_context {
    59 	XML_Parser primary_parser;
    60 	XML_Parser filelists_parser;
    61 	XML_Parser current_parser;
    62 
    63 	struct razor_importer *importer;
    64 	struct import_property_context *current_property_context;
    65 	const char *base_uri;
    66 	GTree *uris;
    67 	char name[256], arch[64], summary[512], description[4096];
    68 	char url[256], license[64], buffer[512], *p;
    69 	char pkgid[128], evr[128];
    70 	uint32_t property_type;
    71 	int state;
    72 
    73 	int total, current;
    74 };
    75 
    76 static uint32_t
    77 yum_to_razor_relation (const char *flags)
    78 {
    79 	if (flags[0] == 'L') {
    80 		if (flags[1] == 'T')
    81 			return RAZOR_PROPERTY_LESS;
    82 		else
    83 			return RAZOR_PROPERTY_LESS | RAZOR_PROPERTY_EQUAL;
    84 	} else if (flags[0] == 'G') {
    85 		if (flags[1] == 'T')
    86 			return RAZOR_PROPERTY_GREATER;
    87 		else
    88 			return RAZOR_PROPERTY_GREATER | RAZOR_PROPERTY_EQUAL;
    89 	} else
    90 		return RAZOR_PROPERTY_EQUAL;
    91 }
    92 
    93 static void
    94 yum_primary_start_element(void *data, const char *name, const char **atts)
    95 {
    96 	struct yum_context *ctx = data;
    97 	const char *n, *epoch, *version, *release;
    98 	char buffer[128];
    99 	char *s;
   100 	gchar *nevra;
   101 	uint32_t pre, relation, flags;
   102 	int i;
   103 
   104 	if (ctx->state == YUM_STATE_SKIPPING_PACKAGE)
   105 		return;
   106 
   107 	if (strcmp(name, "metadata") == 0) {
   108 		for (i = 0; atts[i]; i += 2) {
   109 			if (strcmp(atts[i], "packages") == 0)
   110 				ctx->total = atoi(atts[i + 1]);
   111 		}
   112 	} else if (strcmp(name, "package") == 0) {
   113 		*ctx->name=*ctx->arch=*ctx->summary=*ctx->description='\0';
   114 		*ctx->url=*ctx->license='\0';
   115 	} else if (strcmp(name, "name") == 0) {
   116 		ctx->state = YUM_STATE_PACKAGE_NAME;
   117 		ctx->p = ctx->name;
   118 	} else if (strcmp(name, "arch") == 0) {
   119 		ctx->state = YUM_STATE_PACKAGE_ARCH;
   120 		ctx->p = ctx->arch;
   121 	} else if (strcmp(name, "version") == 0) {
   122 		epoch = NULL;
   123 		version = NULL;
   124 		release = NULL;
   125 		for (i = 0; atts[i]; i += 2) {
   126 			if (strcmp(atts[i], "epoch") == 0)
   127 				epoch = atts[i + 1];
   128 			else if (strcmp(atts[i], "ver") == 0)
   129 				version = atts[i + 1];
   130 			else if (strcmp(atts[i], "rel") == 0)
   131 				release = atts[i + 1];
   132 		}
   133 		if (version == NULL || release == NULL) {
   134 			fprintf(stderr, "invalid version tag, "
   135 				"missing version or  release attribute\n");
   136 			return;
   137 		}
   138 
   139 		razor_build_evr(ctx->evr, sizeof ctx->evr, epoch, version,
   140 				release);
   141 		if (!strcmp(ctx->arch, "noarch") ||
   142 		    !strcmp(ctx->arch, razor_system_arch())) {
   143 			razor_importer_begin_package(ctx->importer, ctx->name,
   144 						     ctx->evr, ctx->arch);
   145 		} else
   146 			ctx->state = YUM_STATE_SKIPPING_PACKAGE;
   147 	} else if (strcmp(name, "summary") == 0) {
   148 		ctx->p = ctx->summary;
   149 		ctx->state = YUM_STATE_SUMMARY;
   150 	} else if (strcmp(name, "description") == 0) {
   151 		ctx->p = ctx->description;
   152 		ctx->state = YUM_STATE_DESCRIPTION;
   153 	} else if (strcmp(name, "url") == 0) {
   154 		ctx->p = ctx->url;
   155 		ctx->state = YUM_STATE_URL;
   156 	} else if (strcmp(name, "checksum") == 0) {
   157 		ctx->p = ctx->pkgid;
   158 		ctx->state = YUM_STATE_CHECKSUM;
   159 	} else if (strcmp(name, "location") == 0) {
   160 		if (ctx->state != YUM_STATE_SKIPPING_PACKAGE) {
   161 		    for (i = 0; atts[i]; i += 2)
   162 			if (strcmp(atts[i], "href") == 0) {
   163 			    nevra=g_strconcat(ctx->name,"-",ctx->evr,".",
   164 			      ctx->arch,NULL);
   165 			    s=razor_path_relative_to_uri(ctx->base_uri,
   166 			      atts[i + 1],NULL);
   167 			    g_tree_insert(ctx->uris,nevra,g_strdup(s));
   168 			    free(s);
   169 			    break;
   170 			}
   171 		}
   172 	} else if (strcmp(name, "rpm:license") == 0) {
   173 		ctx->p = ctx->license;
   174 		ctx->state = YUM_STATE_LICENSE;
   175 	} else if (strcmp(name, "rpm:requires") == 0) {
   176 		ctx->state = YUM_STATE_REQUIRES;
   177 		ctx->property_type = RAZOR_PROPERTY_REQUIRES;
   178 	} else if (strcmp(name, "rpm:provides") == 0) {
   179 		ctx->state = YUM_STATE_PROVIDES;
   180 		ctx->property_type = RAZOR_PROPERTY_PROVIDES;
   181 	} else if (strcmp(name, "rpm:obsoletes") == 0) {
   182 		ctx->state = YUM_STATE_OBSOLETES;
   183 		ctx->property_type = RAZOR_PROPERTY_OBSOLETES;
   184 	} else if (strcmp(name, "rpm:conflicts") == 0) {
   185 		ctx->state = YUM_STATE_CONFLICTS;
   186 		ctx->property_type = RAZOR_PROPERTY_CONFLICTS;
   187 	} else if (strcmp(name, "rpm:entry") == 0 &&
   188 		   ctx->state != YUM_STATE_BEGIN) {
   189 		n = NULL;
   190 		epoch = NULL;
   191 		version = NULL;
   192 		release = NULL;
   193 		relation = RAZOR_PROPERTY_EQUAL;
   194 		pre = 0;
   195 		for (i = 0; atts[i]; i += 2) {
   196 			if (strcmp(atts[i], "name") == 0)
   197 				n = atts[i + 1];
   198 			else if (strcmp(atts[i], "epoch") == 0)
   199 				epoch = atts[i + 1];
   200 			else if (strcmp(atts[i], "ver") == 0)
   201 				version = atts[i + 1];
   202 			else if (strcmp(atts[i], "rel") == 0)
   203 				release = atts[i + 1];
   204 			else if (strcmp(atts[i], "flags") == 0)
   205 				relation = yum_to_razor_relation(atts[i + 1]);
   206 			else if (strcmp(atts[i], "pre") == 0)
   207 				pre = RAZOR_PROPERTY_PRE;
   208 		}
   209 
   210 		if (n == NULL) {
   211 			fprintf(stderr, "invalid rpm:entry, "
   212 				"missing name or version attributes\n");
   213 			return;
   214 		}
   215 
   216 		razor_build_evr(buffer, sizeof buffer, epoch, version, release);
   217 		flags = ctx->property_type | relation | pre;
   218 		razor_importer_add_property(ctx->importer, n, flags, buffer);
   219 	}
   220 }
   221 
   222 static void
   223 yum_primary_end_element (void *data, const char *name)
   224 {
   225 	struct yum_context *ctx = data;
   226 
   227 	switch (ctx->state) {
   228 	case YUM_STATE_PACKAGE_NAME:
   229 	case YUM_STATE_PACKAGE_ARCH:
   230 	case YUM_STATE_SUMMARY:
   231 	case YUM_STATE_DESCRIPTION:
   232 	case YUM_STATE_URL:
   233 	case YUM_STATE_LICENSE:
   234 	case YUM_STATE_CHECKSUM:
   235 	case YUM_STATE_FILE:
   236 		ctx->state = YUM_STATE_BEGIN;
   237 		break;
   238 	}
   239 
   240 	if (strcmp(name, "package") == 0) {
   241 		if (ctx->state != YUM_STATE_SKIPPING_PACKAGE)
   242 			razor_importer_add_details(ctx->importer, ctx->summary,
   243 						   ctx->description, ctx->url,
   244 						   ctx->license);
   245 
   246 		XML_StopParser(ctx->current_parser, XML_TRUE);
   247 		ctx->current_parser = ctx->filelists_parser;
   248 	}
   249 }
   250 
   251 static void
   252 yum_character_data (void *data, const XML_Char *s, int len)
   253 {
   254 	struct yum_context *ctx = data;
   255 
   256 	switch (ctx->state) {
   257 	case YUM_STATE_PACKAGE_NAME:
   258 	case YUM_STATE_PACKAGE_ARCH:
   259 	case YUM_STATE_SUMMARY:
   260 	case YUM_STATE_DESCRIPTION:
   261 	case YUM_STATE_URL:
   262 	case YUM_STATE_LICENSE:
   263 	case YUM_STATE_CHECKSUM:
   264 	case YUM_STATE_FILE:
   265 		memcpy(ctx->p, s, len);
   266 		ctx->p += len;
   267 		*ctx->p = '\0';
   268 		break;
   269 	}
   270 }
   271 
   272 static void
   273 yum_filelists_start_element(void *data, const char *name, const char **atts)
   274 {
   275 	struct yum_context *ctx = data;
   276 	const char *pkg, *pkgid;
   277 	int i;
   278 
   279 	if (strcmp(name, "package") == 0 &&
   280 	    ctx->state != YUM_STATE_SKIPPING_PACKAGE) {
   281 		pkg = NULL;
   282 		pkgid = NULL;
   283 		for (i = 0; atts[i]; i += 2) {
   284 			if (strcmp(atts[i], "name") == 0)
   285 				pkg = atts[i + 1];
   286 			else if (strcmp(atts[i], "pkgid") == 0)
   287 				pkgid = atts[i + 1];
   288 		}
   289 		if (strcmp(pkgid, ctx->pkgid) != 0)
   290 			fprintf(stderr, "primary.xml and filelists.xml "
   291 				"mismatch for %s: %s vs %s",
   292 				pkg, pkgid, ctx->pkgid);
   293 	} else if (strcmp(name, "file") == 0) {
   294 		if (ctx->state != YUM_STATE_SKIPPING_PACKAGE)
   295 			ctx->state = YUM_STATE_FILE;
   296 		ctx->p = ctx->buffer;
   297 	}
   298 }
   299 
   300 static void
   301 yum_filelists_end_element (void *data, const char *name)
   302 {
   303 	struct yum_context *ctx = data;
   304 
   305 	if (strcmp(name, "package") == 0) {
   306 		XML_StopParser(ctx->current_parser, XML_TRUE);
   307 		ctx->current_parser = ctx->primary_parser;
   308 		if (ctx->state != YUM_STATE_SKIPPING_PACKAGE)
   309 			razor_importer_finish_package(ctx->importer);
   310 		ctx->state = YUM_STATE_BEGIN;
   311 	} else if (strcmp(name, "file") == 0) {
   312 		if (ctx->state != YUM_STATE_SKIPPING_PACKAGE)
   313 			razor_importer_add_file(ctx->importer, ctx->buffer);
   314 	}
   315 	if (ctx->state != YUM_STATE_SKIPPING_PACKAGE)
   316 		ctx->state = YUM_STATE_BEGIN;
   317 }
   318 
   319 static int plover_system_arch_is_x86(void)
   320 {
   321     const char *arch=razor_system_arch();
   322     if (!arch || arch[0]!='i' || arch[1]<'3' || arch[1]>'6')
   323 	return 0;
   324     else
   325 	return !strcmp(arch+2,"86");
   326 }
   327 
   328 #define XML_BUFFER_SIZE 4096
   329 
   330 PloverRepository *plover_repository_new_from_yum_uri(const char *base_uri,
   331   GError **error)
   332 {
   333     struct yum_context ctx;
   334     gchar *s,**rpm_uris;
   335     GPtrArray *uris;
   336     char *uri;
   337     const char *name,*version,*arch;
   338     void *buf;
   339     gssize len;
   340     GInputStream *stream;
   341     GInputStream *primary,*filelists;
   342     GZlibDecompressor *decompressor;
   343     XML_ParsingStatus status;
   344     struct razor_set *razor;
   345     struct razor_package_iterator *iter;
   346     struct razor_package *pkg;
   347     PloverPackageSet *set;
   348     PloverRepository *repository;
   349     g_return_val_if_fail(plover__uri_validate(base_uri),NULL);
   350     plover__uri_handler_init();
   351     ctx.importer=razor_importer_create();
   352     ctx.state=YUM_STATE_BEGIN;
   353     ctx.base_uri=base_uri;
   354     ctx.primary_parser=XML_ParserCreate(NULL);
   355     XML_SetUserData(ctx.primary_parser,&ctx);
   356     XML_SetElementHandler(ctx.primary_parser,yum_primary_start_element,
   357       yum_primary_end_element);
   358     XML_SetCharacterDataHandler(ctx.primary_parser,yum_character_data);
   359     ctx.filelists_parser=XML_ParserCreate(NULL);
   360     XML_SetUserData(ctx.filelists_parser,&ctx);
   361     XML_SetElementHandler(ctx.filelists_parser,yum_filelists_start_element,
   362       yum_filelists_end_element);
   363     XML_SetCharacterDataHandler(ctx.filelists_parser,yum_character_data);
   364     uri=razor_path_relative_to_uri(base_uri,"repodata/primary.xml.gz",NULL);
   365     stream=plover_razor_input_stream_new(uri,error);
   366     free(uri);
   367     if (!stream) {
   368 	XML_ParserFree(ctx.primary_parser);
   369 	XML_ParserFree(ctx.filelists_parser);
   370 	return NULL;
   371     }
   372     decompressor=g_zlib_decompressor_new(G_ZLIB_COMPRESSOR_FORMAT_GZIP);
   373     primary=g_converter_input_stream_new(G_INPUT_STREAM(stream),
   374       G_CONVERTER(decompressor));
   375     g_object_unref(stream);
   376     g_object_unref(decompressor);
   377     uri=razor_path_relative_to_uri(base_uri,"repodata/filelists.xml.gz",NULL);
   378     stream=plover_razor_input_stream_new(uri,error);
   379     free(uri);
   380     if (!stream) {
   381 	g_object_unref(primary);
   382 	XML_ParserFree(ctx.primary_parser);
   383 	XML_ParserFree(ctx.filelists_parser);
   384 	return NULL;
   385     }
   386     decompressor=g_zlib_decompressor_new(G_ZLIB_COMPRESSOR_FORMAT_GZIP);
   387     filelists=g_converter_input_stream_new(G_INPUT_STREAM(stream),
   388       G_CONVERTER(decompressor));
   389     g_object_unref(stream);
   390     g_object_unref(decompressor);
   391     ctx.current_parser=ctx.primary_parser;
   392     ctx.uris=g_tree_new_full((GCompareDataFunc)strcmp,NULL,g_free,NULL);
   393     ctx.current=0;
   394     do
   395     {
   396 	XML_GetParsingStatus(ctx.current_parser,&status);
   397 	switch (status.parsing)
   398 	{
   399 	    case XML_SUSPENDED:
   400 		XML_ResumeParser(ctx.current_parser);
   401 		break;
   402 	    case XML_PARSING:
   403 	    case XML_INITIALIZED:
   404 		buf=XML_GetBuffer(ctx.current_parser,XML_BUFFER_SIZE);
   405 		if (ctx.current_parser==ctx.primary_parser)
   406 		    len=g_input_stream_read(G_INPUT_STREAM(primary),buf,
   407 		      XML_BUFFER_SIZE,NULL,error);
   408 		else
   409 		    len=g_input_stream_read(G_INPUT_STREAM(filelists),buf,
   410 		      XML_BUFFER_SIZE,NULL,error);
   411 		if (len<0)
   412 		    return NULL;
   413 		XML_ParseBuffer(ctx.current_parser,len,!len);
   414 		break;
   415 	    case XML_FINISHED:
   416 		break;
   417 	}
   418     } while (status.parsing!=XML_FINISHED);
   419     XML_ParserFree(ctx.primary_parser);
   420     XML_ParserFree(ctx.filelists_parser);
   421     g_object_unref(primary);
   422     g_object_unref(filelists);
   423     razor=razor_importer_finish(ctx.importer);
   424 #if RAZOR_HEADER_VERSION_MIN<=1
   425     /*
   426      * Header version 1 is supported by plover v0.3 and is used on
   427      * 32-bit intel machines which allows the setup and update
   428      * applications from v0.3 to work. On other machines, we don't
   429      * want these old applications to work (since they would do
   430      * the wrong thing) and so we use the current header version
   431      * which they don't support.
   432      */
   433     if (plover_system_arch_is_x86())
   434 	razor_set_set_header_version(razor,1);
   435 #endif
   436     uris=g_ptr_array_new();
   437     iter=razor_package_iterator_create(razor);
   438     while(razor_package_iterator_next(iter,&pkg,RAZOR_DETAIL_NAME,&name,
   439       RAZOR_DETAIL_VERSION,&version,RAZOR_DETAIL_ARCH,&arch,RAZOR_DETAIL_LAST))
   440     {
   441 	s=g_strconcat(name,"-",version,".",arch,NULL);
   442 	g_ptr_array_add(uris,g_tree_lookup(ctx.uris,s));
   443 	g_free(s);
   444     }
   445     razor_package_iterator_destroy(iter);
   446     g_ptr_array_add(uris,NULL);
   447     g_tree_unref(ctx.uris);
   448     rpm_uris=(gchar **)g_ptr_array_free(uris,FALSE);
   449     set=plover_package_set_new_from_razor(razor);
   450     razor_set_unref(razor);
   451     repository=plover_repository_new_from_package_set(set,
   452       (const char **)rpm_uris);
   453     g_object_unref(set);
   454     g_strfreev(rpm_uris);
   455     return repository;
   456 }
   457 
   458 struct razor_set *plover_razor_set_create_from_yum_uri(const char *base_uri,
   459   GError **error)
   460 {
   461     PloverRepository *repository;
   462     PloverPackageSet *set;
   463     struct razor_set *razor;
   464     g_return_val_if_fail(plover__uri_validate(base_uri),NULL);
   465     repository=plover_repository_new_from_yum_uri(base_uri,error);
   466     if (!repository)
   467 	return NULL;
   468     set=plover_repository_get_package_set(repository);
   469     razor=plover_package_set_get_razor(set);
   470     razor_set_ref(razor);
   471     g_object_unref(repository);
   472     return razor;
   473 }
   474 
   475 struct razor_set *plover_razor_set_create_from_yum(const char *base,
   476   GError **error)
   477 {
   478     gchar *base_uri;
   479     GFile *file;
   480     struct razor_set *set;
   481     file=g_file_new_for_path(base);
   482     base_uri=g_file_get_uri(file);
   483     g_object_unref(file);
   484     set=plover_razor_set_create_from_yum_uri(base_uri,error);
   485     g_free(base_uri);
   486     return set;
   487 }