Blame src/logic/slbt_exec_install.c

258073
/*******************************************************************/
258073
/*  slibtool: a skinny libtool implementation, written in C        */
258073
/*  Copyright (C) 2016  Z. Gilboa                                  */
258073
/*  Released under the Standard MIT License; see COPYING.SLIBTOOL. */
258073
/*******************************************************************/
258073
258073
#include <stdio.h>
258073
#include <string.h>
258073
#include <stdbool.h>
258073
#include <fcntl.h>
258073
#include <errno.h>
258073
#include <sys/stat.h>
258073
258073
#define ARGV_DRIVER
258073
258073
#include <slibtool/slibtool.h>
258073
#include "slibtool_install_impl.h"
e8159c
#include "slibtool_readlink_impl.h"
258073
#include "slibtool_spawn_impl.h"
258073
#include "slibtool_symlink_impl.h"
258073
#include "argv/argv.h"
258073
258073
static int slbt_install_usage(
258073
	const char *			program,
258073
	const char *			arg,
258073
	const struct argv_option *	options,
258073
	struct argv_meta *		meta)
258073
{
258073
	char header[512];
258073
258073
	snprintf(header,sizeof(header),
258073
		"Usage: %s --mode=install <install> [options] [SOURCE]... DEST\n"
258073
		"Options:\n",
258073
		program);
258073
258073
	argv_usage(stdout,header,options,arg);
258073
	argv_free(meta);
258073
258073
	return SLBT_USAGE;
258073
}
258073
258073
static int slbt_exec_install_fail(
258073
	struct slbt_exec_ctx *	actx,
258073
	struct argv_meta *	meta)
258073
{
258073
	argv_free(meta);
258073
	slbt_free_exec_ctx(actx);
258073
	return -1;
258073
}
258073
258073
static int slbt_exec_install_init_dstdir(
258073
	struct argv_entry *	dest,
258073
	struct argv_entry *	last,
258073
	char *			dstdir)
258073
{
061d4a
	struct stat	st;
258073
	char *		slash;
258073
	size_t		len;
258073
258073
	if (dest)
258073
		last = dest;
258073
258073
	/* dstdir: initial string */
258073
	if ((size_t)snprintf(dstdir,PATH_MAX,"%s",
258073
			last->arg) >= PATH_MAX)
258073
		return -1;
258073
258073
	/* dstdir might end with a slash */
258073
	len = strlen(dstdir);
258073
258073
	if (dstdir[--len] == '/')
239674
		dstdir[len] = 0;
258073
061d4a
	/* -t DSTDIR? */
061d4a
	if (dest)
061d4a
		return 0;
061d4a
061d4a
	/* is DEST a directory? */
061d4a
	if (!(stat(dstdir,&st)))
061d4a
		if (S_ISDIR(st.st_mode))
061d4a
			return 0;
061d4a
258073
	/* remove last path component */
061d4a
	if ((slash = strrchr(dstdir,'/')))
239674
		*slash = 0;
258073
258073
	return 0;
258073
}
258073
c6e3d1
static int slbt_exec_install_import_libraries(
c6e3d1
	const struct slbt_driver_ctx *	dctx,
c6e3d1
	struct slbt_exec_ctx *		ectx,
c6e3d1
	char *				srcdso,
c6e3d1
	char *				dstdir)
