[LEDE-DEV,opkg-lede,2/2] pkg: alternatives support

Submitted by Yousong Zhou on March 20, 2017, 11:39 a.m.

Details

Message ID 1490009989-29321-2-git-send-email-yszhou4tech@gmail.com
State New
Delegated to: Jo-Philipp Wich
Headers show

Commit Message

Yousong Zhou March 20, 2017, 11:39 a.m.
It's a list of specs specs of the following form seprated by commas to describe
alternatives provided by the package

    <prio>:<path>:<altpath>

<path> will be a symbolic link to <altpath> of the highest <prio>

Size comparison on x86_64 after the change

    function                                             old     new   delta
    pkg_alternatives_update                                -     587    +587
    pkg_parse_line                                      2101    2609    +522
    .rodata                                            24594   24738    +144
    pkg_formatted_field                                 2385    2528    +143
    pkg_deinit                                           427     486     +59
    pkg_print_status                                     264     280     +16
    opkg_configure                                        59      69     +10
    xreadlink                                            120     128      +8
    opkg_remove_pkg                                     1079    1087      +8
    ------------------------------------------------------------------------------
    (add/remove: 2/0 grow/shrink: 8/0 up/down: 1483/0)           Total: 1497 bytes

Signed-off-by: Yousong Zhou <yszhou4tech@gmail.com>
---
 libopkg/CMakeLists.txt     |   2 +-
 libopkg/opkg_configure.c   |   3 ++
 libopkg/opkg_remove.c      |   2 +
 libopkg/pkg.c              |  32 ++++++++++++-
 libopkg/pkg.h              |  19 ++++++++
 libopkg/pkg_alternatives.c | 113 +++++++++++++++++++++++++++++++++++++++++++++
 libopkg/pkg_alternatives.h |  23 +++++++++
 libopkg/pkg_parse.c        |  81 +++++++++++++++++++++++++++++++-
 libopkg/pkg_parse.h        |  53 ++++++++++-----------
 9 files changed, 299 insertions(+), 29 deletions(-)
 create mode 100644 libopkg/pkg_alternatives.c
 create mode 100644 libopkg/pkg_alternatives.h

Patch hide | download patch | download mbox

diff --git a/libopkg/CMakeLists.txt b/libopkg/CMakeLists.txt
index 637dadb..1b2a949 100644
--- a/libopkg/CMakeLists.txt
+++ b/libopkg/CMakeLists.txt
@@ -9,7 +9,7 @@  ADD_LIBRARY(opkg STATIC
 	active_list.c conffile.c conffile_list.c file_util.c hash_table.c
 	nv_pair.c nv_pair_list.c opkg.c opkg_cmd.c opkg_conf.c opkg_configure.c
 	opkg_download.c opkg_install.c opkg_message.c opkg_remove.c
-	opkg_upgrade.c opkg_utils.c parse_util.c pkg.c pkg_depends.c pkg_dest.c
+	opkg_upgrade.c opkg_utils.c parse_util.c pkg.c pkg_alternatives.c pkg_depends.c pkg_dest.c
 	pkg_dest_list.c pkg_extract.c pkg_hash.c pkg_parse.c pkg_src.c
 	pkg_src_list.c pkg_vec.c sha256.c sprintf_alloc.c str_list.c
 	void_list.c xregex.c xsystem.c
diff --git a/libopkg/opkg_configure.c b/libopkg/opkg_configure.c
index dc05f1e..a043c52 100644
--- a/libopkg/opkg_configure.c
+++ b/libopkg/opkg_configure.c
@@ -21,6 +21,7 @@ 
 #include "opkg_configure.h"
 #include "opkg_message.h"
 #include "opkg_cmd.h"
+#include "pkg_alternatives.h"
 
 int opkg_configure(pkg_t * pkg)
 {
@@ -38,5 +39,7 @@  int opkg_configure(pkg_t * pkg)
 		return err;
 	}
 
+	pkg_alternatives_update(pkg);
+
 	return 0;
 }
