Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Add support for scripts in lua
Baptiste Daroussin committed 6 years ago
commit 485bdd9dd7a227b11a4f5782dcfeb74a4434ad73
parent 4521cfb
16 files changed +546 -14
modified libpkg/Makefile.autosetup
@@ -13,7 +13,9 @@ SRCS= backup.c \
	pkg_manifest.c \
	pkg_repo_update.c \
	pkgdb_iterator.c \
-
	scripts.c diff.c \
+
	lua_scripts.c \
+
	scripts.c \
+
	diff.c \
	packing.c \
	pkg_config.c \
	pkg_event.c \
modified libpkg/libpkg.ver
@@ -5,6 +5,7 @@ global:
	pkg_add_from_remote;
	pkg_add_port;
	pkg_adddep;
+
	pkg_addluascript_fileat;
	pkg_addrdep;
	pkg_addscript_fileat;
	pkg_analyse_files;
added libpkg/lua_scripts.c
@@ -0,0 +1,180 @@
+
/*-
+
 * Copyright (c) 2019 Baptiste Daroussin <bapt@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 "pkg_config.h"
+

+
#ifdef HAVE_SYS_PROCCTL_H
+
#include <sys/procctl.h>
+
#endif
+

+
#include <errno.h>
+
#include <utstring.h>
+
#include <lauxlib.h>
+
#include <lualib.h>
+

+
#include "pkg.h"
+
#include "private/pkg.h"
+
#include "private/event.h"
+

+
extern char **environ;
+

+
static lua_CFunction
+
stack_dump(lua_State *L)
+
{
+
	int i;
+
	int top = lua_gettop(L);
+
	UT_string *stack;
+

+
	utstring_new(stack);
+

+
	utstring_printf(stack, "\nLua Stack\n---------\n");
+
	utstring_printf(stack, "\tType   Data\n\t-----------\n" );
+

+
	for (i = 1; i <= top; i++) {  /* repeat for each level */
+
		int t = lua_type(L, i);
+
		utstring_printf(stack, "%i", i);
+
		switch (t) {
+
		case LUA_TSTRING:  /* strings */
+
			utstring_printf(stack, "\tString: `%s'\n", lua_tostring(L, i));
+
			break;
+
		case LUA_TBOOLEAN:  /* booleans */
+
			utstring_printf(stack, "\tBoolean: %s", lua_toboolean(L, i) ? "\ttrue\n" : "\tfalse\n");
+
			break;
+
		case LUA_TNUMBER:  /* numbers */
+
			utstring_printf(stack, "\tNumber: %g\n", lua_tonumber(L, i));
+
			break;
+
		default:  /* other values */
+
			utstring_printf(stack, "\tOther: %s\n", lua_typename(L, t));
+
			break;
+
		}
+
	}
+
	pkg_emit_error("%s\n", utstring_body(stack));
+
	utstring_free(stack);
+

+
	return (0);
+
}
+

+
int
+
pkg_lua_script_run(struct pkg * const pkg, pkg_lua_script type)
+
{
+
	int ret = EPKG_OK;
+
	struct pkg_lua_script *lscript;
+
#ifdef PROC_REAP_KILL
+
	bool do_reap;
+
	pid_t mypid;
+
	struct procctl_reaper_status info;
+
	struct procctl_reaper_kill killemall;
+
#endif
+

+
	if (pkg->lua_scripts[type] == NULL)
+
		return (EPKG_OK);
+

+
	if (!pkg_object_bool(pkg_config_get("RUN_SCRIPTS"))) {
+
		return (EPKG_OK);
+
	}
+

+
#ifdef PROC_REAP_KILL
+
	mypid = getpid();
+
	do_reap = procctl(P_PID, mypid, PROC_REAP_ACQUIRE, NULL) == 0;
+
#endif
+

+

+
	LL_FOREACH(pkg->lua_scripts[type], lscript) {
+
		lua_State *L = luaL_newstate();
+
		luaL_openlibs( L );
+
		lua_atpanic(L, (lua_CFunction)stack_dump ),
+

+
		luaL_dostring(L, lscript->script);
+

+
		lua_close(L);
+
	}
+

+
			/*setenv("PKG_PREFIX", pkg->prefix, 1);
+
			if (ctx.pkg_rootdir == NULL)
+
				ctx.pkg_rootdir = "/";
+
			setenv("PKG_ROOTDIR", ctx.pkg_rootdir, 1); */
+

+
/*			pkg_debug(3, "Scripts: executing\n--- BEGIN ---\n%s\nScripts: --- END ---", utstring_body(script_cmd)); */
+

+
#ifdef PROC_REAP_KILL
+
	/*
+
	 * If the prior PROCCTL_REAP_ACQUIRE call failed, the kernel
+
	 * probably doesn't support this, so don't try.
+
	 */
+
	if (!do_reap)
+
		return (ret);
+

+
	procctl(P_PID, mypid, PROC_REAP_STATUS, &info);
+
	if (info.rs_children != 0) {
+
		killemall.rk_sig = SIGKILL;
+
		killemall.rk_flags = 0;
+
		if (procctl(P_PID, mypid, PROC_REAP_KILL, &killemall) != 0) {
+
			pkg_errno("%s", "Fail to kill all processes");
+
		}
+
	}
+
	procctl(P_PID, mypid, PROC_REAP_RELEASE, NULL);
+
#endif
+

+
	return (ret);
+
}
+

