Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Add unregister
Emmanuel Vadot committed 5 months ago
commit e133b2185722b2e9b8901188701a897e5ff1797f
parent 97530dd
12 files changed +601 -6
modified docs/Makefile.autosetup
@@ -37,6 +37,7 @@ MAN8= pkg-add.8 \
	pkg-ssh.8 \
	pkg-stats.8 \
	pkg-triggers.8 \
+
	pkg-unregister.8 \
	pkg-update.8 \
	pkg-updating.8 \
	pkg-upgrade.8 \
added docs/pkg-unregister.8
@@ -0,0 +1,187 @@
+
.\"
+
.\" FreeBSD pkg - a next generation package for the installation and maintenance
+
.\" of non-core utilities.
+
.\"
+
.\" 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.
+
.\" 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.
+
.\"
+
.\"
+
.\"     @(#)pkg.8
+
.\"
+
.Dd December 2, 2025
+
.Dt PKG-UNREGISTER 8
+
.Os
+
.Sh NAME
+
.Nm "pkg unregister"
+
.Nd deletes packages records from the database
+
.Sh SYNOPSIS
+
.Nm
+
.Op Fl fnqRy
+
.Op Fl Cgix
+
.Ar pkg-name ...
+
.Nm
+
.Op Fl Dnqy
+
.Fl a
+
.Pp
+
.Nm
+
.Op Cm --{force,dry-run,quiet,recursive,yes}
+
.Op Cm --{case-sensitive,glob,case-insensitive,regex}
+
.Ar pkg-name ...
+
.Nm
+
.Op Cm --{dry-run,quiet,yes}
+
.Cm --all
+
.Sh DESCRIPTION
+
.Nm
+
is used for deleting installed packages from the database.
+
.Nm
+
creates a work-list of all the installed packages matching the package
+
names on the command line.
+
The list is presented to the user.
+
If the user confirms that they do want to remove those packages
+
from the db, or if the
+
.Cm ASSUME_ALWAYS_YES
+
configuration option is set,
+
.Nm
+
proceeds to remove from the db the listed packages.
+
.Pp
+
If the set of packages to be unregistered would leave installed packages
+
with unfulfilled dependencies,
+
.Nm
+
will add the packages with unfulfilled dependencies to the list of
+
packages to be unregistered, unless forced to proceed without unregistering any
+
other packages by the
+
.Fl f
+
option.
+
.Pp
+
.Nm
+
avoids deleting itself unless named specifically on the command line.
+
So, for instance,
+
.Nm Fl a
+
will unregister all packages from the db except for the
+
.Xr pkg 8
+
package.
+
To force all packages to be unregistered from the db without exception, use
+
.Nm Fl fa .
+
.Sh OPTIONS
+
The following options are supported by
+
.Nm :
+
.Bl -tag -width recursive
+
.It Fl a , Cm --all
+
Deletes all installed packages from the db except for
+
.Xr pkg 8
+
from the system and cleans the database of information related to removed
+
packages.
+
.It Fl C , Cm --case-sensitive
+
Make the standard or regular expression
+
.Fl ( x )
+
matching against
+
.Ar pkg-name
+
case sensitive.
+
.It Fl D , Cm --no-scripts
+
If a deinstallation script exists for a given package, do not execute it.
+
.It Fl f , Cm --force
+
Forces packages to be removed from the db despite leaving unresolved dependencies.
+
In combination with the
+
.Fl a
+
or
+
.Fl -all
+
flag, causes
+
.Xr pkg 8
+
to be removed as well as all other packages.
+
.It Fl g , Cm --glob
+
Treat the package names as shell glob patterns.
+
.It Fl i , Cm --case-insensitive
+
Make the standard or regular expression
+
.Fl ( x )
+
matching against
+
.Ar pkg-name
+
case insensitive.
+
This is the default, unless modified by setting
+
.Ev CASE_SENSITIVE_MATCH
+
to true in
+
.Pa pkg.conf .
+
.It Fl n , Cm --dry-run
+
Dry run mode.
+
The list of packages to delete is always printed, but no packages are
+
actually deleted.
+
.It Fl q , Cm --quiet
+
Force quiet output, except where
+
.Fl n
+
is also used, when
+
.Nm
+
will show the list of packages to be deleted.
+
.It Fl R , Cm --recursive
+
Delete all packages that require the listed packages as well.
+
.It Fl x , Cm --regex
+
Treat the package names as regular expressions according to the
+
"modern" or "extended" syntax of
+
.Xr re_format 7 .
+
.It Fl y , Cm --yes
+
Assume yes when asked for confirmation before package deletion.
+
.El
+
.Sh ENVIRONMENT
+
The following environment variables affect the execution of
+
.Nm .
+
See
+
.Xr pkg.conf 5
+
for further description.
+
.Bl -tag -width ".Ev NO_DESCRIPTIONS"
+
.It Ev DEFAULT_ALWAYS_YES
+
.It Ev ASSUME_ALWAYS_YES
+
.It Ev HANDLE_RC_SCRIPTS
+
.It Ev PKG_DBDIR
+
.It Ev CASE_SENSITIVE_MATCH
+
.El
+
.Sh FILES
+
See
+
.Xr pkg.conf 5 .
+
.Sh SEE ALSO
+
.Xr pkg_create 3 ,
+
.Xr pkg_delete 3 ,
+
.Xr pkg_printf 3 ,
+
.Xr pkg_repo_create 3 ,
+
.Xr pkg_repos 3 ,
+
.Xr pkg-keywords 5 ,
+
.Xr pkg-lua-script 5 ,
+
.Xr pkg-repository 5 ,
+
.Xr pkg-script 5 ,
+
.Xr pkg-triggers 5 ,
+
.Xr pkg.conf 5 ,
+
.Xr pkg 8 ,
+
.Xr pkg-add 8 ,
+
.Xr pkg-alias 8 ,
+
.Xr pkg-annotate 8 ,
+
.Xr pkg-audit 8 ,
+
.Xr pkg-autoremove 8 ,
+
.Xr pkg-check 8 ,
+
.Xr pkg-clean 8 ,
+
.Xr pkg-config 8 ,
+
.Xr pkg-create 8 ,
+
.Xr pkg-fetch 8 ,
+
.Xr pkg-info 8 ,
+
.Xr pkg-install 8 ,
+
.Xr pkg-key 8 ,
+
.Xr pkg-lock 8 ,
+
.Xr pkg-query 8 ,
+
.Xr pkg-register 8 ,
+
.Xr pkg-repo 8 ,
+
.Xr pkg-repositories 8 ,
+
.Xr pkg-rquery 8 ,
+
.Xr pkg-search 8 ,
+
.Xr pkg-set 8 ,
+
.Xr pkg-shell 8 ,
+
.Xr pkg-shlib 8 ,
+
.Xr pkg-ssh 8 ,
+
.Xr pkg-stats 8 ,
+
.Xr pkg-triggers 8 ,
+
.Xr pkg-update 8 ,
+
.Xr pkg-updating 8 ,
+
.Xr pkg-upgrade 8 ,
+
.Xr pkg-version 8 ,
+
.Xr pkg-which 8
modified libpkg/pkg.h.in
@@ -415,7 +415,8 @@ typedef enum _pkg_flags {
	PKG_FLAG_USE_IPV4 = (1U << 11),
	PKG_FLAG_USE_IPV6 = (1U << 12),
	PKG_FLAG_UPGRADE_VULNERABLE = (1U << 13),
-
	PKG_FLAG_NOEXEC = (1U << 14)
+
	PKG_FLAG_NOEXEC = (1U << 14),
+
	PKG_FLAG_KEEPFILES = (1U << 15)
} pkg_flags;