diff --git a/libopkg/opkg_remove.c b/libopkg/opkg_remove.c
index 694c3f3..96ca558 100644
--- a/libopkg/opkg_remove.c
+++ b/libopkg/opkg_remove.c
@@ -22,6 +22,7 @@ 
 #include "opkg_message.h"
 #include "opkg_remove.h"
 #include "opkg_cmd.h"
+#include "pkg_alternatives.h"
 #include "file_util.h"
 #include "sprintf_alloc.h"
 #include "libbb/libbb.h"
@@ -312,6 +313,7 @@  int opkg_remove_pkg(pkg_t * pkg, int from_upgrade)
 
 	remove_maintainer_scripts(pkg);
 	pkg->state_status = SS_NOT_INSTALLED;
+	pkg_alternatives_update(pkg);
 
 	if (parent_pkg)
 		parent_pkg->state_status = SS_NOT_INSTALLED;
diff --git a/libopkg/pkg.c b/libopkg/pkg.c
index c0de884..8b873bd 100644
--- a/libopkg/pkg.c
+++ b/libopkg/pkg.c
@@ -321,6 +321,20 @@  void pkg_deinit(pkg_t * pkg)
 
 			pkg_set_ptr(pkg, blob_id(cur), NULL);
 			break;
+		case PKG_ALTERNATIVES:
+			ptr = pkg_get_ptr(pkg, blob_id(cur));
+
+			if (ptr) {
+				struct pkg_alternatives *pkg_alts = ptr;
+
+				while (pkg_alts->nalts)
+					free(pkg_alts->alts[--pkg_alts->nalts]);
+				free(pkg_alts->alts);
+				free(pkg_alts);
+			}
+
+			pkg_set_ptr(pkg, blob_id(cur), NULL);
+			break;
 		}
 	}
 
