Blame src/driver/ntux_driver_ctx.c

26e14f
/***********************************************************/
26e14f
/*  ntux: native translation und extension                 */
db57dd
/*  Copyright (C) 2016--2018  Z. Gilboa                    */
26e14f
/*  Released under GPLv2 and GPLv3; see COPYING.NTUX.      */
26e14f
/***********************************************************/
26e14f
471bb0
#include <psxabi/sys_abitypes.h>
ec0005
#include <psxabi/sys_fcntl.h>
471bb0
#include <psxscl/psxscl.h>
471bb0
471bb0
#include <ntapi/nt_tty.h>
26e14f
#include <stdint.h>
26e14f
26e14f
#include <ntux/ntux.h>
26e14f
#include "ntux_nolibc_impl.h"
26e14f
26e14f
#define ARGV_DRIVER
26e14f
26e14f
#include "ntux_version.h"
26e14f
#include "ntux_driver_impl.h"
26e14f
#include "argv/argv.h"
26e14f
26e14f
/* framework integration */
26e14f
#include <psxtypes/section/midipix.h>
26e14f
26e14f
__attr_section_decl__(".midipix")
26e14f
static const nt_tty_affiliation tty_affiliation
26e14f
	__attr_section__(".midipix")
26e14f
	= NT_TTY_AFFILIATION_DEFAULT;
26e14f
c27a50
/* ntux command names */
c27a50
static const char * const ntux_cmd_name[NTUX_CMD_CAP] = {
c27a50
	[NTUX_CMD_STAT]       = "stat",
c27a50
	[NTUX_CMD_SPAWN]      = "spawn",
c27a50
	[NTUX_CMD_STRACE]     = "strace",
80f43a
	[NTUX_CMD_CHMOD]      = "chmod",
c27a50
};
c27a50
c27a50
/* ntux command options */
c27a50
static const struct argv_option * ntux_cmd_options[NTUX_CMD_CAP] = {
c27a50
	[NTUX_CMD_DEFAULT]    = ntux_default_options,
c27a50
	[NTUX_CMD_STAT]       = ntux_default_options,
c27a50
	[NTUX_CMD_SPAWN]      = ntux_spawn_options,
c27a50
	[NTUX_CMD_STRACE]     = ntux_strace_options,
80f43a
	[NTUX_CMD_CHMOD]      = ntux_chmod_options,
c27a50
};
c27a50
26e14f
/* package info */
26e14f
static const struct ntux_source_version ntux_src_version = {
26e14f
	NTUX_TAG_VER_MAJOR,
26e14f
	NTUX_TAG_VER_MINOR,
26e14f
	NTUX_TAG_VER_PATCH,
26e14f
	NTUX_GIT_VERSION
26e14f
};
26e14f
26e14f
struct ntux_driver_ctx_alloc {
26e14f
	struct argv_meta *		meta;
26e14f
	struct ntux_driver_ctx_impl	ctx;
26e14f
	uint64_t			guard;
26e14f
	const char *			units[];
26e14f
};
26e14f
26e14f
static uint32_t ntux_argv_flags(uint32_t flags)
26e14f
{
ecd0a7
	uint32_t ret = ARGV_CLONE_VECTOR;
26e14f
26e14f
	if (flags & NTUX_DRIVER_VERBOSITY_NONE)
26e14f
		ret |= ARGV_VERBOSITY_NONE;
26e14f
26e14f
	if (flags & NTUX_DRIVER_VERBOSITY_ERRORS)
26e14f
		ret |= ARGV_VERBOSITY_ERRORS;
26e14f
26e14f
	if (flags & NTUX_DRIVER_VERBOSITY_STATUS)
26e14f
		ret |= ARGV_VERBOSITY_STATUS;
26e14f
26e14f
	return ret;
26e14f
}
26e14f
26e14f
static int ntux_driver_usage(
ec0005
	int				fdout,
26e14f
	const char *			program,
26e14f
	const char *			arg,
2404cd
	const struct argv_option **	optv,
c27a50
	struct argv_meta *		meta,
c27a50
	enum ntux_cmd			cmd)