c6e3d1
{
c6e3d1
	char *	host;
c6e3d1
	char *	slash;
c6e3d1
	char *	dot;
c6e3d1
	char *	mark;
c6e3d1
	char	srcbuf [PATH_MAX];
c6e3d1
	char	implib [PATH_MAX];
c6e3d1
	char	hosttag[PATH_MAX];
c6e3d1
	char	hostlnk[PATH_MAX];
c6e3d1
	char	major  [128];
c6e3d1
	char	minor  [128];
c6e3d1
	char	rev    [128];
c6e3d1
c6e3d1
	/* .libs/libfoo.so.x.y.z */
c6e3d1
	if ((size_t)snprintf(srcbuf,sizeof(srcbuf),"%s",
c6e3d1
			srcdso) >= sizeof(srcbuf))
c6e3d1
		return -1;
c6e3d1
c6e3d1
	/* (dso is under .libs) */
c6e3d1
	if (!(slash = strrchr(srcbuf,'/')))
c6e3d1
		return -1;
c6e3d1
c6e3d1
	/* libfoo.so.x.y.z */
c6e3d1
	if ((size_t)snprintf(implib,sizeof(implib),"%s",
c6e3d1
			++slash) >= sizeof(implib)
c6e3d1
				    - strlen(dctx->cctx->settings.impsuffix))
c6e3d1
		return -1;
c6e3d1
c6e3d1
	/* guard againt an infinitely long version */
c6e3d1
	mark = srcbuf + strlen(srcbuf);
c6e3d1
c6e3d1
	/* rev */
c6e3d1
	if (!(dot = strrchr(srcbuf,'.')))
c6e3d1
		return -1;
c6e3d1
	else if ((size_t)(mark - dot) > sizeof(rev))
c6e3d1
		return -1;
c6e3d1
	else {
c6e3d1
		strcpy(rev,dot);
239674
		*dot = 0;
c6e3d1
	}
c6e3d1
c6e3d1
	/* minor */
c6e3d1
	if (!(dot = strrchr(srcbuf,'.')))
c6e3d1
		return -1;
c6e3d1
	else if ((size_t)(mark - dot) > sizeof(minor))
c6e3d1
		return -1;
c6e3d1
	else {
c6e3d1
		strcpy(minor,dot);
239674
		*dot = 0;
c6e3d1
	}
c6e3d1
c6e3d1
	/* major */
c6e3d1
	if (!(dot = strrchr(srcbuf,'.')))
c6e3d1
		return -1;
c6e3d1
	else if ((size_t)(mark - dot) > sizeof(major))
c6e3d1
		return -1;
c6e3d1
	else {
c6e3d1
		strcpy(major,dot);
239674
		*dot = 0;
c6e3d1
	}
c6e3d1
c6e3d1
	if (!(dot = strrchr(srcbuf,'.')))
c6e3d1
		return -1;
c6e3d1
c6e3d1
	/* .libs/libfoo.so.def.host */
c6e3d1
	if ((size_t)snprintf(hostlnk,sizeof(hostlnk),"%s.def.host",
c6e3d1
			srcbuf) >= sizeof(hostlnk))
c6e3d1
		return -1;
c6e3d1
c6e3d1
	/* libfoo.so.def.{flavor} */
c6e3d1
	if (slbt_readlink(hostlnk,hosttag,sizeof(hosttag)))
c6e3d1
		return -1;
c6e3d1
c6e3d1
	/* host/flabor */
c6e3d1
	if (!(host = strrchr(hosttag,'.')))
c6e3d1
		return -1;
c6e3d1
	else
c6e3d1
		host++;
c6e3d1
c6e3d1
	/* symlink-based alternate host */
c6e3d1
	if (slbt_set_alternate_host(dctx,host,host))
c6e3d1
		return -1;
c6e3d1
c6e3d1
	/* .libs/libfoo.x.y.z.lib.a */
c6e3d1
	sprintf(dot,"%s%s%s%s",
c6e3d1
		major,minor,rev,
c6e3d1
		dctx->cctx->asettings.impsuffix);
c6e3d1
c6e3d1
	/* copy: .libs/libfoo.x.y.z.lib.a --> dstdir */
c6e3d1
	if (slbt_copy_file(dctx,ectx,srcbuf,dstdir))
c6e3d1
		return -1;
c6e3d1
c6e3d1
	/* .libs/libfoo.x.lib.a */
c6e3d1
	sprintf(dot,"%s%s",
c6e3d1
		major,
c6e3d1
		dctx->cctx->asettings.impsuffix);
c6e3d1
c6e3d1
	/* copy: .libs/libfoo.x.lib.a --> dstdir */
c6e3d1
	if (slbt_copy_file(dctx,ectx,srcbuf,dstdir))
c6e3d1
		return -1;
c6e3d1
c6e3d1
	/* /dstdir/libfoo.lib.a */
c6e3d1
	strcpy(implib,slash);
c6e3d1
	strcpy(dot,dctx->cctx->asettings.impsuffix);
c6e3d1
c6e3d1
	if ((size_t)snprintf(hostlnk,sizeof(hostlnk),"%s/%s",
c6e3d1
			dstdir,slash) >= sizeof(hostlnk))
c6e3d1
		return -1;
c6e3d1
c6e3d1
	if (slbt_create_symlink(
c6e3d1
			dctx,ectx,
c6e3d1
			implib,
c6e3d1
			hostlnk,
c6e3d1
			false))
c6e3d1
		return -1;
c6e3d1
c6e3d1
	return 0;
c6e3d1
}
c6e3d1
258073
static int slbt_exec_install_entry(
258073
	const struct slbt_driver_ctx *	dctx,
258073
	struct slbt_exec_ctx *		ectx,
258073
	struct argv_entry *		entry,
258073
	struct argv_entry *		last,
258073
	struct argv_entry *		dest,
258073
	char *				dstdir,
258073
	char **				src,
258073
	char **				dst)