@@ -636,7 +650,22 @@  void pkg_formatted_field(FILE * fp, pkg_t * pkg, const char *field)
 	switch (field[0]) {
 	case 'a':
 	case 'A':
-		if (strcasecmp(field, "Architecture") == 0) {
+		if (strcasecmp(field, "Alternatives") == 0) {
+			struct pkg_alternatives *pkg_alts = pkg_get_ptr(pkg, PKG_ALTERNATIVES);
+
+			if (pkg_alts && pkg_alts->nalts > 0) {
+				int i;
+				struct pkg_alternative *alt;
+
+				alt = pkg_alts->alts[0];
+				fprintf(fp, "Alternatives: %d:%s:%s", alt->prio, alt->path, alt->altpath);
+				for (i = 1; i < pkg_alts->nalts; i++) {
+					alt = pkg_alts->alts[i];
+					fprintf(fp, ", %d:%s:%s", alt->prio, alt->path, alt->altpath);
+				}
+				fputs("\n", fp);
+			}
+		} else if (strcasecmp(field, "Architecture") == 0) {
 			p = pkg_get_architecture(pkg);
 			if (p) {
 				fprintf(fp, "Architecture: %s\n",
@@ -938,6 +967,7 @@  void pkg_print_status(pkg_t * pkg, FILE * file)
 	pkg_formatted_field(file, pkg, "Conffiles");
 	pkg_formatted_field(file, pkg, "Installed-Time");
 	pkg_formatted_field(file, pkg, "Auto-Installed");
+	pkg_formatted_field(file, pkg, "Alternatives");
 	fputs("\n", file);
 }
 
diff --git a/libopkg/pkg.h b/libopkg/pkg.h
index cf405b1..600fc9e 100644
--- a/libopkg/pkg.h
+++ b/libopkg/pkg.h
@@ -100,6 +100,7 @@  enum pkg_fields {
 	PKG_DEPENDS,
 	PKG_CONFLICTS,
 	PKG_CONFFILES,
+	PKG_ALTERNATIVES,
 };
 
 struct abstract_pkg {
@@ -118,6 +119,24 @@  struct abstract_pkg {
 
 #include "pkg_depends.h"
 
+enum pkg_alternative_field {
+	PAF_PRIO,
+	PAF_PATH,
+	PAF_ALTPATH,
+	__PAF_MAX,
+};
+
+struct pkg_alternative {
+	int prio;
+	char *path;
+	char *altpath;
+};
+
+struct pkg_alternatives {
+	int nalts;
+	struct pkg_alternative **alts;
+};
+
 /* XXX: CLEANUP: I'd like to clean up pkg_t in several ways:
 
    The 3 version fields should go into a single version struct. (This
diff --git a/libopkg/pkg_alternatives.c b/libopkg/pkg_alternatives.c
new file mode 100644
index 0000000..66b64de
--- /dev/null
+++ b/libopkg/pkg_alternatives.c
@@ -0,0 +1,113 @@ 
+/* pkg_alternatives.c - the opkg package management system
+
+   Copyright (C) 2017 Yousong Zhou
+
+   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 <stdio.h>
+#include <sys/types.h>		/* stat */
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "libbb/libbb.h"
+#include "opkg_message.h"
+#include "pkg.h"
+#include "pkg_hash.h"
+#include "pkg_alternatives.h"
+#include "sprintf_alloc.h"
+
+static int pkg_alternatives_update_path(pkg_t *pkg, const pkg_vec_t *installed, const char *path)
+{
+	struct pkg_alternatives *pkg_alts;
+	struct pkg_alternative *the_alt = NULL;
+	pkg_t *the_pkg = pkg;
+	int i, j;
+	int r;
+	char *path_in_dest;
+
+	for (i = 0; i < installed->len; i++) {
+		pkg_t *pkg = installed->pkgs[i];
+		pkg_alts = pkg_get_ptr(pkg, PKG_ALTERNATIVES);
+		if (!pkg_alts)
+			continue;
+
+		for (j = 0; j < pkg_alts->nalts; j++) {
+			struct pkg_alternative *alt = pkg_alts->alts[j];
+
+			if (strcmp(path, alt->path))
+				continue;
+			if (!the_alt || the_alt->prio < alt->prio) {
+				the_pkg = pkg;
+				the_alt = alt;
+			}
+		}
+	}
+
+	/* path is assumed to be an absolute one */
+	sprintf_alloc(&path_in_dest, "%s%s", the_pkg->dest->root_dir, &path[1]);
+	if (!path_in_dest)
+		return -1;
+
+	if (the_alt) {
+		struct stat sb;
+
+		r = lstat(path_in_dest, &sb);
+		if (!r) {
+			char *realpath;
+
+			if (!S_ISLNK(sb.st_mode)) {
+				opkg_msg(ERROR, "%s exists but is not a symlink\n", path_in_dest);
+				r = -1;
+				goto out;
+			}
+			realpath = xreadlink(path_in_dest);
+			if (realpath && strcmp(realpath, the_alt->altpath))
+				unlink(path_in_dest);
+			free(realpath);
+		} else if (errno != ENOENT) {
+			goto out;
+		}
+		r = symlink(the_alt->altpath, path_in_dest);
+		if (r)
+			opkg_msg(INFO, "failed symlinking %s -> %s\n", path_in_dest, the_alt->altpath);
+	} else {
+		unlink(path_in_dest);
+		r = 0;
+	}
+
+out:
+	free(path_in_dest);
+	return r;
+}
+
+int pkg_alternatives_update(pkg_t * pkg)
+{
+	int r = 0;
+	int i;
+	struct pkg_alternatives *pkg_alts;
+	struct pkg_alternative *alt = NULL;
+	pkg_vec_t *installed;
+
+	pkg_alts = pkg_get_ptr(pkg, PKG_ALTERNATIVES);
+	if (!pkg_alts)
+		return 0;
+
+	installed = pkg_vec_alloc();
+	pkg_hash_fetch_all_installed(installed);
+	for (i = 0; i < pkg_alts->nalts; i++) {
+		alt = pkg_alts->alts[i];
+		r |= pkg_alternatives_update_path(pkg, installed, alt->path);
+	}
+	pkg_vec_free(installed);
+
+	return r;
+}
diff --git a/libopkg/pkg_alternatives.h b/libopkg/pkg_alternatives.h
new file mode 100644
index 0000000..25a5fba
--- /dev/null
+++ b/libopkg/pkg_alternatives.h
@@ -0,0 +1,23 @@ 
+/* pkg_alternatives.c - the opkg package management system
+
+   Copyright (C) 2017 Yousong Zhou
+
+   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.
+*/
+
+#ifndef OPKG_ALTERNATIVES_H
+#define OPKG_ALTERNATIVES_H
+
+#include "pkg.h"
+
+int pkg_alternatives_update(pkg_t * pkg);
+
+#endif
diff --git a/libopkg/pkg_parse.c b/libopkg/pkg_parse.c
index 121f147..d35770c 100644
--- a/libopkg/pkg_parse.c
+++ b/libopkg/pkg_parse.c
@@ -112,6 +112,83 @@  static char *parse_architecture(pkg_t *pkg, const char *str)
 	return pkg_set_architecture(pkg, s, e - s);
 }
 
+static void parse_alternatives(pkg_t *pkg, char *list)
+{
+	char *item, *tok;
+	struct pkg_alternatives *pkg_alts;
+	struct pkg_alternative **alts;
+	int nalts;
+
+	pkg_alts = pkg_get_ptr(pkg, PKG_ALTERNATIVES);
+	if (!pkg_alts) {
+		nalts = 0;
+		alts = NULL;
+	} else {
+		nalts = pkg_alts->nalts;
+		alts = pkg_alts->alts;
+	}
+
+	for (item = strtok_r(list, ",", &tok);
+			item;
+			item = strtok_r(NULL, ",", &tok)) {
+		enum pkg_alternative_field i;
+		char *val, *tok1;
+		/* the assignment was intended to quash the -Wmaybe-uninitialized warnings */
+		int prio = prio;
+		char *path = path, *altpath = altpath;
+
+		for (i = PAF_PRIO, val = strtok_r(item, ":", &tok1);
+				val && i < __PAF_MAX;
+				val = strtok_r(NULL, ":", &tok1), i++) {
+			switch (i) {
+				case PAF_PRIO:
+					prio = atoi(val);
+					break;
+				case PAF_PATH:
+					path = val;
+					break;
+				case PAF_ALTPATH:
+					altpath = val;
+					break;
+				default:
+					break;
+			}
+		}
+		if (!val && i == __PAF_MAX) {
+			char *_path, *_altpath;
+			struct pkg_alternative *alt;
+
+			/*
+			 * - path must be absolute
+			 * - altpath must be non-empty
+			 */
+			if (path[0] != '/' || !altpath[0])
+				continue;
+
+			alt = calloc_a(sizeof(*alt),
+					&_path, strlen(path) + 1,
+					&_altpath, strlen(altpath) + 1);
+			if (!alt)
+				continue;
+			strcpy(_path, path);
+			strcpy(_altpath, altpath);
+			alt->prio = prio;
+			alt->path = _path;
+			alt->altpath = _altpath;
+			alts = xrealloc(alts, sizeof(*alts) * (nalts + 1));
+			alts[nalts++] = alt;
+		}
+	}
+
+	if (nalts > 0) {
+		if (!pkg_alts)
+			pkg_alts = xmalloc(sizeof(*pkg_alts));
+		pkg_alts->nalts = nalts;
+		pkg_alts->alts = alts;
+		pkg_set_ptr(pkg, PKG_ALTERNATIVES, pkg_alts);
+	}
+}
+
 int pkg_parse_line(void *ptr, char *line, uint mask)
 {
 	pkg_t *pkg = (pkg_t *) ptr;
@@ -131,7 +208,9 @@  int pkg_parse_line(void *ptr, char *line, uint mask)
 
 	switch (*line) {
 	case 'A':
-		if ((mask & PFM_ARCHITECTURE) && is_field("Architecture", line))
+		if ((mask & PFM_ALTERNATIVES) && is_field("Alternatives", line))
+			parse_alternatives(pkg, line + strlen("Alternatives") + 1);
+		else if ((mask & PFM_ARCHITECTURE) && is_field("Architecture", line))
 			parse_architecture(pkg, line + strlen("Architecture") + 1);
 		else if ((mask & PFM_AUTO_INSTALLED)
 			   && is_field("Auto-Installed", line)) {
diff --git a/libopkg/pkg_parse.h b/libopkg/pkg_parse.h
index ac8d7c0..d1f901a 100644
--- a/libopkg/pkg_parse.h
+++ b/libopkg/pkg_parse.h
@@ -27,32 +27,33 @@  int pkg_parse_line(void *ptr, char *line, uint mask);
 #define EXCESSIVE_LINE_LEN	(4096 << 8)
 
 /* package field mask */
-#define PFM_ARCHITECTURE	(1 << 1)
-#define PFM_AUTO_INSTALLED	(1 << 2)
-#define PFM_CONFFILES		(1 << 3)
-#define PFM_CONFLICTS		(1 << 4)
-#define PFM_DESCRIPTION		(1 << 5)
-#define PFM_DEPENDS		(1 << 6)
-#define PFM_ESSENTIAL		(1 << 7)
-#define PFM_FILENAME		(1 << 8)
-#define PFM_INSTALLED_SIZE	(1 << 9)
-#define PFM_INSTALLED_TIME	(1 << 10)
-#define PFM_MD5SUM		(1 << 11)
-#define PFM_MAINTAINER		(1 << 12)
-#define PFM_PACKAGE		(1 << 13)
-#define PFM_PRIORITY		(1 << 14)
-#define PFM_PROVIDES		(1 << 15)
-#define PFM_PRE_DEPENDS		(1 << 16)
-#define PFM_RECOMMENDS		(1 << 17)
-#define PFM_REPLACES		(1 << 18)
-#define PFM_SECTION		(1 << 19)
-#define PFM_SHA256SUM		(1 << 20)
-#define PFM_SIZE		(1 << 21)
-#define PFM_SOURCE		(1 << 22)
-#define PFM_STATUS		(1 << 23)
-#define PFM_SUGGESTS		(1 << 24)
-#define PFM_TAGS		(1 << 25)
-#define PFM_VERSION		(1 << 26)
+#define PFM_ALTERNATIVES	(1 << 1)
+#define PFM_ARCHITECTURE	(1 << 2)
+#define PFM_AUTO_INSTALLED	(1 << 3)
+#define PFM_CONFFILES		(1 << 4)
+#define PFM_CONFLICTS		(1 << 5)
+#define PFM_DESCRIPTION		(1 << 6)
+#define PFM_DEPENDS		(1 << 7)
+#define PFM_ESSENTIAL		(1 << 8)
+#define PFM_FILENAME		(1 << 9)
+#define PFM_INSTALLED_SIZE	(1 << 10)
+#define PFM_INSTALLED_TIME	(1 << 11)
+#define PFM_MD5SUM		(1 << 12)
+#define PFM_MAINTAINER		(1 << 13)
+#define PFM_PACKAGE		(1 << 14)
+#define PFM_PRIORITY		(1 << 15)
+#define PFM_PROVIDES		(1 << 16)
+#define PFM_PRE_DEPENDS		(1 << 17)
+#define PFM_RECOMMENDS		(1 << 18)
+#define PFM_REPLACES		(1 << 19)
+#define PFM_SECTION		(1 << 20)
+#define PFM_SHA256SUM		(1 << 21)
+#define PFM_SIZE		(1 << 22)
+#define PFM_SOURCE		(1 << 23)
+#define PFM_STATUS		(1 << 24)
+#define PFM_SUGGESTS		(1 << 25)
+#define PFM_TAGS		(1 << 26)
+#define PFM_VERSION		(1 << 27)
 
 #define PFM_ALL	(~(uint)0)