Blame src/logic/slbt_exec_uninstall.c

0fb20a
/*******************************************************************/
0fb20a
/*  slibtool: a skinny libtool implementation, written in C        */
bb281c
/*  Copyright (C) 2016--2021  SysDeer Technologies, LLC            */
0fb20a
/*  Released under the Standard MIT License; see COPYING.SLIBTOOL. */
0fb20a
/*******************************************************************/
0fb20a
0fb20a
#include <stdio.h>
0fb20a
#include <string.h>
0fb20a
#include <stdbool.h>
0fb20a
#include <fcntl.h>
0fb20a
#include <errno.h>
0fb20a
#include <sys/stat.h>
0fb20a
0fb20a
#include <slibtool/slibtool.h>
d58d2f
#include "slibtool_driver_impl.h"
0fb20a
#include "slibtool_uninstall_impl.h"
0fb20a
#include "slibtool_readlink_impl.h"
19022e
#include "slibtool_snprintf_impl.h"
0fb20a
#include "slibtool_errinfo_impl.h"
0fb20a
#include "argv/argv.h"
0fb20a
0fb20a
static int slbt_uninstall_usage(
a82cc2
	int				fdout,
0fb20a
	const char *			program,
0fb20a
	const char *			arg,
d58d2f
	const struct argv_option **	optv,
d4560d
	struct argv_meta *		meta,
d4560d
	int				noclr)
0fb20a
{
0fb20a
	char header[512];
0fb20a
0fb20a
	snprintf(header,sizeof(header),
0fb20a
		"Usage: %s --mode=uninstall <rm> [options] [DEST]...\n"
0fb20a
		"Options:\n",
0fb20a
		program);
0fb20a
d4560d
	switch (noclr) {
d4560d
		case 0:
2f8d3e
			slbt_argv_usage(fdout,header,optv,arg);
d4560d
			break;
d4560d
d4560d
		default:
2f8d3e
			slbt_argv_usage_plain(fdout,header,optv,arg);
d4560d
			break;
d4560d
	}
d4560d
2f8d3e
	slbt_argv_free(meta);
0fb20a
0fb20a
	return SLBT_USAGE;
0fb20a
}
0fb20a
0fb20a
static int slbt_exec_uninstall_fail(
0fb20a
	struct slbt_exec_ctx *	actx,
0fb20a
	struct argv_meta *	meta,
0fb20a
	int			ret)
0fb20a
{
2f8d3e
	slbt_argv_free(meta);
0fb20a
	slbt_free_exec_ctx(actx);
0fb20a
	return ret;
0fb20a
}
0fb20a
0fb20a
static int slbt_exec_uninstall_fs_entry(
0fb20a
	const struct slbt_driver_ctx *	dctx,
0fb20a
	struct slbt_exec_ctx *		ectx,
0fb20a
	char **				parg,
0fb20a
	char *				path,
0fb20a
	uint32_t			flags)
0fb20a
{
0fb20a
	struct stat	st;
7ae5c1
	int		fdcwd;
0fb20a
	char *		slash;
0fb20a
	char		dpath[PATH_MAX];
0fb20a
7ae5c1
	/* fdcwd */
7ae5c1
	fdcwd = slbt_driver_fdcwd(dctx);
7ae5c1
0fb20a
	/* needed? */
7ae5c1
	if (fstatat(fdcwd,path,&st,0) && (errno == ENOENT))
7ae5c1
		if (fstatat(fdcwd,path,&st,AT_SYMLINK_NOFOLLOW))
7ae5c1
			if (errno == ENOENT)
7ae5c1
				return 0;
0fb20a
0fb20a
	/* output */
0fb20a
	*parg = path;
0fb20a
0fb20a
	if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT))
0fb20a
		if (slbt_output_uninstall(dctx,ectx))
0fb20a
			return SLBT_NESTED_ERROR(dctx);
0fb20a
0fb20a
	/* directory? */
0fb20a
	if (S_ISDIR(st.st_mode)) {
e524bd
		if (!unlinkat(fdcwd,path,AT_REMOVEDIR))
0fb20a
			return 0;
0fb20a
0fb20a
		else if ((errno == EEXIST) || (errno == ENOTEMPTY))
0fb20a
			return 0;
0fb20a
0fb20a
		else
6beda1
			return SLBT_SYSTEM_ERROR(dctx,path);
0fb20a
	}