258073
{
258073
	int		ret;
258073
	char *		dot;
258073
	char *		base;
258073
	char *		slash;
258073
	char		target  [PATH_MAX];
258073
	char		srcfile [PATH_MAX];
258073
	char		dstfile [PATH_MAX];
258073
	char		slnkname[PATH_MAX];
258073
	char		dlnkname[PATH_MAX];
258073
	char		lasource[PATH_MAX];
5e5804
	bool		fexe = false;
1c05ca
	bool		fpe;
112a2b
	bool		frelease;
5e5804
	struct stat	st;
5e5804
5e5804
	/* executable wrapper? */
5e5804
	if ((size_t)snprintf(slnkname,sizeof(slnkname),"%s.exe.wrapper",
5e5804
			entry->arg) >= sizeof(slnkname))
5e5804
		return -1;
5e5804
5e5804
	fexe = stat(slnkname,&st)
5e5804
		? false
5e5804
		: true;
258073
af8d17
	dot  = strrchr(entry->arg,'.');
af8d17
af8d17
	/* .lai --> .la */
af8d17
	if (!fexe && dot && !strcmp(dot,".lai"))
af8d17
		dot[3] = 0;
af8d17
258073
	/* .la ? */
af8d17
	if (!fexe && (!dot || strcmp(dot,".la"))) {
258073
		*src = (char *)entry->arg;
258073
		*dst = dest ? 0 : (char *)last->arg;
258073
258073
		if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT))
258073
			if (slbt_output_install(dctx,ectx))
258073
				return -1;
258073
258073
		return (((ret = slbt_spawn(ectx,true)) < 0) || ectx->exitcode)
258073
			? -1 : 0;
258073
	}
258073
9cce84
	/* *dst: consider: cp libfoo.la /dest/dir/libfoo.la */
9cce84
	if ((*dst = dest ? 0 : (char *)last->arg))
9cce84
		if ((dot = strrchr(last->arg,'.')))
9cce84
			if (!(strcmp(dot,".la")))
9cce84
				*dst = dstdir;
9cce84
258073
	/* srcfile */
258073
	if (strlen(entry->arg) + strlen(".libs/") >= (PATH_MAX-1))
258073
		return -1;
258073
258073
	strcpy(lasource,entry->arg);
258073
258073
	if ((slash = strrchr(lasource,'/'))) {
239674
		*slash++ = 0;
258073
		sprintf(srcfile,"%s/.libs/%s",lasource,slash);
258073
	} else
258073
		sprintf(srcfile,".libs/%s",lasource);
258073
5e5804
	/* executable? */
5e5804
	if (fexe) {
5e5804
		*src = srcfile;
5e5804
5e5804
		if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT))
5e5804
			if (slbt_output_install(dctx,ectx))
5e5804
				return -1;
5e5804
5e5804
		return (((ret = slbt_spawn(ectx,true)) < 0) || ectx->exitcode)
5e5804
			? -1 : 0;
5e5804
	}
258073
f38ea1
	/* libfoo.a */
f38ea1
	dot = strrchr(srcfile,'.');
f38ea1
	strcpy(dot,dctx->cctx->settings.arsuffix);
f38ea1
f38ea1
	if (slbt_copy_file(dctx,ectx,
f38ea1
			srcfile,
9cce84
			dest ? (char *)dest->arg : *dst))
f38ea1
		return -1;
f38ea1
112a2b
	/* dot/suffix */
5e5804
	strcpy(slnkname,srcfile);
258073
	dot = strrchr(slnkname,'.');
112a2b
112a2b
	/* libfoo.a --> libfoo.so.release */
112a2b
	sprintf(dot,"%s.release",dctx->cctx->settings.dsosuffix);
112a2b
	frelease = stat(slnkname,&st) ? false : true;
112a2b
112a2b
	/* libfoo.a --> libfoo.so */
b3c21a
	strcpy(dot,dctx->cctx->settings.dsosuffix);
258073
1c05ca
	/* PE support: does .libs/libfoo.so.def exist? */
1c05ca
	if ((size_t)snprintf(dstfile,sizeof(dstfile),"%s.def",
1c05ca
			slnkname) >= sizeof(dstfile))
1c05ca
		return -1;
1c05ca
1c05ca
	fpe = stat(dstfile,&st) ? false : true;
1c05ca
258073
	/* basename */
258073
	if ((base = strrchr(slnkname,'/')))
258073
		base++;
258073
	else
258073
		base = slnkname;