+
ucl_object_t *
+
pkg_lua_script_to_ucl(struct pkg_lua_script *scripts)
+
{
+
	struct pkg_lua_script *script;
+
	ucl_object_t *array;
+
	ucl_object_t *obj;
+

+
	array = ucl_object_typed_new(UCL_ARRAY);
+
	LL_FOREACH(scripts, script) {
+
		obj = ucl_object_typed_new(UCL_OBJECT);
+

+
		ucl_array_append(array, ucl_object_fromstring_common(script->script,
+
				strlen(script->script), UCL_STRING_RAW|UCL_STRING_TRIM));
+
	}
+

+
	return (array);
+
}
+

+
int
+
pkg_lua_script_from_ucl(struct pkg *pkg, const ucl_object_t *obj, pkg_lua_script type)
+
{
+
	struct pkg_lua_script *lscript;
+
	const ucl_object_t *cur;
+
	ucl_object_iter_t it = NULL;
+

+
	while ((cur = ucl_iterate_object(obj, &it, true))) {
+
		if (ucl_object_type(cur) != UCL_STRING) {
+
			pkg_emit_error("lua scripts be strings");
+
			return (EPKG_FATAL);
+
		}
+
		lscript = xcalloc(1, sizeof(*lscript));
+
		lscript->script = xstrdup(ucl_object_tostring(cur));
+
		DL_APPEND(pkg->lua_scripts[type], lscript);
+
	}
+
	return (EPKG_OK);
+
}
+

modified libpkg/pkg.c
@@ -826,6 +826,58 @@ pkg_addscript(struct pkg *pkg, const char *data, pkg_script type)
}

int
+
pkg_add_lua_script(struct pkg *pkg, const char *data, pkg_lua_script type)
+
{
+
	assert(pkg != NULL);
+
	struct pkg_lua_script *lua;
+

+
	if (type >= PKG_LUA_UNKNOWN)
+
		return (EPKG_FATAL);
+

+
	lua = xcalloc(1, sizeof(*lua));
+
	lua->script = xstrdup(data);
+
	DL_APPEND(pkg->lua_scripts[type], lua);
+

+
	return (EPKG_OK);
+
}
+

+
int
+
pkg_addluascript_fileat(int fd, struct pkg *pkg, const char *filename)
+
{
+
	char *data;
+
	pkg_lua_script type;
+
	int ret = EPKG_OK;
+
	off_t sz = 0;
+

+
	assert(pkg != NULL);
+
	assert(filename != NULL);
+

+
	pkg_debug(1, "Adding script from: '%s'", filename);
+

+
	if ((ret = file_to_bufferat(fd, filename, &data, &sz)) != EPKG_OK)
+
		return (ret);
+

+
	if (strcmp(filename, "pkg-pre-install.lua") == 0) {
+
		type = PKG_LUA_PRE_INSTALL;
+
	} else if (strcmp(filename, "pkg-post-install.lua") == 0) {
+
		type = PKG_LUA_POST_INSTALL;
+
	} else if (strcmp(filename, "pkg-pre-deinstall") == 0) {
+
		type = PKG_LUA_PRE_DEINSTALL;
+
	} else if (strcmp(filename, "pkg-post-deinstall") == 0) {
+
		type = PKG_LUA_POST_DEINSTALL;
+
	} else {
+
		pkg_emit_error("unknown lua script '%s'", filename);
+
		ret = EPKG_FATAL;
+
		goto cleanup;
+
	}
+

+
	ret = pkg_add_lua_script(pkg, data, type);
+
cleanup:
+
	free(data);
+
	return (ret);
+
}
+