0fb20a
0fb20a
	/* remove file or symlink entry */
d38565
	if (unlinkat(fdcwd,path,0))
6beda1
		return SLBT_SYSTEM_ERROR(dctx,path);
0fb20a
0fb20a
	/* remove empty containing directory? */
0fb20a
	if (flags & SLBT_UNINSTALL_RMDIR) {
0fb20a
		strcpy(dpath,path);
0fb20a
0fb20a
		/* invalid (current) directory? */
0fb20a
		if (!(slash = strrchr(dpath,'/')))
0fb20a
			return 0;
0fb20a
0fb20a
		*slash = 0;
0fb20a
e524bd
		if (unlinkat(fdcwd,dpath,AT_REMOVEDIR))
6beda1
			return SLBT_SYSTEM_ERROR(dctx,dpath);
0fb20a
	}
0fb20a
0fb20a
	return 0;
0fb20a
}
0fb20a
0fb20a
static int slbt_exec_uninstall_versioned_library(
0fb20a
	const struct slbt_driver_ctx *	dctx,
0fb20a
	struct slbt_exec_ctx *		ectx,
0fb20a
	char **				parg,
0fb20a
	char *				rpath,
0fb20a
	char *				lpath,
0fb20a
	const char *			suffix,
0fb20a
	uint32_t			flags)
0fb20a
{
0fb20a
	char *	slash;
0fb20a
	char *	dot;
0fb20a
	char *	path;
0fb20a
	char	apath[PATH_MAX];
0fb20a
0fb20a
	/* normalize library link path */
0fb20a
	if (lpath[0] == '/') {
0fb20a
		path = lpath;
0fb20a
0fb20a
	} else if (!(slash = strrchr(rpath,'/'))) {
0fb20a
		path = lpath;
0fb20a
0fb20a
	} else {
0fb20a
		strcpy(apath,rpath);
0fb20a
		strcpy(&apath[slash-rpath+1],lpath);
0fb20a
		path = apath;
0fb20a
	}
0fb20a
0fb20a
	/* delete associated version files */
0fb20a
	while ((dot = strrchr(path,'.')) && (strcmp(dot,suffix))) {
0fb20a
		if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags))
0fb20a
			return SLBT_NESTED_ERROR(dctx);
0fb20a
0fb20a
		*dot = 0;
0fb20a
	}
0fb20a
0fb20a
	return 0;
0fb20a
}
0fb20a
0fb20a
static int slbt_exec_uninstall_entry(
0fb20a
	const struct slbt_driver_ctx *	dctx,
0fb20a
	struct slbt_exec_ctx *		ectx,
0fb20a
	struct argv_entry *		entry,
0fb20a
	char **				parg,
0fb20a
	uint32_t			flags)
0fb20a
{
c81d16
	int		fdcwd;
c0ac19
	const char *	dsosuffix;
c0ac19
	char *		dot;
c0ac19
	char		path [PATH_MAX];
c0ac19
	char		lpath[PATH_MAX];
0fb20a
19022e
	if (slbt_snprintf(path,
19022e
			PATH_MAX - 8,
19022e
			"%s",entry->arg) < 0)
0fb20a
		return SLBT_BUFFER_ERROR(dctx);
0fb20a
0fb20a
	*parg = (char *)entry->arg;
0fb20a
c81d16
	/* fdcwd */
c81d16
	fdcwd = slbt_driver_fdcwd(dctx);
c81d16
0fb20a
	/* remove explicit argument */
0fb20a
	if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags))
0fb20a
		return SLBT_NESTED_ERROR(dctx);
0fb20a
0fb20a
	/* non-.la-wrapper argument? */
0fb20a
	if (!(dot = strrchr(path,'.')))
0fb20a
		return 0;
0fb20a
0fb20a
	else if (strcmp(dot,".la"))
0fb20a
		return 0;
0fb20a
0fb20a
	/* remove .a archive as needed */
0fb20a
	strcpy(dot,".a");
0fb20a
0fb20a
	if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags))
0fb20a
		return SLBT_NESTED_ERROR(dctx);