258073
258073
	/* source (build) symlink target */
e8159c
	if (slbt_readlink(slnkname,target,sizeof(target)) < 0) {
b107e2
		/* -avoid-version? */
b107e2
		if (stat(slnkname,&st))
b107e2
			return -1;
b107e2
b107e2
		/* dstfile */
b107e2
		if ((size_t)snprintf(dstfile,sizeof(dstfile),"%s/%s",
b107e2
				dstdir,base) >= sizeof(dstfile))
b107e2
			return -1;
b107e2
b107e2
		/* single spawn, no symlinks */
b107e2
		*src = slnkname;
b107e2
		*dst = dest ? 0 : dstfile;
b107e2
b107e2
		if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT))
b107e2
			if (slbt_output_install(dctx,ectx))
b107e2
				return -1;
b107e2
b107e2
		if (((ret = slbt_spawn(ectx,true)) < 0) || ectx->exitcode)
b107e2
			return -1;
b107e2
b107e2
		return 0;
b107e2
	}
258073
dba2af
	/* -all-static? */
dba2af
	if (!strcmp(target,"/dev/null"))
dba2af
		return 0;
dba2af
258073
	/* srcfile: .libs/libfoo.so.x.y.z */
258073
	slash = strrchr(srcfile,'/');
258073
	strcpy(++slash,target);
258073
258073
	/* dstfile */
258073
	if (!dest)
258073
		if ((size_t)snprintf(dstfile,sizeof(dstfile),"%s/%s",
258073
				dstdir,target) >= sizeof(dstfile))
258073
			return -1;
258073
258073
	/* spawn */
258073
	*src = srcfile;
258073
	*dst = dest ? 0 : dstfile;
258073
258073
	if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT))
258073
		if (slbt_output_install(dctx,ectx))
258073
			return -1;
258073
258073
	if (((ret = slbt_spawn(ectx,true)) < 0) || ectx->exitcode)
258073
		return -1;
258073
258073
	/* destination symlink: dstdir/libfoo.so */
258073
	if ((size_t)snprintf(dlnkname,sizeof(dlnkname),"%s/%s",
258073
			dstdir,base) >= sizeof(dlnkname))
258073
		return -1;
258073
258073
	/* create symlink: libfoo.so --> libfoo.so.x.y.z */
258073
	if (slbt_create_symlink(
258073
			dctx,ectx,
258073
			target,dlnkname,
258073
			false))
258073
		return -1;
258073
112a2b
	if (frelease)
112a2b
		return 0;
112a2b
258073
	/* libfoo.so.x.y.z --> libfoo.so.x */
258073
	strcpy(slnkname,target);
258073
258073
	if ((dot = strrchr(slnkname,'.')))
239674
		*dot = 0;
258073
	else
258073
		return -1;
258073
258073
	if ((dot = strrchr(slnkname,'.')))
239674
		*dot = 0;
258073
	else
258073
		return -1;
258073
258073
	/* destination symlink: dstdir/libfoo.so.x */
258073
	if ((size_t)snprintf(dlnkname,sizeof(dlnkname),"%s/%s",
258073
			dstdir,slnkname) >= sizeof(dlnkname))
258073
		return -1;
258073
1c05ca
	if (fpe) {
1c05ca
		/* copy: .libs/libfoo.so.x.y.z --> libfoo.so.x */
1c05ca
		if (slbt_copy_file(
1c05ca
				dctx,ectx,
1c05ca
				srcfile,
1c05ca
				dlnkname))
1c05ca
			return -1;
c6e3d1
c6e3d1
		/* import libraries */
c6e3d1
		if (slbt_exec_install_import_libraries(
c6e3d1
				dctx,ectx,
c6e3d1
				srcfile,
c6e3d1
				dstdir))
c6e3d1
			return -1;
1c05ca
	} else {
1c05ca
		/* create symlink: libfoo.so.x --> libfoo.so.x.y.z */
1c05ca
		if (slbt_create_symlink(
1c05ca
				dctx,ectx,
1c05ca
				target,dlnkname,
1c05ca
				false))
1c05ca
			return -1;
1c05ca
	}
258073
258073
	return 0;
258073
}
258073
258073
int slbt_exec_install(
258073
	const struct slbt_driver_ctx *	dctx,
258073
	struct slbt_exec_ctx *		ectx)
