Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Issue #406:
Matthew Seaman committed 13 years ago
commit a5e7de047b87049d6446d559947121265c75b8b6
parent 5fb753a
5 files changed +231 -6
modified libpkg/Makefile
@@ -26,6 +26,7 @@ SRCS= backup.c \
		pkg_manifest.c \
		pkg_ports.c \
		pkg_repo.c \
+
		pkg_status.c \
		pkg_version.c \
		pkgdb.c \
		rcscripts.c \
modified libpkg/pkg.h
@@ -37,6 +37,16 @@
#include <sys/sbuf.h>
#include <openssl/pem.h>

+
/* The expected name of the pkg(1) binary executable. */
+
#ifndef PKG_EXEC_NAME
+
#define PKG_EXEC_NAME	"pkg"
+
#endif
+

+
/* The expected name of the pkg-static(1) binary */
+
#ifndef PKG_STATIC_NAME
+
#define PKG_STATIC_NAME	"pkg-static"
+
#endif
+

#define PKGVERSION "1.1.a1"

/* PORTVERSION equivalent for proper pkg-static->ports-mgmt/pkg
@@ -69,6 +79,22 @@ struct pkg_config_value;

struct pkg_plugin;

+
/**
+
 * The system-wide pkg(1) status: ie. is it a) installed or otherwise
+
 * available on the sysem, b) database (local.sqlite) initialised and
+
 * c) has at least one package installed (which should be pkg
+
 * itself). PKG_STATUS_UNINSTALLED logically cannot be returned by
+
 * pkg(1) itself, but it can be useful for the pkg bootstrapper
+
 * /usr/bin/pkg or for applications that link against libpkg.so
+
 */
+

+
typedef enum {
+
	PKG_STATUS_ACTIVE = 0,	/* pkg in use */
+
	PKG_STATUS_NOPACKAGES,	/* local.sqlite empty */
+
	PKG_STATUS_NODB,	/* local.sqlite not found, unreadable or not initialised */
+
	PKG_STATUS_UNINSTALLED,	/* pkg not argv[0] or not on $PATH */
+
} pkg_status_t;
+