0fb20a
c0ac19
	/* dsosuffix */
c0ac19
	dsosuffix = dctx->cctx->settings.dsosuffix;
c0ac19
0fb20a
	/* .so symlink? */
c0ac19
	strcpy(dot,dsosuffix);
0fb20a
c81d16
	if (!(slbt_readlinkat(fdcwd,path,lpath,sizeof(lpath))))
0fb20a
		if (slbt_exec_uninstall_versioned_library(
0fb20a
				dctx,ectx,parg,
0fb20a
				path,lpath,
c0ac19
				dsosuffix,flags))
0fb20a
			return SLBT_NESTED_ERROR(dctx);
0fb20a
0fb20a
	/* .lib.a symlink? */
0fb20a
	strcpy(dot,".lib.a");
0fb20a
c81d16
	if (!(slbt_readlinkat(fdcwd,path,lpath,sizeof(lpath))))
0fb20a
		if (slbt_exec_uninstall_versioned_library(
0fb20a
				dctx,ectx,parg,
0fb20a
				path,lpath,
0fb20a
				".lib.a",flags))
0fb20a
			return SLBT_NESTED_ERROR(dctx);
0fb20a
0fb20a
	/* .dll symlink? */
0fb20a
	strcpy(dot,".dll");
0fb20a
c81d16
	if (!(slbt_readlinkat(fdcwd,path,lpath,sizeof(lpath))))
0fb20a
		if (slbt_exec_uninstall_versioned_library(
0fb20a
				dctx,ectx,parg,
0fb20a
				path,lpath,
0fb20a
				".dll",flags))
0fb20a
			return SLBT_NESTED_ERROR(dctx);
0fb20a
0fb20a
	/* remove .so library as needed */
c0ac19
	strcpy(dot,dsosuffix);
0fb20a
0fb20a
	if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags))
0fb20a
		return SLBT_NESTED_ERROR(dctx);
0fb20a
0fb20a
	/* remove .lib.a import library as needed */
0fb20a
	strcpy(dot,".lib.a");
0fb20a
0fb20a
	if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags))
0fb20a
		return SLBT_NESTED_ERROR(dctx);
0fb20a
0fb20a
	/* remove .dll library as needed */
0fb20a
	strcpy(dot,".dll");
0fb20a
0fb20a
	if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags))
0fb20a
		return SLBT_NESTED_ERROR(dctx);
0fb20a
0fb20a
	/* remove .exe image as needed */
0fb20a
	strcpy(dot,".exe");
0fb20a
0fb20a
	if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags))
0fb20a
		return SLBT_NESTED_ERROR(dctx);
0fb20a
0fb20a
	/* remove binary image as needed */
0fb20a
	*dot = 0;
0fb20a
0fb20a
	if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags))
0fb20a
		return SLBT_NESTED_ERROR(dctx);
0fb20a
0fb20a
	return 0;
0fb20a
}
0fb20a
0fb20a
int slbt_exec_uninstall(
0fb20a
	const struct slbt_driver_ctx *	dctx,
0fb20a
	struct slbt_exec_ctx *		ectx)