+
int
pkg_addscript_fileat(int fd, struct pkg *pkg, const char *filename)
{
	char *data;
modified libpkg/pkg.h.in
@@ -348,6 +348,17 @@ typedef enum {
	PKG_SCRIPT_UNKNOWN
} pkg_script;

+
/**
+
 * Determine the type of a pkg_lua_script.
+
 */
+
typedef enum {
+
	PKG_LUA_PRE_INSTALL = 0,
+
	PKG_LUA_POST_INSTALL,
+
	PKG_LUA_PRE_DEINSTALL,
+
	PKG_LUA_POST_DEINSTALL,
+
	PKG_LUA_UNKNOWN
+
} pkg_lua_script;
+

typedef enum _pkg_jobs_t {
	PKG_JOBS_INSTALL,
	PKG_JOBS_DEINSTALL,
@@ -723,6 +734,7 @@ int pkg_addrdep(struct pkg *pkg, const char *name, const char *origin, const
 * with the correct type.
 */
int pkg_addscript_fileat(int fd, struct pkg *pkg, const char *path);
+
int pkg_addluascript_fileat(int fd, struct pkg *pkg, const char *path);

/**
 * Parse a manifest and set the attributes of pkg accordingly.
@@ -952,6 +964,7 @@ int pkgdb_delete_annotation(struct pkgdb *db, struct pkg *pkg,
#define PKG_LOAD_CONFLICTS		(1U << 13)
#define PKG_LOAD_PROVIDES		(1U << 14)
#define PKG_LOAD_REQUIRES		(1U << 15)
+
#define PKG_LOAD_LUA_SCRIPTS		(1u << 16)
/* Make sure new PKG_LOAD don't conflict with PKG_CONTAINS_* */

/**
modified libpkg/pkg_add.c
@@ -968,10 +968,16 @@ pkg_add_cleanup_old(struct pkgdb *db, struct pkg *old, struct pkg *new, int flag
			ret = pkg_script_run(old, PKG_SCRIPT_PRE_UPGRADE);
		else
			ret = pkg_script_run(old, PKG_SCRIPT_PRE_DEINSTALL);
-
		if (ret != EPKG_OK && pkg_object_bool(pkg_config_get("DEVELOPER_MODE")))
+
		if (ret != EPKG_OK && pkg_object_bool(pkg_config_get("DEVELOPER_MODE"))) {
			return (ret);
-
		else
-
			ret = EPKG_OK;
+
		} else {
+
			ret = pkg_lua_script_run(old, PKG_LUA_PRE_DEINSTALL);
+
			if (ret != EPKG_OK && pkg_object_bool(pkg_config_get("DEVELOPER_MODE"))) {
+
				return (ret);
+
			} else {
+
				ret = EPKG_OK;
+
			}
+
		}
	}

	/* Now remove files that no longer exist in the new package */
@@ -1101,9 +1107,12 @@ pkg_add_common(struct pkgdb *db, const char *path, unsigned flags,
	/*
	 * Execute pre-install scripts
	 */
-
	if ((flags & (PKG_ADD_NOSCRIPT | PKG_ADD_USE_UPGRADE_SCRIPTS)) == 0)
+
	if ((flags & (PKG_ADD_NOSCRIPT | PKG_ADD_USE_UPGRADE_SCRIPTS)) == 0) {
		if ((retcode = pkg_script_run(pkg, PKG_SCRIPT_PRE_INSTALL)) != EPKG_OK)
			goto cleanup;
+
		if ((retcode = pkg_lua_script_run(pkg, PKG_LUA_PRE_INSTALL)) != EPKG_OK)
+
			goto cleanup;
+
	}


	/* add the user and group if necessary */
@@ -1151,6 +1160,7 @@ cleanup_reg:
			pkg_script_run(pkg, PKG_SCRIPT_POST_UPGRADE);
		else
			pkg_script_run(pkg, PKG_SCRIPT_POST_INSTALL);
+
		pkg_lua_script_run(pkg, PKG_LUA_POST_INSTALL);
	}

	/*
@@ -1244,7 +1254,7 @@ pkg_add_upgrade(struct pkgdb *db, const char *path, unsigned flags,
    struct pkg *rp, struct pkg *lp)
{
	if (pkgdb_ensure_loaded(db, lp,
-
	    PKG_LOAD_FILES|PKG_LOAD_SCRIPTS|PKG_LOAD_DIRS) != EPKG_OK)
+
	    PKG_LOAD_FILES|PKG_LOAD_SCRIPTS|PKG_LOAD_DIRS|PKG_LOAD_LUA_SCRIPTS) != EPKG_OK)
		return (EPKG_FATAL);

	return pkg_add_common(db, path, flags, keys, location, rp, lp);
modified libpkg/pkg_create.c
@@ -219,6 +219,14 @@ static const char * const scripts[] = {
	NULL
};

+
static const char * const lua_scripts[] = {
+
	"pkg-pre-install.lua",
+
	"pkg-post-install.lua",
+
	"pkg-pre-deinstall.lua",
+
	"pkg-post-deinstall.lua",
+
	NULL
+
};
+


/* The "no concessions to old pkg_tools" variant: just get everything
 * from the manifest */
@@ -363,6 +371,11 @@ pkg_load_metadata(struct pkg *pkg, const char *mfile, const char *md_dir,
			pkg_addscript_fileat(mfd, pkg, scripts[i]);
	}

+
	for (i = 0; lua_scripts[i] != NULL; i++) {
+
		if (faccessat(mfd, lua_scripts[i], F_OK, 0) == 0)
+
			pkg_addluascript_fileat(mfd, pkg, lua_scripts[i]);
+
	}
+

	if (plist != NULL &&
	    ports_parse_plist(pkg, plist, rootdir) != EPKG_OK) {
		ret = EPKG_FATAL;
@@ -473,7 +486,7 @@ pkg_create_installed(const char *outdir, pkg_formats format, struct pkg *pkg)

	unsigned	 required_flags = PKG_LOAD_DEPS | PKG_LOAD_FILES |
		PKG_LOAD_CATEGORIES | PKG_LOAD_DIRS | PKG_LOAD_SCRIPTS |
-
		PKG_LOAD_OPTIONS | PKG_LOAD_LICENSES ;
+
		PKG_LOAD_OPTIONS | PKG_LOAD_LICENSES | PKG_LOAD_LUA_SCRIPTS;

	assert(pkg->type == PKG_INSTALLED || pkg->type == PKG_OLD_FILE);

modified libpkg/pkg_delete.c
@@ -62,7 +62,7 @@ pkg_delete(struct pkg *pkg, struct pkgdb *db, unsigned flags)
	int		 ret;
	bool		 handle_rc = false;
	const unsigned load_flags = PKG_LOAD_RDEPS|PKG_LOAD_FILES|PKG_LOAD_DIRS|
-
					PKG_LOAD_SCRIPTS|PKG_LOAD_ANNOTATIONS;
+
					PKG_LOAD_SCRIPTS|PKG_LOAD_ANNOTATIONS|PKG_LOAD_LUA_SCRIPTS;

	assert(pkg != NULL);
	assert(db != NULL);
@@ -99,14 +99,19 @@ pkg_delete(struct pkg *pkg, struct pkgdb *db, unsigned flags)
			if (ret != EPKG_OK && pkg_object_bool(pkg_config_get("DEVELOPER_MODE")))
				return (ret);
		}
+
		ret = pkg_lua_script_run(pkg, PKG_LUA_PRE_DEINSTALL);
+
		if (ret != EPKG_OK && pkg_object_bool(pkg_config_get("DEVELOPER_MODE")))
+
			return (ret);
	}

	if ((ret = pkg_delete_files(pkg, flags & PKG_DELETE_FORCE ? 1 : 0))
            != EPKG_OK)
		return (ret);

-
	if ((flags & (PKG_DELETE_NOSCRIPT | PKG_DELETE_UPGRADE)) == 0)
+
	if ((flags & (PKG_DELETE_NOSCRIPT | PKG_DELETE_UPGRADE)) == 0) {
		pkg_script_run(pkg, PKG_SCRIPT_POST_DEINSTALL);
+
		pkg_lua_script_run(pkg, PKG_LUA_POST_DEINSTALL);
+
	}

	ret = pkg_delete_dirs(db, pkg, NULL);
	if (ret != EPKG_OK)
modified libpkg/pkg_manifest.c
@@ -60,6 +60,7 @@
#define PKG_CONFLICTS		-17
#define PKG_PROVIDES		-18
#define PKG_REQUIRES		-19
+
#define PKG_LUA_SCRIPTS		-20

#define PKG_MESSAGE_LEGACY	1
#define PKG_MESSAGE_NEW 2
@@ -140,6 +141,9 @@ static struct pkg_manifest_key {
	{ "licenses",            PKG_LICENSES,
			TYPE_SHIFT(UCL_ARRAY),  pkg_array},

+
	{ "lua_scripts",         PKG_LUA_SCRIPTS,
+
			TYPE_SHIFT(UCL_OBJECT), pkg_obj},
+

	{ "maintainer",          offsetof(struct pkg, maintainer),
			TYPE_SHIFT(UCL_STRING), pkg_string},

@@ -311,6 +315,20 @@ urldecode(const char *src, UT_string **dest)
}

static int
+
lua_script_type_str(const char *str)
+
{
+
	if (strcmp(str, "pre-install") == 0)
+
		return (PKG_LUA_PRE_INSTALL);
+
	if (strcmp(str, "post-install") == 0)
+
		return (PKG_LUA_POST_INSTALL);
+
	if (strcmp(str, "pre-deinstall") == 0)
+
		return (PKG_LUA_PRE_DEINSTALL);
+
	if (strcmp(str, "post-deinstall") == 0)
+
		return (PKG_LUA_POST_DEINSTALL);
+
	return (PKG_LUA_UNKNOWN);
+
}
+

+
static int
script_type_str(const char *str)
{
	if (strcmp(str, "pre-install") == 0)
@@ -495,6 +513,7 @@ pkg_obj(struct pkg *pkg, const ucl_object_t *obj, uint32_t attr)
	const ucl_object_t *cur;
	ucl_object_iter_t it = NULL;
	pkg_script script_type;
+
	pkg_lua_script lua_script_type;
	const char *key, *buf;
	size_t len;

@@ -585,6 +604,20 @@ pkg_obj(struct pkg *pkg, const ucl_object_t *obj, uint32_t attr)
				pkg_addscript(pkg, utstring_body(tmp), script_type);
			}
			break;
+
		case PKG_LUA_SCRIPTS:
+
			if (cur->type != UCL_ARRAY) {
+
				pkg_emit_error("Skipping malformed dependency %s",
+
				    key);
+
				break;
+
			}
+
			lua_script_type = lua_script_type_str(key);
+
			if (script_type == PKG_LUA_UNKNOWN) {
+
				pkg_emit_error("Skipping unknown script "
+
				    "type: %s", key);
+
				break;
+
			}
+
			pkg_lua_script_from_ucl(pkg, cur, lua_script_type);
+
			break;
		case PKG_ANNOTATIONS:
			if (cur->type != UCL_STRING)
				pkg_emit_error("Skipping malformed annotation %s",
@@ -1249,6 +1282,34 @@ pkg_emit_object(struct pkg *pkg, short flags)
		}
		if (map)
			ucl_object_insert_key(top, map, "scripts", 7, false);
+

+
		pkg_debug(4, "Emitting lua scripts");
+
		map = NULL;
+
		for (i = 0; i < PKG_NUM_LUA_SCRIPTS; i++) {
+
			if (pkg->lua_scripts[i] == NULL)
+
				continue;
+
			switch(i) {
+
			case PKG_LUA_PRE_INSTALL:
+
				script_types = "pre-install";
+
				break;
+
			case PKG_LUA_POST_INSTALL:
+
				script_types = "post-install";
+
				break;
+
			case PKG_LUA_PRE_DEINSTALL:
+
				script_types = "pre-deinstall";
+
				break;
+
			case PKG_LUA_POST_DEINSTALL:
+
				script_types = "post-deinstall";
+
				break;
+
			}
+
			if (map == NULL)
+
				map = ucl_object_typed_new(UCL_OBJECT);
+
			ucl_object_insert_key(map,
+
			    pkg_lua_script_to_ucl(pkg->lua_scripts[i]),
+
				    script_types, 0, true);
+
		}
+
		if (map)
+
			ucl_object_insert_key(top, map, "lua_scripts", 11, false);
	}

	pkg_debug(4, "Emitting message");
modified libpkg/pkg_ports.c
@@ -1310,6 +1310,7 @@ pkg_add_port(struct pkgdb *db, struct pkg *pkg, const char *input_path,
	if (!testing) {
		/* Execute pre-install scripts */
		pkg_script_run(pkg, PKG_SCRIPT_PRE_INSTALL);
+
		pkg_lua_script_run(pkg, PKG_LUA_PRE_INSTALL);

		if (input_path != NULL) {
			pkg_register_cleanup_callback(pkg_rollback_cb, pkg);
@@ -1323,6 +1324,7 @@ pkg_add_port(struct pkgdb *db, struct pkg *pkg, const char *input_path,

		/* Execute post-install scripts */
		pkg_script_run(pkg, PKG_SCRIPT_POST_INSTALL);
+
		pkg_lua_script_run(pkg, PKG_LUA_POST_INSTALL);
	}

	if (rc == EPKG_OK) {
modified libpkg/pkgdb.c
@@ -88,7 +88,7 @@
*/

#define DB_SCHEMA_MAJOR	0
-
#define DB_SCHEMA_MINOR	34
+
#define DB_SCHEMA_MINOR	35

#define DBVERSION (DB_SCHEMA_MAJOR * 1000 + DB_SCHEMA_MINOR)

@@ -97,6 +97,7 @@ static int prstmt_initialize(struct pkgdb *db);
/* static int run_prstmt(sql_prstmt_index s, ...); */
static void prstmt_finalize(struct pkgdb *db);
static int pkgdb_insert_scripts(struct pkg *pkg, int64_t package_id, sqlite3 *s);
+
static int pkgdb_insert_lua_scripts(struct pkg *pkg, int64_t package_id, sqlite3 *s);


extern int sqlite3_shell(int, char**);
@@ -690,6 +691,52 @@ pkgdb_init(sqlite3 *sdb)
	    "  ON DELETE RESTRICT ON UPDATE RESTRICT,"
	    "UNIQUE(package_id, require_id)"
	");"
+
	"CREATE TABLE lua_script("
+
	"    lua_script_id INTEGER PRIMARY KEY,"
+
	"    lua_script TEXT NOT NULL UNIQUE"
+
	");"
+
	"CREATE TABLE pkg_lua_script ("
+
		"package_id INTEGER NOT NULL REFERENCES packages(id)"
+
		"  ON DELETE CASCADE ON UPDATE CASCADE,"
+
		"lua_script_id INTEGER NOT NULL REFERENCES lua_script(lua_script_id)"
+
		"  ON DELETE RESTRICT ON UPDATE RESTRICT,"
+
		"type INTEGER,"
+
		"UNIQUE(package_id, lua_script_id)"
+
	");"
+
	"CREATE VIEW lua_scripts AS "
+
		"SELECT package_id, lua_script, type "
+
		"FROM pkg_lua_script JOIN lua_script USING(lua_script_id);"
+
	"CREATE TRIGGER lua_script_update "
+
		"INSTEAD OF UPDATE ON lua_scripts "
+
	"FOR EACH ROW BEGIN "
+
		"UPDATE pkg_lua_script "
+
		"SET type = new.type "
+
		"WHERE package_id = old.package_id AND "
+
		"lua_script_id = (SELECT lua_script_id FROM lua_script "
+
			"WHERE lua_script = old.lua_script );"
+
	"END;"
+
	"CREATE TRIGGER lua_script_insert "
+
		"INSTEAD OF INSERT ON lua_scripts "
+
	"FOR EACH ROW BEGIN "
+
		"INSERT OR IGNORE INTO lua_script(lua_script) "
+
		"VALUES(new.lua_script);"
+
		"INSERT INTO pkg_lua_script(package_id, lua_script_id, type) "
+
		"VALUES (new.package_id, "
+
			"(SELECT lua_script_id FROM lua_script "
+
			"WHERE lua_script = new.lua_script), "
+
			"new.type);"
+
	"END;"
+
	"CREATE TRIGGER lua_script_delete "
+
		"INSTEAD OF DELETE ON lua_scripts "
+
	"FOR EACH ROW BEGIN "
+
		"DELETE FROM pkg_lua_script "
+
		"WHERE package_id = old.package_id AND "
+
			"lua_script_id = ( SELECT lua_script_id FROM lua_script "
+
					   "WHERE lua_script = old.lua_script );"
+
		"DELETE FROM lua_script "
+
		"WHERE lua_script_id NOT IN "
+
			"( SELECT DISTINCT lua_script_id from lua_script );"
+
	"END;"

	"PRAGMA user_version = %d;"
	"COMMIT;"
@@ -1349,6 +1396,8 @@ typedef enum _sql_prstmt_index {
	UPDATE_CONFIG_FILE,
	PKG_REQUIRE,
	REQUIRE,
+
	LUASCRIPT1,
+
	LUASCRIPT2,
	PRSTMT_LAST,
} sql_prstmt_index;

@@ -1572,7 +1621,19 @@ static sql_prstmt sql_prepared_statements[PRSTMT_LAST] = {
		NULL,
		"INSERT OR IGNORE INTO requires(require) VALUES(?1)",
		"T"
-
	}
+
	},
+
	[LUASCRIPT1] = {
+
		NULL,
+
		"INSERT OR IGNORE INTO lua_script(lua_script) VALUES (?1)",
+
		"T",
+
	},
+
	[LUASCRIPT2] = {
+
		NULL,
+
		"INSERT INTO pkg_lua_script(lua_script_id, package_id, type) "
+
		"VALUES ((SELECT lua_script_id FROM lua_script WHERE "
+
		"lua_script = ?1), ?2, ?3)",
+
		"TII",
+
	},
	/* PRSTMT_LAST */
};

@@ -1900,6 +1961,12 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int forced)
		goto cleanup;

	/*
+
	 * Insert lua scripts
+
	 */
+
	if (pkgdb_insert_lua_scripts(pkg, package_id, s) != EPKG_OK)
+
		goto cleanup;
+

+
	/*
	 * Insert options
	 */

@@ -1977,6 +2044,28 @@ pkgdb_insert_scripts(struct pkg *pkg, int64_t package_id, sqlite3 *s)
	return (EPKG_OK);
}

+
static int
+
pkgdb_insert_lua_scripts(struct pkg *pkg, int64_t package_id, sqlite3 *s)
+
{
+
	struct pkg_lua_script	*scripts, *script;
+
	int64_t			 i;
+

+
	for (i = 0; i < PKG_NUM_LUA_SCRIPTS; i++) {
+
		scripts = pkg->lua_scripts[i];
+
		if (scripts == NULL)
+
			continue;
+
		LL_FOREACH(scripts, script) {
+
			if (run_prstmt(LUASCRIPT1, script->script) != SQLITE_DONE
+
			    ||
+
			    run_prstmt(LUASCRIPT2, script->script, package_id, i) != SQLITE_DONE) {
+
				ERROR_SQLITE(s, SQL(LUASCRIPT2));
+
				return (EPKG_FATAL);
+
			}
+
		}
+
	}
+
	return (EPKG_OK);
+
}
+

int
pkgdb_update_shlibs_required(struct pkg *pkg, int64_t package_id, sqlite3 *s)
{
@@ -2331,6 +2420,8 @@ pkgdb_unregister_pkg(struct pkgdb *db, int64_t id)
			"(SELECT DISTINCT shlib_id FROM pkg_shlibs_provided)",
		"script WHERE script_id NOT IN "
		        "(SELECT DISTINCT script_id FROM pkg_script)",
+
		"lua_script WHERE id NOT IN "
+
			"(SELECT DISTINCT lua_script_id FROM pkg_lua_script)",
	};

	assert(db != NULL);
modified libpkg/pkgdb_iterator.c
@@ -628,6 +628,46 @@ pkgdb_load_annotations(sqlite3 *sqlite, struct pkg *pkg)
}

