|
|
0fb20a |
/*******************************************************************/
|
|
|
0fb20a |
/* slibtool: a skinny libtool implementation, written in C */
|
|
|
0fb20a |
/* Copyright (C) 2016 Z. Gilboa */
|
|
|
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 |
#define ARGV_DRIVER
|
|
|
0fb20a |
|
|
|
0fb20a |
#include <slibtool/slibtool.h>
|
|
|
0fb20a |
#include "slibtool_uninstall_impl.h"
|
|
|
0fb20a |
#include "slibtool_readlink_impl.h"
|
|
|
0fb20a |
#include "slibtool_errinfo_impl.h"
|
|
|
0fb20a |
#include "argv/argv.h"
|
|
|
0fb20a |
|
|
|
0fb20a |
static int slbt_uninstall_usage(
|
|
|
0fb20a |
const char * program,
|
|
|
0fb20a |
const char * arg,
|
|
|
0fb20a |
const struct argv_option * options,
|
|
|
0fb20a |
struct argv_meta * meta)
|
|
|
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 |
|
|
|
0fb20a |
argv_usage(stdout,header,options,arg);
|
|
|
0fb20a |
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 |
{
|
|
|
0fb20a |
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;
|
|
|
0fb20a |
char * slash;
|
|
|
0fb20a |
char dpath[PATH_MAX];
|
|
|
0fb20a |
|
|
|
0fb20a |
/* needed? */
|
|
|
0fb20a |
if (stat(path,&st) && (errno == ENOENT))
|
|
|
0fb20a |
if (lstat(path,&st) && (errno == ENOENT))
|
|
|
0fb20a |
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)) {
|
|
|
0fb20a |
if (!(rmdir(path)))
|
|
|
0fb20a |
return 0;
|
|
|
0fb20a |
|
|
|
0fb20a |
else if ((errno == EEXIST) || (errno == ENOTEMPTY))
|
|
|
0fb20a |
return 0;
|
|
|
0fb20a |
|
|
|
0fb20a |
else
|
|
|
0fb20a |
return SLBT_SYSTEM_ERROR(dctx);
|
|
|
0fb20a |
}
|
|
|
0fb20a |
|
|
|
0fb20a |
/* remove file or symlink entry */
|
|
|
0fb20a |
if (unlink(path))
|
|
|
0fb20a |
return SLBT_SYSTEM_ERROR(dctx);
|
|
|
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 |
|
|
|
0fb20a |
if (rmdir(dpath))
|
|
|
0fb20a |
return SLBT_SYSTEM_ERROR(dctx);
|
|
|
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 |
{
|
|
|
0fb20a |
char path [PATH_MAX];
|
|
|
0fb20a |
char lpath[PATH_MAX];
|
|
|
0fb20a |
char * dot;
|
|
|
0fb20a |
|
|
|
0fb20a |
if ((size_t)snprintf(path,PATH_MAX,"%s",
|
|
|
0fb20a |
entry->arg) >= PATH_MAX-8)
|
|
|
0fb20a |
return SLBT_BUFFER_ERROR(dctx);
|
|
|
0fb20a |
|
|
|
0fb20a |
*parg = (char *)entry->arg;
|
|
|
0fb20a |
|
|
|
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 |
|
|
|
0fb20a |
/* .so symlink? */
|
|
|
0fb20a |
strcpy(dot,".so");
|
|
|
0fb20a |
|
|
|
0fb20a |
if (!(slbt_readlink(path,lpath,sizeof(lpath))))
|
|
|
0fb20a |
if (slbt_exec_uninstall_versioned_library(
|
|
|
0fb20a |
dctx,ectx,parg,
|
|
|
0fb20a |
path,lpath,
|
|
|
0fb20a |
".so",flags))
|
|
|
0fb20a |
return SLBT_NESTED_ERROR(dctx);
|
|
|
0fb20a |
|
|
|
0fb20a |
/* .lib.a symlink? */
|
|
|
0fb20a |
strcpy(dot,".lib.a");
|
|
|
0fb20a |
|
|
|
0fb20a |
if (!(slbt_readlink(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 |
|
|
|
0fb20a |
if (!(slbt_readlink(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 */
|
|
|
0fb20a |
strcpy(dot,".so");
|
|
|
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;
|
|
|
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;
|
|
|
0fb20a |
const struct argv_option * options = slbt_uninstall_options;
|
|
|
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;
|
|
|
0fb20a |
|
|
|
0fb20a |
/* work around non-conforming uses of --mode=uninstall */
|
|
|
0fb20a |
if (!(strcmp(iargv[0],"/bin/sh")) || !strcmp(iargv[0],"/bin/bash"))
|
|
|
0fb20a |
iargv++;
|
|
|
0fb20a |
|
|
|
0fb20a |
/* missing arguments? */
|
|
|
0fb20a |
if (!iargv[1] && (dctx->cctx->drvflags & SLBT_DRIVER_VERBOSITY_USAGE))
|
|
|
0fb20a |
return slbt_uninstall_usage(dctx->program,0,options,0);
|
|
|
0fb20a |
|
|
|
0fb20a |
/* <uninstall> argv meta */
|
|
|
0fb20a |
if (!(meta = argv_get(
|
|
|
0fb20a |
iargv,
|
|
|
0fb20a |
options,
|
|
|
0fb20a |
dctx->cctx->drvflags & SLBT_DRIVER_VERBOSITY_ERRORS
|
|
|
0fb20a |
? ARGV_VERBOSITY_ERRORS
|
|
|
0fb20a |
: ARGV_VERBOSITY_NONE)))
|
|
|
0fb20a |
return slbt_exec_uninstall_fail(
|
|
|
0fb20a |
actx,meta,
|
|
|
0fb20a |
SLBT_CUSTOM_ERROR(dctx,0));
|
|
|
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) {
|
|
|
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) {
|
|
|
0fb20a |
slbt_uninstall_usage(dctx->program,0,options,meta);
|
|
|
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 |
|
|
|
0fb20a |
argv_free(meta);
|
|
|
0fb20a |
slbt_free_exec_ctx(actx);
|
|
|
0fb20a |
|
|
|
0fb20a |
return 0;
|
|
|
0fb20a |
}
|