26e14f
{
c27a50
	char		header[512];
c27a50
	char *		cmdarg[2];
c27a50
	const char *	cmdname;
c27a50
c27a50
	if (cmd == NTUX_CMD_DEFAULT) {
c27a50
		cmdarg[0] = "";
c27a50
		cmdarg[1] = "";
c27a50
		cmdname   = "";
c27a50
	} else {
c27a50
		cmdarg[0] = " (--cmd=";
c27a50
		cmdarg[1] = ")";
c27a50
		cmdname   = ntux_cmd_name[cmd];
c27a50
	}
26e14f
26e14f
	snprintf(header,sizeof(header),
6a7c02
		"Usage: %s [options] ...\n"
7deda5
		"Usage: %s [options] [--cmd=<command>] <arg> <arg> ...\n\n"
7deda5
		"Notes: --cmd must precede all non-option arguments, as well as\n"
7deda5
		"       all arguments that are specific to the selected command;\n"
7deda5
		"       for commands that spawn other programs (e.g. spawn, strace),\n"
7deda5
		"       the first non-option argument must specify the name of the\n"
7deda5
		"       program to be executed.\n\n"
c27a50
		"Options%s%s%s:\n",
c27a50
		program,program,cmdarg[0],cmdname,cmdarg[1]);
26e14f
ec0005
	argv_usage(fdout,header,optv,arg);
26e14f
	argv_free(meta);
26e14f
26e14f
	return NTUX_USAGE;
26e14f
}
26e14f
26e14f
static struct ntux_driver_ctx_impl * ntux_driver_ctx_alloc(
26e14f
	struct argv_meta *		meta,
ec0005
	const struct ntux_fd_ctx *	fdctx,
26e14f
	const struct ntux_common_ctx *	cctx,
26e14f
	size_t				nunits)
26e14f
{
26e14f
	struct ntux_driver_ctx_alloc *	ictx;
26e14f
	size_t				size;
26e14f
	struct argv_entry *		entry;
26e14f
	const char **			units;
26e14f
	int				elements;
26e14f
26e14f
	(void)tty_affiliation;
26e14f
26e14f
	size =  sizeof(struct ntux_driver_ctx_alloc);
26e14f
	size += (nunits+1)*sizeof(const char *);
26e14f
26e14f
	if (!(ictx = calloc(1,size)))
26e14f
		return 0;
26e14f
26e14f
	elements = sizeof(ictx->ctx.erribuf) / sizeof(*ictx->ctx.erribuf);
26e14f
26e14f
	ictx->ctx.errinfp  = &ictx->ctx.erriptr[0];
26e14f
	ictx->ctx.erricap  = &ictx->ctx.erriptr[--elements];
26e14f
ec0005
	memcpy(&ictx->ctx.fdctx,fdctx,sizeof(*fdctx));
ec0005
	memcpy(&ictx->ctx.cctx,cctx,sizeof(*cctx));
26e14f
26e14f
	for (entry=meta->entries,units=ictx->units; entry->fopt || entry->arg; entry++)
26e14f
		if (!entry->fopt)
26e14f
			*units++ = entry->arg;
26e14f
26e14f
	ictx->meta = meta;
26e14f
	ictx->ctx.ctx.units = ictx->units;
26e14f
	ictx->ctx.ctx.errv  = ictx->ctx.errinfp;
26e14f
	return &ictx->ctx;
26e14f
}
26e14f
7deda5
static int ntux_cctx_update(
7deda5
	const char *			program,
7deda5
	const struct argv_option **	optv,
7deda5
	struct argv_meta *		meta,
7deda5
	uint32_t			flags,
ec0005
	const struct ntux_fd_ctx *	fdctx,
7deda5
	struct ntux_common_ctx *	cctx,
7deda5
	size_t *			nunits)