static int
+
pkgdb_load_lua_scripts(sqlite3 *sqlite, struct pkg *pkg)
+
{
+
	sqlite3_stmt	*stmt = NULL;
+
	int		ret;
+
	const char	sql[] = ""
+
		"SELECT lua_script, type"
+
		"  FROM lua_script"
+
		"    JOIN pkg_lua_script USING(lua_script_id)"
+
		"  WHERE package_id = ?1";
+

+
	assert(pkg != NULL);
+
	assert(pkg->type == PKG_INSTALLED);
+

+
	if (pkg->flags & PKG_LOAD_LUA_SCRIPTS)
+
		return (EPKG_OK);
+

+
	pkg_debug(4, "Pkgdb: running '%s'", sql);
+
	if (sqlite3_prepare_v2(sqlite, sql, -1, &stmt, NULL) != SQLITE_OK) {
+
		ERROR_SQLITE(sqlite, sql);
+
		return (EPKG_FATAL);
+
	}
+

+
	sqlite3_bind_int64(stmt, 1, pkg->id);
+

+
	while ((ret = sqlite3_step(stmt)) == SQLITE_ROW) {
+
		pkg_add_lua_script(pkg, sqlite3_column_text(stmt, 0),
+
		    sqlite3_column_int64(stmt, 1));
+
	}
+
	sqlite3_finalize(stmt);
+

+
	if (ret != SQLITE_DONE) {
+
		ERROR_SQLITE(sqlite, sql);
+
		return (EPKG_FATAL);
+
	}
+

+
	pkg->flags |= PKG_LOAD_LUA_SCRIPTS;
+
	return (EPKG_OK);
+
}
+