0fb20a
{
0fb20a
	int				ret;
a82cc2
	int				fdout;
0fb20a
	char **				argv;
0fb20a
	char **				iargv;
0fb20a
	uint32_t			flags;
0fb20a
	struct slbt_exec_ctx *		actx;
0fb20a
	struct argv_meta *		meta;
0fb20a
	struct argv_entry *		entry;
d58d2f
	const struct argv_option *	optv[SLBT_OPTV_ELEMENTS];
0fb20a
0fb20a
	/* dry run */
0fb20a
	if (dctx->cctx->drvflags & SLBT_DRIVER_DRY_RUN)
0fb20a
		return 0;
0fb20a
0fb20a
	/* context */
0fb20a
	if (ectx)
0fb20a
		actx = 0;
0fb20a
	else if ((ret = slbt_get_exec_ctx(dctx,&ectx)))
0fb20a
		return ret;
0fb20a
	else
0fb20a
		actx = ectx;
0fb20a
0fb20a
	/* initial state, uninstall mode skin */
0fb20a
	slbt_reset_arguments(ectx);
0fb20a
	slbt_disable_placeholders(ectx);
0fb20a
	iargv = ectx->cargv;
a82cc2
	fdout = slbt_driver_fdout(dctx);
0fb20a
0fb20a
	/* missing arguments? */
2f8d3e
	slbt_optv_init(slbt_uninstall_options,optv);
d58d2f
0fb20a
	if (!iargv[1] && (dctx->cctx->drvflags & SLBT_DRIVER_VERBOSITY_USAGE))
a82cc2
		return slbt_uninstall_usage(
a82cc2
			fdout,
a82cc2
			dctx->program,
d4560d
			0,optv,0,
d4560d
			dctx->cctx->drvflags & SLBT_DRIVER_ANNOTATE_NEVER);
0fb20a
0fb20a
	/* <uninstall> argv meta */
2f8d3e
	if (!(meta = slbt_argv_get(
a82cc2
			iargv,optv,
0fb20a
			dctx->cctx->drvflags & SLBT_DRIVER_VERBOSITY_ERRORS
0fb20a
				? ARGV_VERBOSITY_ERRORS
93f9e4
				: ARGV_VERBOSITY_NONE,
a82cc2
			fdout)))
0fb20a
		return slbt_exec_uninstall_fail(
0fb20a
			actx,meta,
Kylie McClain 7ce25c
			SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_UNINSTALL_FAIL));
0fb20a
0fb20a
	/* dest, alternate argument vector options */
0fb20a
	argv  = ectx->altv;
0fb20a
	flags = 0;
0fb20a
0fb20a
	*argv++ = iargv[0];
0fb20a
0fb20a
	for (entry=meta->entries; entry->fopt || entry->arg; entry++) {
0fb20a
		if (entry->fopt) {
0fb20a
			switch (entry->tag) {
57ed91
				case TAG_UNINSTALL_SYSROOT:
57ed91
					break;
57ed91
0fb20a
				case TAG_UNINSTALL_HELP:
0fb20a
					flags |= SLBT_UNINSTALL_HELP;
0fb20a
					break;
0fb20a
0fb20a
				case TAG_UNINSTALL_VERSION:
0fb20a
					flags |= SLBT_UNINSTALL_VERSION;
0fb20a
					break;
0fb20a
0fb20a
				case TAG_UNINSTALL_FORCE:
0fb20a
					*argv++ = "-f";
0fb20a
					flags |= SLBT_UNINSTALL_FORCE;
0fb20a
					break;
0fb20a
0fb20a
				case TAG_UNINSTALL_RMDIR:
0fb20a
					*argv++ = "-d";
0fb20a
					flags |= SLBT_UNINSTALL_RMDIR;
0fb20a
					break;
0fb20a
0fb20a
				case TAG_UNINSTALL_VERBOSE:
0fb20a
					*argv++ = "-v";
0fb20a
					flags |= SLBT_UNINSTALL_VERBOSE;
0fb20a
					break;
0fb20a
			}
0fb20a
		}
0fb20a
	}
0fb20a
0fb20a
	/* --help */
0fb20a
	if (flags & SLBT_UNINSTALL_HELP) {
a82cc2
		slbt_uninstall_usage(
a82cc2
			fdout,
a82cc2
			dctx->program,
d4560d
			0,optv,meta,
d4560d
			dctx->cctx->drvflags & SLBT_DRIVER_ANNOTATE_NEVER);
0fb20a
		return 0;
0fb20a
	}
0fb20a
0fb20a
	/* uninstall */
0fb20a
	ectx->argv    = ectx->altv;
0fb20a
	ectx->program = ectx->altv[0];
0fb20a
0fb20a
	/* uninstall entries one at a time */
0fb20a
	for (entry=meta->entries; entry->fopt || entry->arg; entry++)
0fb20a
		if (!entry->fopt)
0fb20a
			if (slbt_exec_uninstall_entry(dctx,ectx,entry,argv,flags))
0fb20a
				return slbt_exec_uninstall_fail(
0fb20a
					actx,meta,
0fb20a
					SLBT_NESTED_ERROR(dctx));
0fb20a
2f8d3e
	slbt_argv_free(meta);
0fb20a
	slbt_free_exec_ctx(actx);
0fb20a
0fb20a
	return 0;
0fb20a
}