7deda5
{
7deda5
	struct argv_entry *		entry;
7deda5
7deda5
	/* get options, count units */
7deda5
	for (entry=meta->entries; entry->fopt || entry->arg; entry++) {
7deda5
		if (entry->fopt) {
7deda5
			switch (entry->tag) {
7deda5
				case TAG_HELP:
7deda5
					if (flags & NTUX_DRIVER_VERBOSITY_USAGE)
7deda5
						return ntux_driver_usage(
ec0005
							fdctx->fdout,
7deda5
							program,entry->arg,
c27a50
							optv,0,cctx->cmd);
7deda5
7deda5
				case TAG_VERSION:
7deda5
					cctx->drvflags |= NTUX_DRIVER_VERSION;
7deda5
					break;
7deda5
80f43a
				case TAG_VERBOSE:
80f43a
					cctx->drvflags |= NTUX_DRIVER_VERBOSE;
80f43a
					break;
80f43a
80f43a
				case TAG_SILENT:
80f43a
					cctx->drvflags |= NTUX_DRIVER_SILENT;
80f43a
					break;
80f43a
7deda5
				case TAG_CMD:
7deda5
					if (*nunits)
7deda5
						return ntux_driver_usage(
ec0005
							fdctx->fderr,
7deda5
							program,entry->arg,
c27a50
							optv,0,cctx->cmd);
7deda5
7deda5
					if (!strcmp(entry->arg,"stat"))
7deda5
						cctx->cmd = NTUX_CMD_STAT;
7deda5
7deda5
					else if (!strcmp(entry->arg,"spawn"))
7deda5
						cctx->cmd = NTUX_CMD_SPAWN;
7deda5
7deda5
					else if (!strcmp(entry->arg,"strace"))
7deda5
						cctx->cmd = NTUX_CMD_STRACE;
7deda5
80f43a
					else if (!strcmp(entry->arg,"chmod"))
80f43a
						cctx->cmd = NTUX_CMD_CHMOD;
80f43a
7deda5
					break;
9682ca
9682ca
				case TAG_LOADER:
9682ca
					cctx->loader = entry->arg;
9682ca
					break;
9682ca
9682ca
				case TAG_LOGFILE:
9682ca
					cctx->logfile = entry->arg;
9682ca
					break;
80f43a
80f43a
				case TAG_REFMODE:
80f43a
					cctx->refmode = entry->arg;
80f43a
					break;
80f43a
80f43a
				case TAG_STRMODE:
80f43a
					cctx->strmode = entry->arg;
80f43a
					break;
80f43a
80f43a
				case TAG_RECURSIVE:
80f43a
					cctx->drvflags |= NTUX_DRIVER_RECURSIVE;
80f43a
					break;
80f43a
80f43a
				case TAG_CHANGES:
80f43a
					cctx->drvflags |= NTUX_DRIVER_CHANGES;
80f43a
					break;
80f43a
80f43a
				case TAG_ROOTED:
80f43a
					cctx->drvflags &= ~(uint64_t)NTUX_DRIVER_ROOTLESS;
80f43a
					cctx->drvflags |= NTUX_DRIVER_ROOTED;
80f43a
					break;
80f43a
80f43a
				case TAG_ROOTLESS:
80f43a
					cctx->drvflags &= ~(uint64_t)NTUX_DRIVER_ROOTED;
80f43a
					cctx->drvflags |= NTUX_DRIVER_ROOTLESS;
80f43a
					break;
7deda5
			}
7deda5
		} else {
7deda5
			(*nunits)++;
7deda5
		}
7deda5
	}
7deda5
7deda5
	return 0;
7deda5
}
7deda5
26e14f
static int ntux_get_driver_ctx_fail(struct argv_meta * meta)
26e14f
{
26e14f
	argv_free(meta);
26e14f
	return -1;
26e14f
}
26e14f
c27a50
static int ntux_cmd_from_program(const char * program)
c27a50
{
c27a50
	const char *	dot;
c27a50
	const char *	hyphen;
c27a50
	const char *	mark;
c27a50
c27a50
	dot    = strrchr(program,'.');
c27a50
	hyphen = strrchr(program,'-');
c27a50
c27a50
	if (hyphen > dot)
c27a50
		mark = ++hyphen;
c27a50
	else if (dot > hyphen)
c27a50
		mark = ++dot;
c27a50
	else
c27a50
		mark = program;
c27a50
c27a50
	if (!strcmp(mark,"stat"))
c27a50
		return NTUX_CMD_STAT;
c27a50
	else if (!strcmp(mark,"spawn"))
c27a50
		return NTUX_CMD_SPAWN;
c27a50
	else if (!strcmp(mark,"strace"))
c27a50
		return NTUX_CMD_STRACE;
80f43a
	else if (!strcmp(mark,"chmod"))
80f43a
		return NTUX_CMD_CHMOD;
c27a50
c27a50
	return NTUX_CMD_DEFAULT;
c27a50
}
c27a50
26e14f
int ntux_get_driver_ctx(
26e14f
	char **				argv,
26e14f
	char **				envp,
26e14f
	uint32_t			flags,
ec0005
	const struct ntux_fd_ctx *	fdctx,
26e14f
	struct ntux_driver_ctx **	pctx)