+
static int
pkgdb_load_scripts(sqlite3 *sqlite, struct pkg *pkg)
{
	sqlite3_stmt	*stmt = NULL;
@@ -949,6 +989,7 @@ static struct load_on_flag {
	{ PKG_LOAD_CONFLICTS,		pkgdb_load_conflicts },
	{ PKG_LOAD_PROVIDES,		pkgdb_load_provides },
	{ PKG_LOAD_REQUIRES,		pkgdb_load_requires },
+
	{ PKG_LOAD_LUA_SCRIPTS,		pkgdb_load_lua_scripts },
	{ -1,			        NULL }
};

modified libpkg/private/db_upgrades.h
@@ -1,5 +1,5 @@
/*-
-
 * Copyright (c) 2011-2015 Baptiste Daroussin <bapt@FreeBSD.org>
+
 * Copyright (c) 2011-2019 Baptiste Daroussin <bapt@FreeBSD.org>
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
 * Copyright (c) 2013 Matthew Seaman <matthew@FreeBSD.org>
 * Copyright (c) 2013-2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
@@ -677,6 +677,54 @@ static struct db_upgrades {
	{34,
	"DROP TABLE pkg_search;"
	},
+
	{35,
+
	"CREATE TABLE lua_script("
+
	"    lua_script_id INTEGER PRIMARY KEY,"
+
	"    lua_script TEXT NOT NULL UNIQUE"
+
	");"
+
	"CREATE TABLE pkg_lua_script ("
+
		"package_id INTEGER NOT NULL REFERENCES packages(id)"
+
		"  ON DELETE CASCADE ON UPDATE CASCADE,"
+
		"lua_script_id INTEGER NOT NULL REFERENCES lua_script(lua_script_id)"
+
		"  ON DELETE RESTRICT ON UPDATE RESTRICT,"
+
		"type INTEGER,"
+
		"UNIQUE(package_id, lua_script_id)"
+
	");"
+
	"CREATE VIEW lua_scripts AS "
+
		"SELECT package_id, lua_script, type "
+
		"FROM pkg_lua_script JOIN lua_script USING(lua_script_id);"
+
	"CREATE TRIGGER lua_script_update "
+
		"INSTEAD OF UPDATE ON lua_scripts "
+
	"FOR EACH ROW BEGIN "
+
		"UPDATE pkg_lua_script "
+
		"SET type = new.type "
+
		"WHERE package_id = old.package_id AND "
+
		"lua_script_id = (SELECT lua_script_id FROM lua_script "
+
			"WHERE lua_script = old.lua_script );"
+
	"END;"
+
	"CREATE TRIGGER lua_script_insert "
+
		"INSTEAD OF INSERT ON lua_scripts "
+
	"FOR EACH ROW BEGIN "
+
		"INSERT OR IGNORE INTO lua_script(lua_script) "
+
		"VALUES(new.lua_script);"
+
		"INSERT INTO pkg_lua_script(package_id, lua_script_id, type) "
+
		"VALUES (new.package_id, "
+
			"(SELECT lua_script_id FROM lua_script "
+
			"WHERE lua_script = new.lua_script), "
+
			"new.type);"
+
	"END;"
+
	"CREATE TRIGGER lua_script_delete "
+
		"INSTEAD OF DELETE ON lua_scripts "
+
	"FOR EACH ROW BEGIN "
+
		"DELETE FROM pkg_lua_script "
+
		"WHERE package_id = old.package_id AND "
+
			"lua_script_id = ( SELECT lua_script_id FROM lua_script "
+
					   "WHERE lua_script = old.lua_script );"
+
		"DELETE FROM lua_script "
+
		"WHERE lua_script_id NOT IN "
+
			"( SELECT DISTINCT lua_script_id from lua_script );"
+
	"END;"
+
	},
	/* Mark the end of the array */
	{ -1, NULL }

modified libpkg/private/pkg.h
@@ -50,6 +50,7 @@
#define UCL_COUNT(obj) ((obj)?((obj)->len):0)

#define PKG_NUM_SCRIPTS 9
+
#define PKG_NUM_LUA_SCRIPTS 5

/*
 * Some compatibility checks
@@ -252,6 +253,7 @@ extern struct pkg_ctx ctx;
struct pkg_repo_it;
struct pkg_repo;
struct pkg_message;
+
struct pkg_lua_script;

KHASH_MAP_INIT_STR(pkg_deps, struct pkg_dep *);
KHASH_MAP_INIT_STR(pkg_files, struct pkg_file *);
@@ -268,6 +270,7 @@ struct pkg {
	bool		 vital;
	int64_t		 id;
	UT_string	*scripts[PKG_NUM_SCRIPTS];
+
	struct pkg_lua_script	*lua_scripts[PKG_NUM_LUA_SCRIPTS];
	char			*name;
	char			*origin;
	char			*version;
@@ -351,6 +354,11 @@ struct pkg_message {
	struct pkg_message	*next, *prev;
};

+
struct pkg_lua_script {
+
	char			*script;
+
	struct pkg_lua_script	*next, *prev;
+
};
+

enum pkg_conflict_type {
	PKG_CONFLICT_ALL = 0,
	PKG_CONFLICT_REMOTE_LOCAL,
@@ -682,6 +690,8 @@ int pkg_repo_load_fingerprints(struct pkg_repo *repo);
int pkg_start_stop_rc_scripts(struct pkg *, pkg_rc_attr attr);

int pkg_script_run(struct pkg *, pkg_script type);
+
int pkg_lua_script_run(struct pkg *, pkg_lua_script type);
+
ucl_object_t *pkg_lua_script_to_ucl(struct pkg_lua_script *);

int pkg_open2(struct pkg **p, struct archive **a, struct archive_entry **ae,
	      const char *path, struct pkg_manifest_key *keys, int flags, int fd);
@@ -789,6 +799,7 @@ int plist_parse_line(struct plist *p, char *line);
void plist_free(struct plist *);
int pkg_appendscript(struct pkg *pkg, const char *cmd, pkg_script type);

+
int pkg_add_lua_script(struct pkg *pkg, const char *data, pkg_lua_script type);
int pkg_addscript(struct pkg *pkg, const char *data, pkg_script type);
int pkg_addfile(struct pkg *pkg, const char *path, const char *sha256,
    bool check_duplicates);
@@ -821,6 +832,7 @@ bool pkg_is_config_file(struct pkg *p, const char *path, const struct pkg_file *
int pkg_message_from_ucl(struct pkg *pkg, const ucl_object_t *obj);
int pkg_message_from_str(struct pkg *pkg, const char *str, size_t len);
ucl_object_t* pkg_message_to_ucl(const struct pkg *pkg);
+
int pkg_lua_script_from_ucl(struct pkg *pkg, const ucl_object_t *obj, pkg_lua_script);
char* pkg_message_to_str(struct pkg *pkg);

int metalog_open(const char *metalog);
modified src/create.c
@@ -86,7 +86,7 @@ pkg_create_matches(int argc, char **argv, match_t match, pkg_formats fmt,
	    PKG_LOAD_OPTIONS | PKG_LOAD_LICENSES |
	    PKG_LOAD_USERS | PKG_LOAD_GROUPS | PKG_LOAD_SHLIBS_REQUIRED |
	    PKG_LOAD_PROVIDES | PKG_LOAD_REQUIRES |
-
	    PKG_LOAD_SHLIBS_PROVIDED | PKG_LOAD_ANNOTATIONS;
+
	    PKG_LOAD_SHLIBS_PROVIDED | PKG_LOAD_ANNOTATIONS | PKG_LOAD_LUA_SCRIPTS;
	struct pkg_entry *e = NULL, *etmp;
	char pkgpath[MAXPATHLEN];
	const char *format = NULL;
modified src/utils.c
@@ -315,7 +315,8 @@ info_flags(uint64_t opt, bool remote)
				PKG_LOAD_DIRS    |
				PKG_LOAD_USERS   |
				PKG_LOAD_GROUPS  |
-
				PKG_LOAD_SCRIPTS;
+
				PKG_LOAD_SCRIPTS |
+
				PKG_LOAD_LUA_SCRIPTS;
		}
	}