From f431cd1a48a6a5186633bf5f16a2d21cb4399e8c Mon Sep 17 00:00:00 2001 From: P. J. McDermott Date: Thu, 09 Feb 2012 10:56:43 -0500 Subject: Initial commit. TODO: Copyright information. Including source code and a patch to add files generated by GNU Autoconf is not very pretty... Running 'make dist' in the SVN trunk to generate a source archive might be better. --- (limited to 'src/libopkg/pkg.c') diff --git a/src/libopkg/pkg.c b/src/libopkg/pkg.c new file mode 100644 index 0000000..d8c3984 --- /dev/null +++ b/src/libopkg/pkg.c @@ -0,0 +1,1428 @@ +/* pkg.c - the opkg package management system + + Carl D. Worth + + Copyright (C) 2001 University of Southern California + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. +*/ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "pkg.h" + +#include "pkg_parse.h" +#include "pkg_extract.h" +#include "opkg_message.h" +#include "opkg_utils.h" + +#include "libbb/libbb.h" +#include "sprintf_alloc.h" +#include "file_util.h" +#include "xsystem.h" +#include "opkg_conf.h" + +typedef struct enum_map enum_map_t; +struct enum_map +{ + unsigned int value; + const char *str; +}; + +static const enum_map_t pkg_state_want_map[] = { + { SW_UNKNOWN, "unknown"}, + { SW_INSTALL, "install"}, + { SW_DEINSTALL, "deinstall"}, + { SW_PURGE, "purge"} +}; + +static const enum_map_t pkg_state_flag_map[] = { + { SF_OK, "ok"}, + { SF_REINSTREQ, "reinstreq"}, + { SF_HOLD, "hold"}, + { SF_REPLACE, "replace"}, + { SF_NOPRUNE, "noprune"}, + { SF_PREFER, "prefer"}, + { SF_OBSOLETE, "obsolete"}, + { SF_USER, "user"}, +}; + +static const enum_map_t pkg_state_status_map[] = { + { SS_NOT_INSTALLED, "not-installed" }, + { SS_UNPACKED, "unpacked" }, + { SS_HALF_CONFIGURED, "half-configured" }, + { SS_INSTALLED, "installed" }, + { SS_HALF_INSTALLED, "half-installed" }, + { SS_CONFIG_FILES, "config-files" }, + { SS_POST_INST_FAILED, "post-inst-failed" }, + { SS_REMOVAL_FAILED, "removal-failed" } +}; + +static void +pkg_init(pkg_t *pkg) +{ + pkg->name = NULL; + pkg->epoch = 0; + pkg->version = NULL; + pkg->revision = NULL; + pkg->dest = NULL; + pkg->src = NULL; + pkg->architecture = NULL; + pkg->maintainer = NULL; + pkg->section = NULL; + pkg->description = NULL; + pkg->state_want = SW_UNKNOWN; + pkg->state_flag = SF_OK; + pkg->state_status = SS_NOT_INSTALLED; + pkg->depends_str = NULL; + pkg->provides_str = NULL; + pkg->depends_count = 0; + pkg->depends = NULL; + pkg->suggests_str = NULL; + pkg->recommends_str = NULL; + pkg->suggests_count = 0; + pkg->recommends_count = 0; + + active_list_init(&pkg->list); + + pkg->conflicts = NULL; + pkg->conflicts_count = 0; + + pkg->replaces = NULL; + pkg->replaces_count = 0; + + pkg->pre_depends_count = 0; + pkg->pre_depends_str = NULL; + pkg->provides_count = 0; + pkg->provides = NULL; + pkg->filename = NULL; + pkg->local_filename = NULL; + pkg->tmp_unpack_dir = NULL; + pkg->md5sum = NULL; +#if defined HAVE_SHA256 + pkg->sha256sum = NULL; +#endif + pkg->size = 0; + pkg->installed_size = 0; + pkg->priority = NULL; + pkg->source = NULL; + conffile_list_init(&pkg->conffiles); + pkg->installed_files = NULL; + pkg->installed_files_ref_cnt = 0; + pkg->essential = 0; + pkg->provided_by_hand = 0; +} + +pkg_t * +pkg_new(void) +{ + pkg_t *pkg; + + pkg = xcalloc(1, sizeof(pkg_t)); + pkg_init(pkg); + + return pkg; +} + +static void +compound_depend_deinit(compound_depend_t *depends) +{ + int i; + for (i = 0; i < depends->possibility_count; i++) + { + depend_t *d; + d = depends->possibilities[i]; + free (d->version); + free (d); + } + free (depends->possibilities); +} + +void +pkg_deinit(pkg_t *pkg) +{ + int i; + + if (pkg->name) + free(pkg->name); + pkg->name = NULL; + + pkg->epoch = 0; + + if (pkg->version) + free(pkg->version); + pkg->version = NULL; + /* revision shares storage with version, so don't free */ + pkg->revision = NULL; + + /* owned by opkg_conf_t */ + pkg->dest = NULL; + /* owned by opkg_conf_t */ + pkg->src = NULL; + + if (pkg->architecture) + free(pkg->architecture); + pkg->architecture = NULL; + + if (pkg->maintainer) + free(pkg->maintainer); + pkg->maintainer = NULL; + + if (pkg->section) + free(pkg->section); + pkg->section = NULL; + + if (pkg->description) + free(pkg->description); + pkg->description = NULL; + + pkg->state_want = SW_UNKNOWN; + pkg->state_flag = SF_OK; + pkg->state_status = SS_NOT_INSTALLED; + + active_list_clear(&pkg->list); + + if (pkg->replaces) + free (pkg->replaces); + pkg->replaces = NULL; + + if (pkg->depends) { + int count = pkg->pre_depends_count + + pkg->depends_count + + pkg->recommends_count + + pkg->suggests_count; + + for (i=0; idepends[i]); + free (pkg->depends); + } + + if (pkg->conflicts) { + for (i=0; iconflicts_count; i++) + compound_depend_deinit (&pkg->conflicts[i]); + free (pkg->conflicts); + } + + if (pkg->provides) + free (pkg->provides); + + pkg->pre_depends_count = 0; + pkg->provides_count = 0; + + if (pkg->filename) + free(pkg->filename); + pkg->filename = NULL; + + if (pkg->local_filename) + free(pkg->local_filename); + pkg->local_filename = NULL; + + /* CLEANUP: It'd be nice to pullin the cleanup function from + opkg_install.c here. See comment in + opkg_install.c:cleanup_temporary_files */ + if (pkg->tmp_unpack_dir) + free(pkg->tmp_unpack_dir); + pkg->tmp_unpack_dir = NULL; + + if (pkg->md5sum) + free(pkg->md5sum); + pkg->md5sum = NULL; + +#if defined HAVE_SHA256 + if (pkg->sha256sum) + free(pkg->sha256sum); + pkg->sha256sum = NULL; +#endif + + if (pkg->priority) + free(pkg->priority); + pkg->priority = NULL; + + if (pkg->source) + free(pkg->source); + pkg->source = NULL; + + conffile_list_deinit(&pkg->conffiles); + + /* XXX: QUESTION: Is forcing this to 1 correct? I suppose so, + since if they are calling deinit, they should know. Maybe do an + assertion here instead? */ + pkg->installed_files_ref_cnt = 1; + pkg_free_installed_files(pkg); + pkg->essential = 0; + + if (pkg->tags) + free (pkg->tags); + pkg->tags = NULL; +} + +int +pkg_init_from_file(pkg_t *pkg, const char *filename) +{ + int fd, err = 0; + FILE *control_file; + char *control_path, *tmp; + + pkg_init(pkg); + + pkg->local_filename = xstrdup(filename); + + tmp = xstrdup(filename); + sprintf_alloc(&control_path, "%s/%s.control.XXXXXX", + conf->tmp_dir, + basename(tmp)); + free(tmp); + fd = mkstemp(control_path); + if (fd == -1) { + opkg_perror(ERROR, "Failed to make temp file %s", control_path); + err = -1; + goto err0; + } + + control_file = fdopen(fd, "r+"); + if (control_file == NULL) { + opkg_perror(ERROR, "Failed to fdopen %s", control_path); + close(fd); + err = -1; + goto err1; + } + + err = pkg_extract_control_file_to_stream(pkg, control_file); + if (err) { + opkg_msg(ERROR, "Failed to extract control file from %s.\n", + filename); + goto err2; + } + + rewind(control_file); + + if ((err = pkg_parse_from_stream(pkg, control_file, 0))) { + if (err == 1) { + opkg_msg(ERROR, "Malformed package file %s.\n", + filename); + } + err = -1; + } + +err2: + fclose(control_file); +err1: + unlink(control_path); +err0: + free(control_path); + + return err; +} + +/* Merge any new information in newpkg into oldpkg */ +int +pkg_merge(pkg_t *oldpkg, pkg_t *newpkg) +{ + if (oldpkg == newpkg) { + return 0; + } + + if (!oldpkg->auto_installed) + oldpkg->auto_installed = newpkg->auto_installed; + + if (!oldpkg->src) + oldpkg->src = newpkg->src; + if (!oldpkg->dest) + oldpkg->dest = newpkg->dest; + if (!oldpkg->architecture) + oldpkg->architecture = xstrdup(newpkg->architecture); + if (!oldpkg->arch_priority) + oldpkg->arch_priority = newpkg->arch_priority; + if (!oldpkg->section) + oldpkg->section = xstrdup(newpkg->section); + if(!oldpkg->maintainer) + oldpkg->maintainer = xstrdup(newpkg->maintainer); + if(!oldpkg->description) + oldpkg->description = xstrdup(newpkg->description); + + if (!oldpkg->depends_count && !oldpkg->pre_depends_count && !oldpkg->recommends_count && !oldpkg->suggests_count) { + oldpkg->depends_count = newpkg->depends_count; + newpkg->depends_count = 0; + + oldpkg->depends = newpkg->depends; + newpkg->depends = NULL; + + oldpkg->pre_depends_count = newpkg->pre_depends_count; + newpkg->pre_depends_count = 0; + + oldpkg->recommends_count = newpkg->recommends_count; + newpkg->recommends_count = 0; + + oldpkg->suggests_count = newpkg->suggests_count; + newpkg->suggests_count = 0; + } + + if (oldpkg->provides_count <= 1) { + oldpkg->provides_count = newpkg->provides_count; + newpkg->provides_count = 0; + + if (!oldpkg->provides) { + oldpkg->provides = newpkg->provides; + newpkg->provides = NULL; + } + } + + if (!oldpkg->conflicts_count) { + oldpkg->conflicts_count = newpkg->conflicts_count; + newpkg->conflicts_count = 0; + + oldpkg->conflicts = newpkg->conflicts; + newpkg->conflicts = NULL; + } + + if (!oldpkg->replaces_count) { + oldpkg->replaces_count = newpkg->replaces_count; + newpkg->replaces_count = 0; + + oldpkg->replaces = newpkg->replaces; + newpkg->replaces = NULL; + } + + if (!oldpkg->filename) + oldpkg->filename = xstrdup(newpkg->filename); + if (!oldpkg->local_filename) + oldpkg->local_filename = xstrdup(newpkg->local_filename); + if (!oldpkg->tmp_unpack_dir) + oldpkg->tmp_unpack_dir = xstrdup(newpkg->tmp_unpack_dir); + if (!oldpkg->md5sum) + oldpkg->md5sum = xstrdup(newpkg->md5sum); +#if defined HAVE_SHA256 + if (!oldpkg->sha256sum) + oldpkg->sha256sum = xstrdup(newpkg->sha256sum); +#endif + if (!oldpkg->size) + oldpkg->size = newpkg->size; + if (!oldpkg->installed_size) + oldpkg->installed_size = newpkg->installed_size; + if (!oldpkg->priority) + oldpkg->priority = xstrdup(newpkg->priority); + if (!oldpkg->source) + oldpkg->source = xstrdup(newpkg->source); + + if (nv_pair_list_empty(&oldpkg->conffiles)){ + list_splice_init(&newpkg->conffiles.head, &oldpkg->conffiles.head); + } + + if (!oldpkg->installed_files){ + oldpkg->installed_files = newpkg->installed_files; + oldpkg->installed_files_ref_cnt = newpkg->installed_files_ref_cnt; + newpkg->installed_files = NULL; + } + + if (!oldpkg->essential) + oldpkg->essential = newpkg->essential; + + return 0; +} + +static void +abstract_pkg_init(abstract_pkg_t *ab_pkg) +{ + ab_pkg->provided_by = abstract_pkg_vec_alloc(); + ab_pkg->dependencies_checked = 0; + ab_pkg->state_status = SS_NOT_INSTALLED; +} + +abstract_pkg_t * +abstract_pkg_new(void) +{ + abstract_pkg_t * ab_pkg; + + ab_pkg = xcalloc(1, sizeof(abstract_pkg_t)); + abstract_pkg_init(ab_pkg); + + return ab_pkg; +} + +void +set_flags_from_control(pkg_t *pkg){ + char *file_name; + FILE *fp; + + sprintf_alloc(&file_name,"%s/%s.control", pkg->dest->info_dir, pkg->name); + + fp = fopen(file_name, "r"); + if (fp == NULL) { + opkg_perror(ERROR, "Failed to open %s", file_name); + free(file_name); + return; + } + + free(file_name); + + if (pkg_parse_from_stream(pkg, fp, PFM_ALL ^ PFM_ESSENTIAL)) { + opkg_msg(DEBUG, "Unable to read control file for %s. May be empty.\n", + pkg->name); + } + + fclose(fp); + + return; +} + +static const char * +pkg_state_want_to_str(pkg_state_want_t sw) +{ + int i; + + for (i=0; i < ARRAY_SIZE(pkg_state_want_map); i++) { + if (pkg_state_want_map[i].value == sw) { + return pkg_state_want_map[i].str; + } + } + + opkg_msg(ERROR, "Internal error: state_want=%d\n", sw); + return ""; +} + +pkg_state_want_t +pkg_state_want_from_str(char *str) +{ + int i; + + for (i=0; i < ARRAY_SIZE(pkg_state_want_map); i++) { + if (strcmp(str, pkg_state_want_map[i].str) == 0) { + return pkg_state_want_map[i].value; + } + } + + opkg_msg(ERROR, "Internal error: state_want=%s\n", str); + return SW_UNKNOWN; +} + +static char * +pkg_state_flag_to_str(pkg_state_flag_t sf) +{ + int i; + unsigned int len; + char *str; + + /* clear the temporary flags before converting to string */ + sf &= SF_NONVOLATILE_FLAGS; + + if (sf == 0) + return xstrdup("ok"); + + len = 0; + for (i=0; i < ARRAY_SIZE(pkg_state_flag_map); i++) { + if (sf & pkg_state_flag_map[i].value) + len += strlen(pkg_state_flag_map[i].str) + 1; + } + + str = xmalloc(len+1); + str[0] = '\0'; + + for (i=0; i < ARRAY_SIZE(pkg_state_flag_map); i++) { + if (sf & pkg_state_flag_map[i].value) { + strncat(str, pkg_state_flag_map[i].str, len); + strncat(str, ",", len); + } + } + + len = strlen(str); + str[len-1] = '\0'; /* squash last comma */ + + return str; +} + +pkg_state_flag_t +pkg_state_flag_from_str(const char *str) +{ + int i; + int sf = SF_OK; + const char *sfname; + unsigned int sfname_len; + + if (strcmp(str, "ok") == 0) { + return SF_OK; + } + for (i=0; i < ARRAY_SIZE(pkg_state_flag_map); i++) { + sfname = pkg_state_flag_map[i].str; + sfname_len = strlen(sfname); + if (strncmp(str, sfname, sfname_len) == 0) { + sf |= pkg_state_flag_map[i].value; + str += sfname_len; + if (str[0] == ',') { + str++; + } else { + break; + } + } + } + + return sf; +} + +static const char * +pkg_state_status_to_str(pkg_state_status_t ss) +{ + int i; + + for (i=0; i < ARRAY_SIZE(pkg_state_status_map); i++) { + if (pkg_state_status_map[i].value == ss) { + return pkg_state_status_map[i].str; + } + } + + opkg_msg(ERROR, "Internal error: state_status=%d\n", ss); + return ""; +} + +pkg_state_status_t +pkg_state_status_from_str(const char *str) +{ + int i; + + for (i=0; i < ARRAY_SIZE(pkg_state_status_map); i++) { + if (strcmp(str, pkg_state_status_map[i].str) == 0) { + return pkg_state_status_map[i].value; + } + } + + opkg_msg(ERROR, "Internal error: state_status=%s\n", str); + return SS_NOT_INSTALLED; +} + +void +pkg_formatted_field(FILE *fp, pkg_t *pkg, const char *field) +{ + int i, j; + char *str; + int depends_count = pkg->pre_depends_count + + pkg->depends_count + + pkg->recommends_count + + pkg->suggests_count; + + if (strlen(field) < PKG_MINIMUM_FIELD_NAME_LEN) { + goto UNKNOWN_FMT_FIELD; + } + + switch (field[0]) + { + case 'a': + case 'A': + if (strcasecmp(field, "Architecture") == 0) { + if (pkg->architecture) { + fprintf(fp, "Architecture: %s\n", pkg->architecture); + } + } else if (strcasecmp(field, "Auto-Installed") == 0) { + if (pkg->auto_installed) + fprintf(fp, "Auto-Installed: yes\n"); + } else { + goto UNKNOWN_FMT_FIELD; + } + break; + case 'c': + case 'C': + if (strcasecmp(field, "Conffiles") == 0) { + conffile_list_elt_t *iter; + + if (nv_pair_list_empty(&pkg->conffiles)) + return; + + fprintf(fp, "Conffiles:\n"); + for (iter = nv_pair_list_first(&pkg->conffiles); iter; iter = nv_pair_list_next(&pkg->conffiles, iter)) { + if (((conffile_t *)iter->data)->name && ((conffile_t *)iter->data)->value) { + fprintf(fp, " %s %s\n", + ((conffile_t *)iter->data)->name, + ((conffile_t *)iter->data)->value); + } + } + } else if (strcasecmp(field, "Conflicts") == 0) { + struct depend *cdep; + if (pkg->conflicts_count) { + fprintf(fp, "Conflicts:"); + for(i = 0; i < pkg->conflicts_count; i++) { + cdep = pkg->conflicts[i].possibilities[0]; + fprintf(fp, "%s %s", i == 0 ? "" : ",", + cdep->pkg->name); + if (cdep->version) { + fprintf(fp, " (%s%s)", + constraint_to_str(cdep->constraint), + cdep->version); + } + } + fprintf(fp, "\n"); + } + } else { + goto UNKNOWN_FMT_FIELD; + } + break; + case 'd': + case 'D': + if (strcasecmp(field, "Depends") == 0) { + if (pkg->depends_count) { + fprintf(fp, "Depends:"); + for (j=0, i=0; idepends[i].type != DEPEND) + continue; + str = pkg_depend_str(pkg, i); + fprintf(fp, "%s %s", j == 0 ? "" : ",", str); + free(str); + j++; + } + fprintf(fp, "\n"); + } + } else if (strcasecmp(field, "Description") == 0) { + if (pkg->description) { + fprintf(fp, "Description: %s\n", pkg->description); + } + } else { + goto UNKNOWN_FMT_FIELD; + } + break; + case 'e': + case 'E': + if (pkg->essential) { + fprintf(fp, "Essential: yes\n"); + } + break; + case 'f': + case 'F': + if (pkg->filename) { + fprintf(fp, "Filename: %s\n", pkg->filename); + } + break; + case 'i': + case 'I': + if (strcasecmp(field, "Installed-Size") == 0) { + fprintf(fp, "Installed-Size: %ld\n", pkg->installed_size); + } else if (strcasecmp(field, "Installed-Time") == 0 && pkg->installed_time) { + fprintf(fp, "Installed-Time: %lu\n", pkg->installed_time); + } + break; + case 'm': + case 'M': + if (strcasecmp(field, "Maintainer") == 0) { + if (pkg->maintainer) { + fprintf(fp, "Maintainer: %s\n", pkg->maintainer); + } + } else if (strcasecmp(field, "MD5sum") == 0) { + if (pkg->md5sum) { + fprintf(fp, "MD5Sum: %s\n", pkg->md5sum); + } + } else { + goto UNKNOWN_FMT_FIELD; + } + break; + case 'p': + case 'P': + if (strcasecmp(field, "Package") == 0) { + fprintf(fp, "Package: %s\n", pkg->name); + } else if (strcasecmp(field, "Priority") == 0) { + fprintf(fp, "Priority: %s\n", pkg->priority); + } else if (strcasecmp(field, "Provides") == 0) { + if (pkg->provides_count) { + fprintf(fp, "Provides:"); + for(i = 1; i < pkg->provides_count; i++) { + fprintf(fp, "%s %s", i == 1 ? "" : ",", + pkg->provides[i]->name); + } + fprintf(fp, "\n"); + } + } else { + goto UNKNOWN_FMT_FIELD; + } + break; + case 'r': + case 'R': + if (strcasecmp (field, "Replaces") == 0) { + if (pkg->replaces_count) { + fprintf(fp, "Replaces:"); + for (i = 0; i < pkg->replaces_count; i++) { + fprintf(fp, "%s %s", i == 0 ? "" : ",", + pkg->replaces[i]->name); + } + fprintf(fp, "\n"); + } + } else if (strcasecmp (field, "Recommends") == 0) { + if (pkg->recommends_count) { + fprintf(fp, "Recommends:"); + for (j=0, i=0; idepends[i].type != RECOMMEND) + continue; + str = pkg_depend_str(pkg, i); + fprintf(fp, "%s %s", j == 0 ? "" : ",", str); + free(str); + j++; + } + fprintf(fp, "\n"); + } + } else { + goto UNKNOWN_FMT_FIELD; + } + break; + case 's': + case 'S': + if (strcasecmp(field, "Section") == 0) { + if (pkg->section) { + fprintf(fp, "Section: %s\n", pkg->section); + } +#if defined HAVE_SHA256 + } else if (strcasecmp(field, "SHA256sum") == 0) { + if (pkg->sha256sum) { + fprintf(fp, "SHA256sum: %s\n", pkg->sha256sum); + } +#endif + } else if (strcasecmp(field, "Size") == 0) { + if (pkg->size) { + fprintf(fp, "Size: %ld\n", pkg->size); + } + } else if (strcasecmp(field, "Source") == 0) { + if (pkg->source) { + fprintf(fp, "Source: %s\n", pkg->source); + } + } else if (strcasecmp(field, "Status") == 0) { + char *pflag = pkg_state_flag_to_str(pkg->state_flag); + fprintf(fp, "Status: %s %s %s\n", + pkg_state_want_to_str(pkg->state_want), + pflag, + pkg_state_status_to_str(pkg->state_status)); + free(pflag); + } else if (strcasecmp(field, "Suggests") == 0) { + if (pkg->suggests_count) { + fprintf(fp, "Suggests:"); + for (j=0, i=0; idepends[i].type != SUGGEST) + continue; + str = pkg_depend_str(pkg, i); + fprintf(fp, "%s %s", j == 0 ? "" : ",", str); + free(str); + j++; + } + fprintf(fp, "\n"); + } + } else { + goto UNKNOWN_FMT_FIELD; + } + break; + case 't': + case 'T': + if (strcasecmp(field, "Tags") == 0) { + if (pkg->tags) { + fprintf(fp, "Tags: %s\n", pkg->tags); + } + } + break; + case 'v': + case 'V': + { + char *version = pkg_version_str_alloc(pkg); + if (version == NULL) + return; + fprintf(fp, "Version: %s\n", version); + free(version); + } + break; + default: + goto UNKNOWN_FMT_FIELD; + } + + return; + +UNKNOWN_FMT_FIELD: + opkg_msg(ERROR, "Internal error: field=%s\n", field); +} + +void +pkg_formatted_info(FILE *fp, pkg_t *pkg) +{ + pkg_formatted_field(fp, pkg, "Package"); + pkg_formatted_field(fp, pkg, "Version"); + pkg_formatted_field(fp, pkg, "Depends"); + pkg_formatted_field(fp, pkg, "Recommends"); + pkg_formatted_field(fp, pkg, "Suggests"); + pkg_formatted_field(fp, pkg, "Provides"); + pkg_formatted_field(fp, pkg, "Replaces"); + pkg_formatted_field(fp, pkg, "Conflicts"); + pkg_formatted_field(fp, pkg, "Status"); + pkg_formatted_field(fp, pkg, "Section"); + pkg_formatted_field(fp, pkg, "Essential"); + pkg_formatted_field(fp, pkg, "Architecture"); + pkg_formatted_field(fp, pkg, "Maintainer"); + pkg_formatted_field(fp, pkg, "MD5sum"); + pkg_formatted_field(fp, pkg, "Size"); + pkg_formatted_field(fp, pkg, "Filename"); + pkg_formatted_field(fp, pkg, "Conffiles"); + pkg_formatted_field(fp, pkg, "Source"); + pkg_formatted_field(fp, pkg, "Description"); + pkg_formatted_field(fp, pkg, "Installed-Time"); + pkg_formatted_field(fp, pkg, "Tags"); + fputs("\n", fp); +} + +void +pkg_print_status(pkg_t * pkg, FILE * file) +{ + if (pkg == NULL) { + return; + } + + pkg_formatted_field(file, pkg, "Package"); + pkg_formatted_field(file, pkg, "Version"); + pkg_formatted_field(file, pkg, "Depends"); + pkg_formatted_field(file, pkg, "Recommends"); + pkg_formatted_field(file, pkg, "Suggests"); + pkg_formatted_field(file, pkg, "Provides"); + pkg_formatted_field(file, pkg, "Replaces"); + pkg_formatted_field(file, pkg, "Conflicts"); + pkg_formatted_field(file, pkg, "Status"); + pkg_formatted_field(file, pkg, "Essential"); + pkg_formatted_field(file, pkg, "Architecture"); + pkg_formatted_field(file, pkg, "Conffiles"); + pkg_formatted_field(file, pkg, "Installed-Time"); + pkg_formatted_field(file, pkg, "Auto-Installed"); + fputs("\n", file); +} + +/* + * libdpkg - Debian packaging suite library routines + * vercmp.c - comparison of version numbers + * + * Copyright (C) 1995 Ian Jackson + */ + +/* assume ascii; warning: evaluates x multiple times! */ +#define order(x) ((x) == '~' ? -1 \ + : isdigit((x)) ? 0 \ + : !(x) ? 0 \ + : isalpha((x)) ? (x) \ + : (x) + 256) + +static int +verrevcmp(const char *val, const char *ref) { + if (!val) val= ""; + if (!ref) ref= ""; + + while (*val || *ref) { + int first_diff= 0; + + while ( (*val && !isdigit(*val)) || (*ref && !isdigit(*ref)) ) { + int vc= order(*val), rc= order(*ref); + if (vc != rc) return vc - rc; + val++; ref++; + } + + while ( *val == '0' ) val++; + while ( *ref == '0' ) ref++; + while (isdigit(*val) && isdigit(*ref)) { + if (!first_diff) first_diff= *val - *ref; + val++; ref++; + } + if (isdigit(*val)) return 1; + if (isdigit(*ref)) return -1; + if (first_diff) return first_diff; + } + return 0; +} + +int +pkg_compare_versions(const pkg_t *pkg, const pkg_t *ref_pkg) +{ + int r; + + if (pkg->epoch > ref_pkg->epoch) { + return 1; + } + + if (pkg->epoch < ref_pkg->epoch) { + return -1; + } + + r = verrevcmp(pkg->version, ref_pkg->version); + if (r) { + return r; + } + + r = verrevcmp(pkg->revision, ref_pkg->revision); + if (r) { + return r; + } + + return r; +} + + +int +pkg_version_satisfied(pkg_t *it, pkg_t *ref, const char *op) +{ + int r; + + r = pkg_compare_versions(it, ref); + + if (strcmp(op, "<=") == 0 || strcmp(op, "<") == 0) { + return r <= 0; + } + + if (strcmp(op, ">=") == 0 || strcmp(op, ">") == 0) { + return r >= 0; + } + + if (strcmp(op, "<<") == 0) { + return r < 0; + } + + if (strcmp(op, ">>") == 0) { + return r > 0; + } + + if (strcmp(op, "=") == 0) { + return r == 0; + } + + opkg_msg(ERROR, "Unknown operator: %s.\n", op); + return 0; +} + +int +pkg_name_version_and_architecture_compare(const void *p1, const void *p2) +{ + const pkg_t *a = *(const pkg_t**) p1; + const pkg_t *b = *(const pkg_t**) p2; + int namecmp; + int vercmp; + if (!a->name || !b->name) { + opkg_msg(ERROR, "Internal error: a->name=%p, b->name=%p.\n", + a->name, b->name); + return 0; + } + + namecmp = strcmp(a->name, b->name); + if (namecmp) + return namecmp; + vercmp = pkg_compare_versions(a, b); + if (vercmp) + return vercmp; + if (!a->arch_priority || !b->arch_priority) { + opkg_msg(ERROR, "Internal error: a->arch_priority=%i b->arch_priority=%i.\n", + a->arch_priority, b->arch_priority); + return 0; + } + if (a->arch_priority > b->arch_priority) + return 1; + if (a->arch_priority < b->arch_priority) + return -1; + return 0; +} + +int +abstract_pkg_name_compare(const void *p1, const void *p2) +{ + const abstract_pkg_t *a = *(const abstract_pkg_t **)p1; + const abstract_pkg_t *b = *(const abstract_pkg_t **)p2; + if (!a->name || !b->name) { + opkg_msg(ERROR, "Internal error: a->name=%p b->name=%p.\n", + a->name, b->name); + return 0; + } + return strcmp(a->name, b->name); +} + + +char * +pkg_version_str_alloc(pkg_t *pkg) +{ + char *version; + + if (pkg->epoch) { + if (pkg->revision) + sprintf_alloc(&version, "%d:%s-%s", + pkg->epoch, pkg->version, pkg->revision); + else + sprintf_alloc(&version, "%d:%s", + pkg->epoch, pkg->version); + } else { + if (pkg->revision) + sprintf_alloc(&version, "%s-%s", + pkg->version, pkg->revision); + else + version = xstrdup(pkg->version); + } + + return version; +} + +/* + * XXX: this should be broken into two functions + */ +str_list_t * +pkg_get_installed_files(pkg_t *pkg) +{ + int err, fd; + char *list_file_name = NULL; + FILE *list_file = NULL; + char *line; + char *installed_file_name; + unsigned int rootdirlen = 0; + int list_from_package; + + pkg->installed_files_ref_cnt++; + + if (pkg->installed_files) { + return pkg->installed_files; + } + + pkg->installed_files = str_list_alloc(); + + /* + * For installed packages, look at the package.list file in the database. + * For uninstalled packages, get the file list directly from the package. + */ + if (pkg->state_status == SS_NOT_INSTALLED || pkg->dest == NULL) + list_from_package = 1; + else + list_from_package = 0; + + if (list_from_package) { + if (pkg->local_filename == NULL) { + return pkg->installed_files; + } + /* XXX: CLEANUP: Maybe rewrite this to avoid using a temporary + file. In other words, change deb_extract so that it can + simply return the file list as a char *[] rather than + insisting on writing it to a FILE * as it does now. */ + sprintf_alloc(&list_file_name, "%s/%s.list.XXXXXX", + conf->tmp_dir, pkg->name); + fd = mkstemp(list_file_name); + if (fd == -1) { + opkg_perror(ERROR, "Failed to make temp file %s.", + list_file_name); + free(list_file_name); + return pkg->installed_files; + } + list_file = fdopen(fd, "r+"); + if (list_file == NULL) { + opkg_perror(ERROR, "Failed to fdopen temp file %s.", + list_file_name); + close(fd); + unlink(list_file_name); + free(list_file_name); + return pkg->installed_files; + } + err = pkg_extract_data_file_names_to_stream(pkg, list_file); + if (err) { + opkg_msg(ERROR, "Error extracting file list from %s.\n", + pkg->local_filename); + fclose(list_file); + unlink(list_file_name); + free(list_file_name); + str_list_deinit(pkg->installed_files); + pkg->installed_files = NULL; + return NULL; + } + rewind(list_file); + } else { + sprintf_alloc(&list_file_name, "%s/%s.list", + pkg->dest->info_dir, pkg->name); + list_file = fopen(list_file_name, "r"); + if (list_file == NULL) { + opkg_perror(ERROR, "Failed to open %s", + list_file_name); + free(list_file_name); + return pkg->installed_files; + } + free(list_file_name); + } + + if (conf->offline_root) + rootdirlen = strlen(conf->offline_root); + + while (1) { + char *file_name; + + line = file_read_line_alloc(list_file); + if (line == NULL) { + break; + } + file_name = line; + + if (list_from_package) { + if (*file_name == '.') { + file_name++; + } + if (*file_name == '/') { + file_name++; + } + sprintf_alloc(&installed_file_name, "%s%s", + pkg->dest->root_dir, file_name); + } else { + if (conf->offline_root && + strncmp(conf->offline_root, file_name, rootdirlen)) { + sprintf_alloc(&installed_file_name, "%s%s", + conf->offline_root, file_name); + } else { + // already contains root_dir as header -> ABSOLUTE + sprintf_alloc(&installed_file_name, "%s", file_name); + } + } + str_list_append(pkg->installed_files, installed_file_name); + free(installed_file_name); + free(line); + } + + fclose(list_file); + + if (list_from_package) { + unlink(list_file_name); + free(list_file_name); + } + + return pkg->installed_files; +} + +/* XXX: CLEANUP: This function and it's counterpart, + (pkg_get_installed_files), do not match our init/deinit naming + convention. Nor the alloc/free convention. But, then again, neither + of these conventions currrently fit the way these two functions + work. */ +void +pkg_free_installed_files(pkg_t *pkg) +{ + pkg->installed_files_ref_cnt--; + + if (pkg->installed_files_ref_cnt > 0) + return; + + if (pkg->installed_files) { + str_list_purge(pkg->installed_files); + } + + pkg->installed_files = NULL; +} + +void +pkg_remove_installed_files_list(pkg_t *pkg) +{ + char *list_file_name; + + sprintf_alloc(&list_file_name, "%s/%s.list", + pkg->dest->info_dir, pkg->name); + + if (!conf->noaction) + (void)unlink(list_file_name); + + free(list_file_name); +} + +conffile_t * +pkg_get_conffile(pkg_t *pkg, const char *file_name) +{ + conffile_list_elt_t *iter; + conffile_t *conffile; + + if (pkg == NULL) { + return NULL; + } + + for (iter = nv_pair_list_first(&pkg->conffiles); iter; iter = nv_pair_list_next(&pkg->conffiles, iter)) { + conffile = (conffile_t *)iter->data; + + if (strcmp(conffile->name, file_name) == 0) { + return conffile; + } + } + + return NULL; +} + +int +pkg_run_script(pkg_t *pkg, const char *script, const char *args) +{ + int err; + char *path; + char *cmd; + + if (conf->noaction) + return 0; + + if (conf->offline_root && !conf->force_postinstall) { + opkg_msg(INFO, "Offline root mode: not running %s.%s.\n", + pkg->name, script); + return 0; + } + + /* Installed packages have scripts in pkg->dest->info_dir, uninstalled packages + have scripts in pkg->tmp_unpack_dir. */ + if (pkg->state_status == SS_INSTALLED || pkg->state_status == SS_UNPACKED) { + if (pkg->dest == NULL) { + opkg_msg(ERROR, "Internal error: %s has a NULL dest.\n", + pkg->name); + return -1; + } + sprintf_alloc(&path, "%s/%s.%s", pkg->dest->info_dir, pkg->name, script); + } else { + if (pkg->tmp_unpack_dir == NULL) { + opkg_msg(ERROR, "Internal error: %s has a NULL tmp_unpack_dir.\n", + pkg->name); + return -1; + } + sprintf_alloc(&path, "%s/%s", pkg->tmp_unpack_dir, script); + } + + opkg_msg(INFO, "Running script %s.\n", path); + + setenv("PKG_ROOT", + pkg->dest ? pkg->dest->root_dir : conf->default_dest->root_dir, 1); + + if (! file_exists(path)) { + free(path); + return 0; + } + + sprintf_alloc(&cmd, "%s %s", path, args); + free(path); + { + const char *argv[] = {"sh", "-c", cmd, NULL}; + err = xsystem(argv); + } + free(cmd); + + if (err) { + opkg_msg(ERROR, "package \"%s\" %s script returned status %d.\n", + pkg->name, script, err); + return err; + } + + return 0; +} + +int +pkg_arch_supported(pkg_t *pkg) +{ + nv_pair_list_elt_t *l; + + if (!pkg->architecture) + return 1; + + list_for_each_entry(l , &conf->arch_list.head, node) { + nv_pair_t *nv = (nv_pair_t *)l->data; + if (strcmp(nv->name, pkg->architecture) == 0) { + opkg_msg(DEBUG, "Arch %s (priority %s) supported for pkg %s.\n", + nv->name, nv->value, pkg->name); + return 1; + } + } + + opkg_msg(DEBUG, "Arch %s unsupported for pkg %s.\n", + pkg->architecture, pkg->name); + return 0; +} + +void +pkg_info_preinstall_check(void) +{ + int i; + pkg_vec_t *installed_pkgs = pkg_vec_alloc(); + + /* update the file owner data structure */ + opkg_msg(INFO, "Updating file owner list.\n"); + pkg_hash_fetch_all_installed(installed_pkgs); + for (i = 0; i < installed_pkgs->len; i++) { + pkg_t *pkg = installed_pkgs->pkgs[i]; + str_list_t *installed_files = pkg_get_installed_files(pkg); /* this causes installed_files to be cached */ + str_list_elt_t *iter, *niter; + if (installed_files == NULL) { + opkg_msg(ERROR, "Failed to determine installed " + "files for pkg %s.\n", pkg->name); + break; + } + for (iter = str_list_first(installed_files), niter = str_list_next(installed_files, iter); + iter; + iter = niter, niter = str_list_next(installed_files, iter)) { + char *installed_file = (char *) iter->data; + file_hash_set_file_owner(installed_file, pkg); + } + pkg_free_installed_files(pkg); + } + pkg_vec_free(installed_pkgs); +} + +struct pkg_write_filelist_data { + pkg_t *pkg; + FILE *stream; +}; + +static void +pkg_write_filelist_helper(const char *key, void *entry_, void *data_) +{ + struct pkg_write_filelist_data *data = data_; + pkg_t *entry = entry_; + if (entry == data->pkg) { + fprintf(data->stream, "%s\n", key); + } +} + +int +pkg_write_filelist(pkg_t *pkg) +{ + struct pkg_write_filelist_data data; + char *list_file_name; + + sprintf_alloc(&list_file_name, "%s/%s.list", + pkg->dest->info_dir, pkg->name); + + opkg_msg(INFO, "Creating %s file for pkg %s.\n", + list_file_name, pkg->name); + + data.stream = fopen(list_file_name, "w"); + if (!data.stream) { + opkg_perror(ERROR, "Failed to open %s", + list_file_name); + free(list_file_name); + return -1; + } + + data.pkg = pkg; + hash_table_foreach(&conf->file_hash, pkg_write_filelist_helper, &data); + fclose(data.stream); + free(list_file_name); + + pkg->state_flag &= ~SF_FILELIST_CHANGED; + + return 0; +} + +int +pkg_write_changed_filelists(void) +{ + pkg_vec_t *installed_pkgs = pkg_vec_alloc(); + int i, err, ret = 0; + + if (conf->noaction) + return 0; + + opkg_msg(INFO, "Saving changed filelists.\n"); + + pkg_hash_fetch_all_installed(installed_pkgs); + for (i = 0; i < installed_pkgs->len; i++) { + pkg_t *pkg = installed_pkgs->pkgs[i]; + if (pkg->state_flag & SF_FILELIST_CHANGED) { + err = pkg_write_filelist(pkg); + if (err) + ret = -1; + } + } + + pkg_vec_free (installed_pkgs); + + return ret; +} -- cgit v0.9.1