typedef enum _pkg_stats_t {
modified libpkg/pkg_delete.c
@@ -100,11 +100,13 @@ pkg_delete(struct pkg *pkg, struct pkg *rpkg, struct pkgdb *db, int flags,
			return (ret);
	}

-
	ret = pkg_delete_files(db, pkg, rpkg, flags, t);
-
	if (ret == EPKG_CANCEL)
-
		cancel = 1;
-
	else if (ret != EPKG_OK)
-
		return (ret);
+
	if ((flags & PKG_DELETE_KEEPFILES) == 0) {
+
		ret = pkg_delete_files(db, pkg, rpkg, flags, t);
+
		if (ret == EPKG_CANCEL)
+
			cancel = 1;
+
		else if (ret != EPKG_OK)
+
			return (ret);
+
	}

	if ((flags & PKG_DELETE_NOSCRIPT) == 0) {
		bool noexec = ((flags & PKG_DELETE_NOEXEC) == PKG_DELETE_NOEXEC);
modified libpkg/pkg_jobs.c
@@ -2018,6 +2018,8 @@ pkg_jobs_handle_delete(struct pkg_solved *ps, struct pkg_jobs *j)
	flags = 0;
	if ((j->flags & PKG_FLAG_NOSCRIPT) != 0)
		flags |= PKG_DELETE_NOSCRIPT;
+
	if ((j->flags & PKG_FLAG_KEEPFILES) != 0)
+
		flags |= PKG_DELETE_KEEPFILES;
	if (ps->type == PKG_SOLVED_UPGRADE_REMOVE) {
		flags |= PKG_DELETE_UPGRADE;
		rpkg = ps->xlink->items[0]->pkg;
modified libpkg/private/pkg.h
@@ -663,6 +663,7 @@ int pkg_delete(struct pkg *pkg, struct pkg *rpkg, struct pkgdb *db, int flags,
#define PKG_DELETE_UPGRADE	(1 << 1)	/* delete as a split upgrade */
#define PKG_DELETE_NOSCRIPT	(1 << 2)	/* don't run delete scripts */
#define PKG_DELETE_NOEXEC	(1 << 3)	/* don't run delete scripts which execute things*/
+
#define PKG_DELETE_KEEPFILES	(1 << 4)	/* don't delete files */

int pkg_fetch_file_to_fd(struct pkg_repo *repo, int dest, struct fetch_item *,
    bool silent);
modified src/Makefile.autosetup
@@ -31,6 +31,7 @@ SRCS= add.c \
	ssh.c \
	stats.c \
	triggers.c \
+
	unregister.c \
	update.c \
	updating.c \
	upgrade.c \
modified src/main.c
@@ -89,6 +89,7 @@ static struct commands {
	{ "stats", "Displays package database statistics", exec_stats, usage_stats},
	{ "triggers", "Execute deferred triggers", exec_triggers, usage_triggers},
	{ "unlock", "Unlocks a package, allowing modification or deletion", exec_unlock, usage_lock},
+
	{ "unregister", "Deletes only packages entries from the database", exec_unregister, usage_unregister},
	{ "update", "Updates package repository catalogues", exec_update, usage_update},
	{ "updating", "Displays UPDATING information for a package", exec_updating, usage_updating},
	{ "upgrade", "Performs upgrades of packaged software distributions", exec_upgrade, usage_upgrade},
modified src/pkgcli.h
@@ -125,6 +125,10 @@ void usage_stats(void);
int exec_triggers(int, char **);
void usage_triggers(void);

+
/* pkg unregister */
+
void usage_unregister(void);
+
int exec_unregister(int, char **);
+

/* pkg update */
int exec_update(int, char **);
void usage_update(void);
added src/unregister.c
@@ -0,0 +1,252 @@
+
/*-
+
 * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org>
+
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
+
 * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com>
+
 * Copyright (c) 2013-2014 Matthew Seaman <matthew@FreeBSD.org>
+
 * Copyright (c) 2016 Vsevolod Stakhov <vsevolod@FreeBSD.org>
+
 * All rights reserved.
+
 * Copyright (c) 2025 Emmanuel Vadot <manu@FreeBSD.org>
+
 *
+
 * 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 <err.h>
+
#include <getopt.h>
+
#include <stdio.h>
+
#include <string.h>
+
#include <unistd.h>
+

+
#include <pkg.h>
+

+
#include "pkgcli.h"
+

+
void
+
usage_unregister(void)
+
{
+
	fprintf(stderr, "Usage: pkg unregister [-fnqRy] [-Cgix] <pkg-name> ...\n");
+
	fprintf(stderr, "       pkg unregister [-nqy] -a\n\n");
+
	fprintf(stderr, "For more information see 'pkg help unregister'.\n");
+
}
+

+
int
+
exec_unregister(int argc, char **argv)
+
{
+
	struct pkg_jobs	*jobs = NULL;
+
	struct pkgdb	*db = NULL;
+
	match_t		 match = MATCH_EXACT;
+
	pkg_flags	 f = PKG_FLAG_KEEPFILES | PKG_FLAG_NOSCRIPT;
+
	bool		 recursive_flag = false, rc = false;
+
	int		 retcode = EXIT_FAILURE;
+
	int		 ch;
+
	int		 i;
+
	int		 lock_type = PKGDB_LOCK_ADVISORY;
+
	int		 locked_pkgs = 0;
+
	int		 nbactions = 0;
+

+
	struct option longopts[] = {
+
		{ "all",			no_argument,	NULL,	'a' },
+
		{ "case-sensitive",		no_argument,	NULL,	'C' },
+
		{ "force",			no_argument,	NULL,	'f' },
+
		{ "glob",			no_argument,	NULL,	'g' },
+
		{ "case-insensitive",		no_argument,	NULL,	'i' },
+
		{ "dry-run",			no_argument,	NULL,	'n' },
+
		{ "quiet",			no_argument,	NULL,	'q' },
+
		{ "recursive",			no_argument,	NULL,	'R' },
+
		{ "regex",			no_argument,	NULL,	'x' },
+
		{ "yes",			no_argument,	NULL,	'y' },
+
		{ NULL,				0,		NULL,	0   },
+
	};
+

+
	while ((ch = getopt_long(argc, argv, "aCfginqRxy", longopts, NULL)) != -1) {
+
		switch (ch) {
+
		case 'a':
+
			match = MATCH_ALL;
+
			break;
+
		case 'C':
+
			pkgdb_set_case_sensitivity(true);
+
			break;
+
		case 'f':
+
			f |= PKG_FLAG_FORCE;
+
			force = true;
+
			break;
+
		case 'g':
+
			match = MATCH_GLOB;
+
			break;
+
		case 'i':
+
			pkgdb_set_case_sensitivity(false);
+
			break;
+
		case 'n':
+
			f |= PKG_FLAG_DRY_RUN;
+
			lock_type = PKGDB_LOCK_READONLY;
+
			dry_run = true;
+
			break;
+
		case 'q':
+
			quiet = true;
+
			break;
+
		case 'R':
+
			recursive_flag = true;
+
			break;
+
		case 'x':
+
			match = MATCH_REGEX;
+
			break;
+
		case 'y':
+
			yes = true;
+
			break;
+
		default:
+
			usage_unregister();
+
			return (EXIT_FAILURE);
+
		}
+
	}
+

+
	argc -= optind;
+
	argv += optind;
+

+
	if (argc < 1 && match != MATCH_ALL) {
+
		usage_unregister();
+
		return (EXIT_FAILURE);
+
	}
+

+
	if (dry_run)
+
		retcode = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);
+
	else
+
		retcode = pkgdb_access(PKGDB_MODE_READ|PKGDB_MODE_WRITE,
+
				       PKGDB_DB_LOCAL);
+

+
	if (retcode == EPKG_ENODB) {
+
		warnx("No packages installed.  Nothing to do!");
+
		return (EXIT_SUCCESS);
+
	} else if (retcode == EPKG_ENOACCESS) {
+
		warnx("Insufficient privileges to delete packages");
+
		return (EXIT_FAILURE);
+
	} else if (retcode != EPKG_OK) {
+
		warnx("Error accessing the package database");
+
		return (EXIT_FAILURE);
+
	}
+

+
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
+
		return (EXIT_FAILURE);
+

+
	if (pkgdb_obtain_lock(db, lock_type) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
+
		return (EXIT_FAILURE);
+
	}
+

+

+
	if (pkg_jobs_new(&jobs, PKG_JOBS_DEINSTALL, db) != EPKG_OK) {
+
		pkgdb_close(db);
+
		return (EXIT_FAILURE);
+
	}
+

+
	/*
+
	 * By default delete packages recursively.
+
	 * If force mode is enabled then we try to remove packages non-recursively.
+
	 * However, if -f and -R flags are both enabled then we return to
+
	 * recursive deletion.
+
	 */
+
	if (!force || recursive_flag)
+
		f |= PKG_FLAG_RECURSIVE;
+

+
	pkg_jobs_set_flags(jobs, f);
+

+
	if (match == MATCH_EXACT) {
+
		for (i = 0; i < argc; i++) {
+
			if (strchr(argv[i], '*') != NULL) {
+
				match = MATCH_GLOB;
+
				break;
+
			}
+
		}
+
	}
+

+
	if (pkg_jobs_add(jobs, match, argv, argc) == EPKG_FATAL)
+
		goto cleanup;
+

+
	if (pkg_jobs_solve(jobs) != EPKG_OK) {
+
		fprintf(stderr, "Cannot perform request\n");
+
		retcode = EXIT_FAILURE;
+
		goto cleanup;
+
	}
+

+
	if (pkg_jobs_has_lockedpkgs(jobs)) {
+
		printf("The following package(s) are locked or vital and may not ");
+
		printf("be removed:\n\n");
+
		pkg_jobs_iter_lockedpkgs(jobs, print_pkg, &locked_pkgs);
+
		putchar('\n');
+
	}
+

+
	/* check if we have something to deinstall */
+
	if ((nbactions = pkg_jobs_count(jobs)) == 0) {
+
		if (argc == 0) {
+
			if (!quiet)
+
				printf("Nothing to do.\n");
+

+
			retcode = EXIT_SUCCESS;
+
			goto cleanup;
+
		}
+
		if (!quiet) {
+
			printf("%d packages requested for removal form the db: "
+
			    "%d locked, %d missing\n",
+
			    argc, locked_pkgs, argc - locked_pkgs);
+
		}
+
		if (locked_pkgs > 0) {
+
			retcode = EPKG_LOCKED;
+
		} else {
+
			retcode = EXIT_FAILURE;
+
		}
+
		goto cleanup;
+
	}
+

+
	if (!quiet || dry_run) {
+
		if (!quiet) {
+
			print_jobs_summary(jobs,
+
				"Unregister has been requested for the following %d packages "
+
				"(of %d packages in the universe):\n\n", nbactions,
+
				pkg_jobs_total(jobs));
+
		}
+
		if (dry_run) {
+
			retcode = EXIT_SUCCESS;
+
			goto cleanup;
+
		}
+
		rc = query_yesno(false,
+
		            "\nProceed with unregistering packages? ");
+
	}
+
	else
+
		rc = yes;
+

+
	if (!rc || (retcode = pkg_jobs_apply(jobs)) != EPKG_OK)
+
		goto cleanup;
+

+
	if (messages != NULL) {
+
		fflush(messages->fp);
+
		printf("%s", messages->buf);
+
	}
+
	pkgdb_compact(db);
+

+
	retcode = EXIT_SUCCESS;
+

+
cleanup:
+
	pkgdb_release_lock(db, lock_type);
+
	pkg_jobs_free(jobs);
+
	pkgdb_close(db);
+

+
	return (retcode);
+
}
modified tests/Makefile.autosetup
@@ -59,6 +59,7 @@ TESTS_SH= \
	frontend/set.sh \
	frontend/version.sh \
	frontend/vital.sh \
+
	frontend/unregister.sh \
	frontend/update.sh \
	frontend/updating.sh \
	frontend/upgrade.sh \
added tests/frontend/unregister.sh
@@ -0,0 +1,142 @@
+
#! /usr/bin/env atf-sh
+

+
. $(atf_get_srcdir)/test_environment.sh
+

+
tests_init \
+
	unregister_all \
+
	unregister_pkg \
+
	unregister_with_directory_owned \
+
	simple_unregister \
+
	simple_unregister_prefix_ending_with_slash
+

+
unregister_all_body() {
+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "foo" "foo" "1"
+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "pkg" "pkg" "1"
+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "test" "test" "1"
+

+
	atf_check -o ignore pkg register -M foo.ucl
+
	atf_check -o ignore pkg register -M pkg.ucl
+
	atf_check -o ignore pkg register -M test.ucl
+

+
	atf_check -o ignore pkg unregister -ay
+
}
+

+
unregister_pkg_body() {
+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "pkg" "pkg" "1"
+
	atf_check -o ignore pkg register -M pkg.ucl
+
	atf_check -o ignore -e ignore -s exit:3 pkg unregister -y pkg
+
	atf_check -o ignore -e ignore pkg unregister -yf pkg
+
}
+

+
simple_unregister_body() {
+
	touch file1
+
	mkdir dir
+
	touch dir/file2
+

+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "test" "test" "1" "${TMPDIR}"
+
	cat << EOF >> test.ucl
+
files: {
+
    ${TMPDIR}/file1: "",
+
    ${TMPDIR}/dir/file2: "",
+
}
+
EOF
+

+
	atf_check \
+
		-o match:".*Installing.*\.\.\.$" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg register -M test.ucl
+

+
	atf_check \
+
		-o match:".*Deinstalling.*" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg unregister -y test
+

+
	test -f file1 || atf_fail "'file1' is not present"
+
	test -f dir/file2 || atf_fail "'dir/file2' is not present"
+
	test -d dir || atf_fail "'dir' is not present"
+
	test -d ${TMPDIR} || atf_fail "Prefix have been removed"
+
}
+

+
simple_unregister_prefix_ending_with_slash_body() {
+
	touch file1
+
	mkdir dir
+
	touch dir/file2
+

+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "test" "test" "1" "${TMPDIR}/"
+
	cat << EOF >> test.ucl
+
files: {
+
    ${TMPDIR}/file1: "",
+
    ${TMPDIR}/dir/file2: "",
+
}
+
EOF
+

+
	atf_check \
+
		-o match:".*Installing.*\.\.\.$" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg register -M test.ucl
+

+
	atf_check \
+
		-o match:".*Deinstalling.*" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg unregister -y test
+

+
	test -f file1 || atf_fail "'file1' is not present"
+
	test -f dir/file2 || atf_fail "'dir/file2' is not present"
+
	test -d dir || atf_fail "'dir' is not present"
+
	test -d ${TMPDIR} || atf_fail "Prefix have been removed"
+
}
+

+
unregister_with_directory_owned_body() {
+
	touch file1
+
	mkdir dir
+
	touch dir/file2
+

+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "test" "test" "1" "${TMPDIR}/"
+
	cat << EOF >> test.ucl
+
files: {
+
    ${TMPDIR}/file1: "",
+
    ${TMPDIR}/dir/file2: "",
+
}
+
EOF
+

+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "test2" "test2" "1" "${TMPDIR}/"
+
	cat << EOF >> test2.ucl
+
directories: {
+
    ${TMPDIR}/dir: 'y',
+
}
+
EOF
+
	atf_check \
+
		-o match:".*Installing.*\.\.\.$" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg register -M test.ucl
+

+
	atf_check \
+
		-o match:".*Installing.*\.\.\.$" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg register -M test2.ucl
+

+
	atf_check \
+
		-o match:".*Deinstalling.*" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg unregister -y test
+

+
	test -f file1 || atf_fail "'file1' is not present"
+
	test -f dir/file2 || atf_fail "'dir/file2' is not present"
+
	test -d dir || atf_fail "'dir' has been removed"
+

+
	atf_check \
+
		-o match:".*Deinstalling.*" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg unregister -y test2
+

+
	test -d dir || atf_fail "'dir' is not present"
+
	test -d ${TMPDIR} || atf_fail "Prefix has been removed"
+
}