26e14f
{
7deda5
	struct ntux_driver_ctx_impl *	ictx;
26e14f
	struct ntux_common_ctx		cctx;
2404cd
	const struct argv_option *	optv[NTUX_OPTV_ELEMENTS];
26e14f
	struct argv_meta *		meta;
26e14f
	size_t				nunits;
26e14f
	const char *			program;
7deda5
	char **				parg;
4fe92c
	char **				pargcap;
7deda5
	char **				cmdargv;
7deda5
	char *				cmdmark;
7deda5
	char **				execargv;
7deda5
	char *				execarg;
7deda5
	struct argv_ctx			ctx = {ARGV_VERBOSITY_NONE,
7deda5
                                               ARGV_MODE_SCAN,
7deda5
                                               0,0,0,0,0,0,0};
7deda5
ec0005
	/* fdctx */
ec0005
	if (!fdctx) {
ec0005
		fdctx = &(const struct ntux_fd_ctx) {
ec0005
			.fdin  = STDIN_FILENO,
ec0005
			.fdout = STDOUT_FILENO,
ec0005
			.fderr = STDERR_FILENO,
ec0005
			.fdlog = (-1),
ec0005
			.fdcwd = AT_FDCWD,
ec0005
			.fddst = AT_FDCWD,
ec0005
		};
ec0005
	}
ec0005
ec0005
	/* cctx */
26e14f
	memset(&cctx,0,sizeof(cctx));
7deda5
7deda5
	nunits	      = 0;
7deda5
	execargv      = 0;
c27a50
	program       = argv_program_name(argv[0]);
c27a50
	cctx.cmd      = ntux_cmd_from_program(program);
c27a50
	cctx.drvflags = flags;
7deda5
7deda5
	/* missing arguments? */
c27a50
	argv_optv_init(ntux_cmd_options[cctx.cmd],optv);
26e14f
26e14f
	if (!argv[1] && (flags & NTUX_DRIVER_VERBOSITY_USAGE))
ec0005
		return ntux_driver_usage(
ec0005
			fdctx->fderr,
ec0005
			program,0,
ec0005
			optv,0,cctx.cmd);
7deda5
7deda5
	/* initial argv scan: ... --cmd=xxx ... */
dc1caf
	argv_scan(argv,optv,&ctx,0);
dc1caf
4fe92c
	/* position of last base ntux argument */
4fe92c
	if (ctx.erridx && ctx.unitidx)
4fe92c
		pargcap = &argv[ctx.unitidx];
4fe92c
4fe92c
	else if (ctx.erridx)
4fe92c
		pargcap = &argv[ctx.erridx];
4fe92c
4fe92c
	else
4fe92c
		for (pargcap=argv; *pargcap; pargcap++)
4fe92c
			(void)0;
dc1caf
dc1caf
	/* scan for --cmd */
4fe92c
	for (parg=argv, cmdargv=0; (parg
7deda5
		if (!strcmp(*parg,"--cmd") && parg[1]) {
7deda5
				cmdargv = &parg[2];
7deda5
				cmdmark = parg[2];
7deda5
		} else if (!strncmp(*parg,"--cmd=",6)) {
7deda5
				cmdargv = &parg[1];
7deda5
				cmdmark = parg[1];
7deda5
		}
7deda5
	}
26e14f
4fe92c
	/* invalid ntux arguments? */
4fe92c
	if (!ctx.erridx) {
4fe92c
		(void)0;
4fe92c
4fe92c
	} else if (&argv[ctx.erridx] >= cmdargv) {
4fe92c
		(void)0;
4fe92c
4fe92c
	} else if ((cctx.cmd == NTUX_CMD_SPAWN) && (ctx.erridx > ctx.unitidx)) {
4fe92c
		(void)0;
4fe92c
4fe92c
	} else if ((cctx.cmd == NTUX_CMD_STRACE) && (ctx.erridx > ctx.unitidx)) {
4fe92c
		(void)0;
4fe92c
4fe92c
	} else {
4fe92c
		if (flags & NTUX_DRIVER_VERBOSITY_ERRORS)
4fe92c
			argv_get(
4fe92c
				argv,optv,
4fe92c
				ARGV_VERBOSITY_ERRORS,
4fe92c
				fdctx->fderr);
4fe92c
		return -1;
4fe92c
	}
4fe92c
7deda5
	/* process argv entries preceding --cmd */
7deda5
	if (cmdargv) {
7deda5
		*cmdargv = 0;
26e14f
1a5016
		if (!(meta = argv_get(
1a5016
				argv,optv,
1a5016
				ntux_argv_flags(flags),
ec0005
				fdctx->fderr)))
7deda5
			return -1;
6a7c02
ec0005
		if (ntux_cctx_update(
ec0005
				program,optv,meta,flags,
ec0005
				fdctx,&cctx,&nunits)) {
7deda5
			argv_free(meta);
7deda5
			return -1;
7deda5
		}
7deda5
89e9e6
		argv_free(meta);
89e9e6
7deda5
		*cmdargv-- = cmdmark;
7deda5
		*cmdargv   = argv[0];
7deda5
		argv       = cmdargv;
7deda5
	}
7deda5
7deda5
	/* set option vector by command */
7deda5
	if (cctx.cmd == NTUX_CMD_SPAWN)
7deda5
		argv_optv_init(ntux_spawn_options,optv);
7deda5
7deda5
	else if (cctx.cmd == NTUX_CMD_STRACE)
7deda5
		argv_optv_init(ntux_strace_options,optv);
7deda5
80f43a
	else if (cctx.cmd == NTUX_CMD_CHMOD)
80f43a
		argv_optv_init(ntux_chmod_options,optv);
80f43a
7deda5
	/* spawn, strace */
7deda5
	if ((cctx.cmd == NTUX_CMD_SPAWN) || (cctx.cmd == NTUX_CMD_STRACE)) {
7deda5
		argv_scan(argv,optv,&ctx,0);
7deda5
7deda5
		if (ctx.erridx && !ctx.unitidx) {
7deda5
			if (flags & NTUX_DRIVER_VERBOSITY_ERRORS)
7deda5
				argv_get(
7deda5
					argv,optv,
1a5016
					ntux_argv_flags(flags),
ec0005
					fdctx->fderr);
7deda5
			return -1;
7deda5
		}
7deda5
7deda5
		if (!ctx.unitidx && (flags & NTUX_DRIVER_VERBOSITY_USAGE))
ec0005
			ntux_driver_usage(
ec0005
				fdctx->fderr,
ec0005
				program,0,
ec0005
				optv,0,cctx.cmd);
7deda5
7deda5
		if (!ctx.unitidx)
7deda5
			return NTUX_ERROR;
7deda5
7deda5
		execargv  = &argv[ctx.unitidx];
7deda5
		execarg   = argv[ctx.unitidx];
7deda5
		*execargv = 0;
7deda5
	}
7deda5
7deda5
	/* obtain ntux's own arguments */
1a5016
	if (!(meta = argv_get(
1a5016
			argv,optv,
1a5016
			ntux_argv_flags(flags),
ec0005
			fdctx->fderr)))
7deda5
		return -1;
7deda5
ec0005
	if (ntux_cctx_update(
ec0005
			program,optv,meta,flags,
ec0005
			fdctx,&cctx,&nunits)) {
7deda5
		argv_free(meta);
7deda5
		return -1;
7deda5
	}
7deda5
7deda5
	/* spawn, strace: exec argv */
7deda5
	if (execargv) {
7deda5
		*execargv  = execarg;
7deda5
		cctx.sargv = execargv;
7deda5
		cctx.senvp = envp;
7deda5
		nunits     = 0;
26e14f
	}
26e14f
7deda5
	/* finalize */
c59ae6
	if (nunits && !cctx.cmd)
ec0005
		return ntux_driver_usage(
ec0005
			fdctx->fderr,
ec0005
			program,0,
ec0005
			optv,meta,
ec0005
			NTUX_CMD_DEFAULT);
c59ae6
ec0005
	if (!(ictx = ntux_driver_ctx_alloc(meta,fdctx,&cctx,nunits)))
26e14f
		return ntux_get_driver_ctx_fail(meta);
26e14f
7deda5
	ictx->ctx.program = program;
7deda5
	ictx->ctx.cctx    = &ictx->cctx;
7deda5
	*pctx             = &ictx->ctx;
26e14f
7deda5
	return 0;
26e14f
}
26e14f
26e14f
static void ntux_free_driver_ctx_impl(struct ntux_driver_ctx_alloc * ictx)
26e14f
{
26e14f
	argv_free(ictx->meta);
26e14f
	free(ictx);
26e14f
}
26e14f
26e14f
void ntux_free_driver_ctx(struct ntux_driver_ctx * ctx)
26e14f
{
26e14f
	struct ntux_driver_ctx_alloc *	ictx;
26e14f
	uintptr_t			addr;
26e14f
26e14f
	if (ctx) {
26e14f
		addr = (uintptr_t)ctx - offsetof(struct ntux_driver_ctx_impl,ctx);
26e14f
		addr = addr - offsetof(struct ntux_driver_ctx_alloc,ctx);
26e14f
		ictx = (struct ntux_driver_ctx_alloc *)addr;
26e14f
		ntux_free_driver_ctx_impl(ictx);
26e14f
	}
26e14f
}
26e14f
26e14f
const struct ntux_source_version * ntux_source_version(void)
26e14f
{
26e14f
	return &ntux_src_version;
26e14f
}
ec0005
ec0005
int ntux_get_driver_fdctx(
ec0005
	const struct ntux_driver_ctx *	dctx,
ec0005
	struct ntux_fd_ctx *		fdctx)
ec0005
{
ec0005
	struct ntux_driver_ctx_impl *	ictx;
ec0005
ec0005
	ictx = ntux_get_driver_ictx(dctx);
ec0005
ec0005
	fdctx->fdin  = ictx->fdctx.fdin;
ec0005
	fdctx->fdout = ictx->fdctx.fdout;
ec0005
	fdctx->fderr = ictx->fdctx.fderr;
ec0005
	fdctx->fdlog = ictx->fdctx.fdlog;
ec0005
	fdctx->fdcwd = ictx->fdctx.fdcwd;
ec0005
	fdctx->fddst = ictx->fdctx.fddst;
ec0005
ec0005
	return 0;
ec0005
}
ec0005
ec0005
int ntux_set_driver_fdctx(
ec0005
	struct ntux_driver_ctx *	dctx,
ec0005
	const struct ntux_fd_ctx *	fdctx)
ec0005
{
ec0005
	struct ntux_driver_ctx_impl *	ictx;
ec0005
ec0005
	ictx = ntux_get_driver_ictx(dctx);
ec0005
ec0005
	ictx->fdctx.fdin  = fdctx->fdin;
ec0005
	ictx->fdctx.fdout = fdctx->fdout;
ec0005
	ictx->fdctx.fderr = fdctx->fderr;
ec0005
	ictx->fdctx.fdlog = fdctx->fdlog;
ec0005
	ictx->fdctx.fdcwd = fdctx->fdcwd;
ec0005
	ictx->fdctx.fddst = fdctx->fddst;
ec0005
ec0005
	return 0;
ec0005
}