258073
{
258073
	int				ret;
258073
	char **				argv;
258073
	char **				src;
258073
	char **				dst;
258073
	struct slbt_exec_ctx *		actx;
258073
	struct argv_meta *		meta;
258073
	struct argv_entry *		entry;
258073
	struct argv_entry *		copy;
258073
	struct argv_entry *		dest;
258073
	struct argv_entry *		last;
258073
	char				dstdir[PATH_MAX];
258073
	const struct argv_option *	options = slbt_install_options;
258073
258073
	/* context */
258073
	if (ectx)
258073
		actx = 0;
258073
	else if ((ret = slbt_get_exec_ctx(dctx,&ectx)))
258073
		return ret;
258073
	else
258073
		actx = ectx;
258073
258073
	/* initial state, install mode skin */
258073
	slbt_reset_arguments(ectx);
258073
	slbt_disable_placeholders(ectx);
258073
	argv = ectx->cargv;
258073
258073
	/* missing arguments? */
258073
	if (!argv[1] && (dctx->cctx->drvflags & SLBT_DRIVER_VERBOSITY_USAGE))
258073
		return slbt_install_usage(dctx->program,0,options,0);
258073
258073
	/* <install> argv meta */
258073
	if (!(meta = argv_get(
258073
			argv,
258073
			options,
258073
			dctx->cctx->drvflags & SLBT_DRIVER_VERBOSITY_ERRORS
258073
				? ARGV_VERBOSITY_ERRORS
258073
				: ARGV_VERBOSITY_NONE)))
258073
		return slbt_exec_install_fail(actx,meta);
258073
258073
	/* dest, alternate argument vector options */
258073
	argv = ectx->altv;
258073
	copy = meta->entries;
258073
	dest = 0;
258073
	last = 0;
258073
258073
	*argv++ = ectx->cargv[0];
258073
258073
	for (entry=meta->entries; entry->fopt || entry->arg; entry++) {
258073
		if (entry->fopt) {
258073
			switch (entry->tag) {
258073
				case TAG_INSTALL_COPY:
258073
					*argv++ = "-c";
258073
					copy = entry;
258073
					break;
258073
258073
				case TAG_INSTALL_MKDIR:
258073
					*argv++ = "-d";
258073
					copy = 0;
258073
					break;
258073
258073
				case TAG_INSTALL_TARGET_MKDIR:
258073
					*argv++ = "-D";
258073
					copy = 0;
258073
					break;
258073
258073
				case TAG_INSTALL_STRIP:
258073
					*argv++ = "-s";
258073
					break;
258073
258073
				case TAG_INSTALL_PRESERVE:
258073
					*argv++ = "-p";
258073
					break;
258073
258073
				case TAG_INSTALL_USER:
258073
					*argv++ = "-o";
258073
					break;
258073
258073
				case TAG_INSTALL_GROUP:
258073
					*argv++ = "-g";
258073
					break;
258073
258073
				case TAG_INSTALL_MODE:
258073
					*argv++ = "-m";
258073
					break;
258073
258073
				case TAG_INSTALL_DSTDIR:
258073
					*argv++ = "-t";
258073
					dest = entry;
258073
					break;
258073
			}
258073
258073
			if (entry->fval)
258073
				*argv++ = (char *)entry->arg;
258073
		} else
258073
			last = entry;
258073
	}
258073
258073
	/* install */
258073
	if (copy) {
258073
		/* using alternate argument vector */
258073
		ectx->argv    = ectx->altv;
258073
		ectx->program = ectx->altv[0];
258073
258073
		/* marks */
258073
		src = argv++;
258073
		dst = argv++;
258073
258073
		/* dstdir */
258073
		if (slbt_exec_install_init_dstdir(dest,last,dstdir))
258073
			return slbt_exec_install_fail(actx,meta);
258073
258073
		/* install entries one at a time */
258073
		for (entry=meta->entries; entry->fopt || entry->arg; entry++)
258073
			if (!entry->fopt && (dest || (entry != last)))
258073
				if (slbt_exec_install_entry(
258073
						dctx,ectx,
258073
						entry,last,
258073
						dest,dstdir,
258073
						src,dst))
258073
					return slbt_exec_install_fail(actx,meta);
258073
	} else {
258073
		/* using original argument vector */
258073
		ectx->argv    = ectx->cargv;
258073
		ectx->program = ectx->cargv[0];
258073
258073
		/* spawn */
258073
		if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT))
258073
			if (slbt_output_install(dctx,ectx))
258073
				return -1;
258073
258073
		if (((ret = slbt_spawn(ectx,true)) < 0) || ectx->exitcode)
258073
			return slbt_exec_install_fail(actx,meta);
258073
	}
258073
258073
	argv_free(meta);
258073
	slbt_free_exec_ctx(actx);
258073
258073
	return 0;
258073
}