typedef enum {
	/**
	 * The license logic is OR (dual in the ports)
@@ -383,6 +409,13 @@ typedef enum {
} pkg_error_t;

/**
+
 * test if pkg is installed and activated.
+
 * @param count  If all the tests pass, and count is non-NULL,
+
 * write the number of installed packages into *count
+
 */
+
pkg_status_t pkg_status(int *count);
+

+
/**
 * Allocate a new pkg.
 * Allocated pkg must be deallocated by pkg_free().
 */
added libpkg/pkg_status.c
@@ -0,0 +1,122 @@
+
/*
+
 * Copyright (c) 2012 Matthew Seaman <matthew@FreeBSD.org>
+
 * All rights reserved.
+
 * 
+
 * Redistribution and use in source and binary forms, with or without
+
 * modification, are permitted provided that the following conditions
+
 * are met:
+
 * 1. Redistributions of source code must retain the above copyright
+
 *    notice, this list of conditions and the following disclaimer
+
 *    in this position and unchanged.
+
 * 2. Redistributions in binary form must reproduce the above copyright
+
 *    notice, this list of conditions and the following disclaimer in the
+
 *    documentation and/or other materials provided with the distribution.
+
 * 
+
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 */
+

+
#include <sys/param.h>
+

+
#include <stdio.h>
+
#include <stdlib.h>
+
#include <string.h>
+
#include <unistd.h>
+

+
#include <sqlite3.h>
+

+
#include "pkg.h"
+

+

+
#define _LOCALBASE	"/usr/local"
+

+
static bool is_exec_at_localbase(const char *progname);
+

+
pkg_status_t
+
pkg_status(int *count)
+
{
+
	const char	*progname;
+
	const char	*dbdir;
+
	char		 dbpath[MAXPATHLEN];
+
	int		 numpkgs;
+
	sqlite3		*db = NULL;
+
	sqlite3_stmt	*stmt = NULL;
+
	const char      *sql = "SELECT COUNT(*) FROM packages";
+
	bool		 dbsuccess;
+

+
	/* Is this executable called pkg, or does pkg exist at
+
	   $LOCALBASE/sbin/pkg.  Ditto: pkg-static. Portability:
+
	   assumes setprogname() has been called */
+

+
	progname = getprogname();
+
	if (progname == NULL)
+
		return (PKG_STATUS_UNINSTALLED);
+

+
	if (strcmp(progname, PKG_EXEC_NAME) != 0   &&
+
	    strcmp(progname, PKG_STATIC_NAME) != 0 &&
+
	    !is_exec_at_localbase(PKG_EXEC_NAME)   &&
+
	    !is_exec_at_localbase(PKG_STATIC_NAME))
+
		return (PKG_STATUS_UNINSTALLED);
+

+
	/* Does the local.sqlite pkg database exist, and can we open
+
	   it for reading? */
+

+
	if (pkg_config_string(PKG_CONFIG_DBDIR, &dbdir) != EPKG_OK)
+
		return (PKG_STATUS_NODB);
+

+
	snprintf(dbpath, sizeof(dbpath), "%s/local.sqlite", dbdir);
+

+
	if (eaccess(dbpath, R_OK) == -1)
+
		return (PKG_STATUS_NODB);
+
	
+
	/* Try opening the DB and preparing and running a simple query. */
+

+
	dbsuccess = (sqlite3_initialize() == SQLITE_OK);
+
	if (dbsuccess) {
+
		dbsuccess = (sqlite3_open_v2(dbpath, &db, SQLITE_OPEN_READONLY, NULL) == SQLITE_OK);
+
		if (dbsuccess) {
+
			dbsuccess = (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK);
+
			if (dbsuccess) {
+
				dbsuccess = (sqlite3_step(stmt) == SQLITE_ROW);
+
				if (dbsuccess) {
+
					numpkgs = sqlite3_column_int(stmt, 0);
+
				}
+
				sqlite3_finalize(stmt);
+
			}
+
			sqlite3_close(db);
+
		}
+
		sqlite3_shutdown();
+
	}
+

+
	if (!dbsuccess)
+
		return (PKG_STATUS_NODB);
+

+
	/* Save result, if requested */
+
	if (count != NULL)
+
		*count = numpkgs;
+

+
	return (numpkgs == 0 ? PKG_STATUS_NOPACKAGES : PKG_STATUS_ACTIVE);
+
}
+

+
static bool
+
is_exec_at_localbase(const char *progname)
+
{
+
	char	pkgpath[MAXPATHLEN];
+
	bool	result = true;
+

+
	snprintf(pkgpath, sizeof(pkgpath), "%s/sbin/%s",
+
		 getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE,
+
		 progname);
+
	if (access(pkgpath, X_OK) == -1)
+
		result = false;
+

+
	return (result);
+
}
modified pkg/main.c
@@ -126,9 +126,9 @@ usage(void)
	bool plugins_enabled = false;

#ifndef NO_LIBJAIL
-
 	fprintf(stderr, "usage: pkg [-v] [-d] [-j <jail name or id>|-c <chroot path>] <command> [<args>]\n\n");
+
 	fprintf(stderr, "usage: pkg [-v] [-d] [-n] [-j <jail name or id>|-c <chroot path>] <command> [<args>]\n\n");
#else
-
	fprintf(stderr, "usage: pkg [-v] [-d] [-c <chroot path>] <command> [<args>]\n\n");
+
	fprintf(stderr, "usage: pkg [-v] [-d] [-n] [-c <chroot path>] <command> [<args>]\n\n");
#endif
	fprintf(stderr, "Global options supported:\n");
	fprintf(stderr, "\t%-15s%s\n", "-d", "Increment debug level");
@@ -137,6 +137,7 @@ usage(void)
#endif
	fprintf(stderr, "\t%-15s%s\n", "-c", "Execute pkg(1) inside a chroot(8)");
	fprintf(stderr, "\t%-15s%s\n\n", "-v", "Display pkg(1) version");
+
	fprintf(stderr, "\t%-15s%s\n\n", "-n", "Test if pkg(1) is activated and avoid auto-activation");
	fprintf(stderr, "Commands supported:\n");

	for (unsigned int i = 0; i < cmd_len; i++)
@@ -228,6 +229,7 @@ main(int argc, char **argv)
	const char *buf = NULL;
	bool b, plugins_enabled = false;
	bool show_commands = false;
+
	bool activation_test = false;
	struct pkg_config_kv *kv = NULL;
	struct pkg_config_value *list = NULL;
	struct plugcmd *c;
@@ -243,9 +245,9 @@ main(int argc, char **argv)
		usage();

#ifndef NO_LIBJAIL
-
	while ((ch = getopt(argc, argv, "dj:c:lvq")) != -1) {
+
	while ((ch = getopt(argc, argv, "dj:c:lnvq")) != -1) {
#else
-
	while ((ch = getopt(argc, argv, "d:c:lvq")) != -1) {
+
	while ((ch = getopt(argc, argv, "d:c:lnvq")) != -1) {
#endif
		switch (ch) {
		case 'd':
@@ -262,6 +264,9 @@ main(int argc, char **argv)
		case 'l':
			show_commands = true;
			break;
+
		case 'n':
+
			activation_test = true;
+
			break;
		case 'v':
			version++;
			break;
@@ -280,7 +285,7 @@ main(int argc, char **argv)
		show_command_names();
		exit(EX_OK);
	}
-
	if (argc == 0 && version == 0)
+
	if (argc == 0 && version == 0 && !activation_test)
		usage();

	umask(022);
@@ -411,6 +416,44 @@ main(int argc, char **argv)
		exit(EX_OK);
	}

+
	if (activation_test) {
+
		int	count;
+

+
		/* Test to see if pkg(1) has been activated.  Exit
+
		   with an error code if not.  Can be combined with -c
+
		   and -j to test if pkg is activated in chroot or
+
		   jail. If there are no other arguments, and pkg(1)
+
		   has been activated, show how many packages have
+
		   been installed. */
+

+
		switch (pkg_status(&count)) {
+
		case PKG_STATUS_UNINSTALLED: /* This case shouldn't ever happen... */
+
			pkg_shutdown();
+
			pkg_plugins_shutdown();
+
			errx(EX_UNAVAILABLE, "can't execute " PKG_EXEC_NAME " or " PKG_STATIC_NAME "\n");
+
			/* NOTREACHED */
+
		case PKG_STATUS_NODB:
+
			pkg_shutdown();
+
			pkg_plugins_shutdown();
+
			errx(EX_UNAVAILABLE, "package database non-existent\n");
+
			/* NOTREACHED */
+
		case PKG_STATUS_NOPACKAGES:
+
			pkg_shutdown();
+
			pkg_plugins_shutdown();
+
			errx(EX_UNAVAILABLE, "no packages registered\n");
+
			/* NOTREACHED */
+
		case PKG_STATUS_ACTIVE:
+
			if (argc == 0) {
+
				warnx("%d packages installed\n", count);
+
				pkg_shutdown();
+
				pkg_plugins_shutdown();
+
				exit(EX_OK);
+
			}
+
			break;
+
		}
+
			
+
	}
+

	len = strlen(argv[0]);
	for (i = 0; i < cmd_len; i++) {
		if (strncmp(argv[0], cmd[i].name, len) == 0) {
modified pkg/pkg.8
@@ -15,7 +15,7 @@
.\"     @(#)pkg.8
.\" $FreeBSD$
.\"
-
.Dd October 19, 2012
+
.Dd December 21, 2012
.Dt PKG 8
.Os
.\" ---------------------------------------------------------------------------
@@ -29,6 +29,7 @@
.Op Fl v
.Op Fl d
.Op Fl l
+
.Op Fl n
.Op Fl j Ao jail name or id Ac | Fl c Ao chroot path Ac
.Ao command Ac Ao Ar flags Ac
.\" ---------------------------------------------------------------------------
@@ -64,6 +65,31 @@ option takes precedence over
but
.Fl l
will override any other command line arguments.
+
.It Fl n
+
Sanity check mode.
+
Prevent
+
.Nm
+
from automatically creating or initializing the sqlite database in
+
.Fa /var/db/pkg/local.sqlite
+
if it does not already exist.
+
.Pp
+
Prevent
+
.Nm
+
from performing any actions if no packages are currently installed, on
+
the basis that a correctly initialised system using
+
.Nm
+
will always have at least the
+
.Nm
+
package itself registered.
+
.Pp
+
If used without any other arguments,
+
.Nm Fl n
+
will run the sanity tests and print out a short message showing how
+
many packages are currently installed.
+
The exit status should be a reliable indication of whether the system
+
is configured to use
+
.Nm
+
as its package management system or not.
.It Fl j Ao jail name or id Ac
.Nm
will execute in the given