Blob Blame History Raw
/*********************************************************/
/*  toksvc: a framework-native token broker service      */
/*  Copyright (C) 2020  Z. Gilboa                        */
/*  Released under GPLv2 and GPLv3; see COPYING.TOKSVC.  */
/*********************************************************/

#include <ntapi/ntapi.h>
#include <ntapi/nt_atomic.h>
#include <ntapi/nt_fsctl.h>

#include <stdint.h>

#include <toksvc/toksvc.h>
#include "toksvc_init_impl.h"
#include "toksvc_nolibc_impl.h"

#define ARGV_DRIVER

#include "toksvc_version.h"
#include "toksvc_daemon_impl.h"
#include "toksvc_dprintf_impl.h"
#include "toksvc_driver_impl.h"
#include "argv/argv.h"

#undef   strlen

/* pty integration */
#include <psxtypes/section/freestd.h>

__attr_section_decl__(".freestd")
static const nt_tty_affiliation tty_affiliation
	__attr_section__(".freestd")
	= NT_TTY_AFFILIATION_DEFAULT;

/* ntapi accessor table */
const ntapi_vtbl * toks_ntapi;

/* daemon */
static struct toks_daemon_ctx toks_daemon_ctx;
static const  nt_guid toks_daemon_default_guid = TOKS_PORT_GUID_DAEMON;
static const  wchar16_t toks_service_name[6]   = TOKS_PORT_NAME_PREFIX;

/* package info */
static const struct toks_source_version toks_src_version = {
	TOKS_TAG_VER_MAJOR,
	TOKS_TAG_VER_MINOR,
	TOKS_TAG_VER_PATCH,
	TOKSVC_GIT_VERSION
};

struct toks_driver_ctx_alloc {
	struct argv_meta *		meta;
	struct toks_driver_ctx_impl	ctx;
	uint64_t			guard;
};

struct toks_split_vector {
	char **		targv;
	char **		eargv;
};

static uint32_t toks_argv_flags(uint32_t flags)
{
	uint32_t ret = ARGV_CLONE_VECTOR;

	if (flags & TOKS_DRIVER_VERBOSITY_NONE)
		ret |= ARGV_VERBOSITY_NONE;

	if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
		ret |= ARGV_VERBOSITY_ERRORS;

	if (flags & TOKS_DRIVER_VERBOSITY_STATUS)
		ret |= ARGV_VERBOSITY_STATUS;

	return ret;
}

static int toks_driver_usage(
	const char *			program,
	const char *			arg,
	const struct argv_option **	optv,
	struct argv_meta *		meta)
{
	char header[512];

	snprintf(header,sizeof(header),
		"Usage: %s [options] <file>...\n" "Options:\n",
		program);

	argv_usage(STDOUT_FILENO,header,optv,arg);
	argv_free(meta);

	return TOKS_USAGE;
}

static int64_t toks_arg_to_int64(struct argv_entry * entry)
{
	int64_t      ret;
	const char * ch;

	for (ret=0, ch=entry->arg; *ch && (ret>=0); ch++) {
		if ((*ch < '0') || (*ch >'9'))
			return (-1);

		ret *= 10;
		ret += (*ch - '0');
	}

	return ret;
}

static int32_t toks_arg_to_int32(struct argv_entry * entry)
{
	int64_t ret = toks_arg_to_int64(entry);
	return (ret >= 0) && (ret <= 0x7fffffff)
		? ret : (-1);
}

static int32_t toks_query_performance_counters_failover(nt_filetime * ticks)
{
	(void)ticks;
	return 0;
}

static int32_t toks_daemon_library_once;

static int32_t toks_daemon_library_init(void)
{
	int32_t		status;
	int		argc;
	char **		argv;
	char **		envp;
	nt_rtdata *	rtdata;
	nt_timeout	timeout;

	switch (at_locked_cas_32(&toks_daemon_library_once,0,1)) {
		case 0:
			status = ntapi->tt_get_argv_envp_utf8(
				&argc,&argv,&envp,
				0,0,0);

			if (status) {
				at_store_32(&toks_daemon_library_once,3);
				return status;
			}

			if ((status = ntapi->tt_get_runtime_data(&rtdata,0))) {
				at_store_32(&toks_daemon_library_once,3);
				return status;
			}

			if (!rtdata->argv) {
				rtdata->argc = argc;
				rtdata->argv = argv;
				rtdata->envp = envp;
			}

			ntapi->tt_guid_copy(
				&rtdata->srv_guid,
				&toks_daemon_default_guid);

			at_locked_inc_32(&toks_daemon_library_once);
			return 0;

		case 1:
			timeout.quad = -10;

			for (; (at_locked_cas_32(&toks_daemon_library_once,0,1) == 1); )
				ntapi->zw_delay_execution(
					NT_SYNC_ALERTABLE,
					&timeout);

			return (toks_daemon_library_once == 2)
				? 0 : -1;

		case 2:
			return 0;

		case 3:
		default:
			return -1;
	}

	return NT_STATUS_INTERNAL_ERROR;
}

static struct toks_driver_ctx_impl * toks_driver_ctx_alloc(
	struct argv_meta *		meta,
	const struct toks_common_ctx *	cctx)
{
	struct toks_driver_ctx_alloc *	ictx;
	size_t				size;
	nt_runtime_data *		rtdata;

	switch (ntapi->tt_get_runtime_data(&rtdata,0)) {
		case NT_STATUS_SUCCESS:
			break;

		case NT_STATUS_MORE_PROCESSING_REQUIRED:
			toks_daemon_library_init();

			if (ntapi->tt_get_runtime_data(&rtdata,0))
				return 0;

			break;

		default:
			return 0;
	}

	size =  sizeof(struct toks_driver_ctx_alloc);

	if (!(ictx = calloc(1,size)))
		return 0;

	if (cctx)
		memcpy(&ictx->ctx.cctx,cctx,sizeof(*cctx));

	ictx->ctx.ticks.qpc         = toks_query_performance_counters_failover;
	ictx->ctx.ticks.pcfreq.quad = 0;

	ictx->meta = meta;
	ictx->ctx.rtdata = rtdata;
	return &ictx->ctx;
}

static int toks_get_driver_ctx_fail(struct argv_meta * meta)
{
	argv_free(meta);
	return -1;
}

#define TOKS_SARGV_ELEMENTS		1024

static int toks_split_argv(
	char **				argv,
	struct toks_split_vector *	sargv)
{
	ptrdiff_t	argc;
	char **		parg;

	/* argc */
	for (parg=argv; *parg; )
		parg++;

	if ((argc = parg - argv) >= TOKS_SARGV_ELEMENTS)
		return -1;

	/* clone argv into targv */
	ntapi->tt_aligned_block_memset(
		(uintptr_t *)sargv->targv,
		0,TOKS_SARGV_ELEMENTS*sizeof(char *));

	ntapi->tt_aligned_block_memcpy(
		(uintptr_t *)sargv->targv,
		(uintptr_t *)argv,
		argc*sizeof(char *));

	/* eargv */
	for (parg=sargv->targv; *parg; parg++) {
		if (!(strcmp(*parg,"-e")) || !(strcmp(*parg,"--exec"))) {
			sargv->eargv = &argv[parg-sargv->targv];
			sargv->eargv++;
			*parg = 0;
			return 0;
		}
	}

	return 0;
}

static int32_t toks_set_tmpfs_from_uuid(struct toks_driver_ctx_impl * ctx)
{
	int32_t						status;
	void *						htmproot;
	void *						hsvcroot;
	nt_oa						oa;
	nt_iosb						iosb;
	nt_unicode_string				path;
	uint32_t					access;
	nt_sd_common_buffer				sd;
	nt_large_integer				alloc_size;
	nt_guid_str_utf16				guidstr;
	uint32_t					access_owner;
	uint32_t					access_group;
	uint32_t					access_other;
	uint32_t					access_admin;
	wchar16_t					tmproot[3] = {'t','m','p'};
	wchar16_t					toksvc[6]  = {'t','o','k','s','v','c'};

	/* sd */
	access_owner  = NT_GENERIC_ALL|NT_SEC_SPECIFIC_RIGHTS_ALL;
	access_admin  = access_owner;

	access_group  = NT_GENERIC_READ | NT_GENERIC_WRITE | NT_GENERIC_EXECUTE;
	access_group |= NT_SEC_READ_CONTROL | NT_FILE_READ_ATTRIBUTES;

	access_other  = NT_GENERIC_READ | NT_GENERIC_EXECUTE;
	access_other |= NT_SEC_READ_CONTROL | NT_FILE_READ_ATTRIBUTES;

	ntapi->acl_init_common_descriptor(
		&sd,0,0,0,0,
		access_owner,access_group,access_other,
		access_admin,access_owner,
		0);

	/* oa */
	oa.len      = sizeof(nt_oa);
	oa.root_dir = 0;
	oa.obj_name = &path;
	oa.obj_attr = 0;
	oa.sec_desc = &sd.sd;
	oa.sec_qos  = 0;

	/* alloc_size (always zero) */
	alloc_size.quad = 0;

	/* access */
	access  = NT_SEC_SYNCHRONIZE | NT_SEC_READ_CONTROL | NT_FILE_READ_ATTRIBUTES;
	access |= NT_FILE_LIST_DIRECTORY | NT_FILE_ADD_FILE | NT_FILE_ADD_SUBDIRECTORY;
	access |= NT_FILE_TRAVERSE | NT_FILE_DELETE_CHILD;

	/* /tmp or alternative */
	if (ctx->rtdata->htmpfslroot) {
		if ((status = ntapi->zw_duplicate_object(
				ctx->rtdata->hself,
				ctx->rtdata->htmpfslroot,
				ctx->rtdata->hself,
				&htmproot,
				0,0,
				NT_DUPLICATE_SAME_ATTRIBUTES|NT_DUPLICATE_SAME_ACCESS)))
			return status;
	} else {
		path.maxlen = 0;
		path.strlen = sizeof(tmproot);
		path.buffer = tmproot;

		oa.root_dir = ctx->rtdata->hroot;

		if ((status = ntapi->zw_create_file(
				&htmproot,
				access,&oa,&iosb,
				&alloc_size,
				NT_FILE_ATTRIBUTE_DIRECTORY,
				NT_FILE_SHARE_READ | NT_FILE_SHARE_WRITE | NT_FILE_SHARE_DELETE,
				NT_FILE_OPEN_IF,
				NT_FILE_DIRECTORY_FILE | NT_FILE_SYNCHRONOUS_IO_ALERT,
				0,0)))
			return status;
	}

	/* service tmpfs root */
	path.maxlen = 0;
	path.strlen = sizeof(toksvc);
	path.buffer = toksvc;

	oa.root_dir = htmproot;

	status = ntapi->zw_create_file(
		&hsvcroot,
		access,&oa,&iosb,
		&alloc_size,
		NT_FILE_ATTRIBUTE_DIRECTORY,
		NT_FILE_SHARE_READ | NT_FILE_SHARE_WRITE | NT_FILE_SHARE_DELETE,
		NT_FILE_OPEN_IF,
		NT_FILE_DIRECTORY_FILE | NT_FILE_SYNCHRONOUS_IO_ALERT,
		0,0);

	if (status) {
		ntapi->zw_close(htmproot);
		return status;
	}

	/* instance-specific tmpfs root */
	ntapi->tt_guid_to_string_utf16(
		&ctx->uuid,
		&guidstr);

	path.maxlen = 0;
	path.strlen = sizeof(guidstr);
	path.buffer = &guidstr.lbrace;

	oa.root_dir = hsvcroot;

	status = ntapi->zw_create_file(
		&ctx->htmpfs,
		access,&oa,&iosb,
		&alloc_size,
		NT_FILE_ATTRIBUTE_DIRECTORY,
		NT_FILE_SHARE_READ | NT_FILE_SHARE_WRITE | NT_FILE_SHARE_DELETE,
		NT_FILE_OPEN_IF,
		NT_FILE_DIRECTORY_FILE | NT_FILE_SYNCHRONOUS_IO_ALERT,
		0,0);

	ntapi->zw_close(hsvcroot);
	ntapi->zw_close(htmproot);

	return status;
}

static int32_t toks_set_uuid_from_root(
	struct toks_driver_ctx_impl *   ctx,
	struct _nt_guid *               svcguid)
{
	int32_t                         status;
	nt_iosb                         iosb;
	nt_file_object_id_buffer        objid;

	if (!ctx->rtdata->hroot)
		return NT_STATUS_INVALID_PARAMETER;

	iosb.status = NT_STATUS_PENDING;

	status = ntapi->zw_fs_control_file(
		ctx->rtdata->hroot,
		ctx->hevent,0,0,
		&iosb,
		NT_FSCTL_CREATE_OR_GET_OBJECT_ID,
		0,0,&objid,sizeof(objid));

	if (status == NT_STATUS_PENDING)
		ntapi->zw_wait_for_single_object(
			ctx->hevent,
			NT_SYNC_NON_ALERTABLE,
			0);

	if (iosb.status)
		return iosb.status;

	svcguid->data1    = (objid.obj_id[0]);
	svcguid->data1   |= (objid.obj_id[1] << 8);
	svcguid->data1   |= (objid.obj_id[2] << 16);
	svcguid->data1   |= (objid.obj_id[3] << 24);

	svcguid->data2    = (objid.obj_id[4]);
	svcguid->data2   |= (objid.obj_id[5] << 8);

	svcguid->data3    = (objid.obj_id[6]);
	svcguid->data3   |= (objid.obj_id[7] << 8);

	svcguid->data4[0] = (objid.obj_id[8]);
	svcguid->data4[1] = (objid.obj_id[9]);
	svcguid->data4[2] = (objid.obj_id[10]);
	svcguid->data4[3] = (objid.obj_id[11]);
	svcguid->data4[4] = (objid.obj_id[12]);
	svcguid->data4[5] = (objid.obj_id[13]);
	svcguid->data4[6] = (objid.obj_id[14]);
	svcguid->data4[7] = (objid.obj_id[15]);

	return NT_STATUS_SUCCESS;
}

int toks_get_driver_ctx(
	char **				argv,
	char **				envp,
	uint32_t			flags,
	struct toks_driver_ctx **	pctx)
{
	int32_t				status;
	struct toks_driver_ctx_impl *	ctx;
	struct toks_common_ctx		cctx;
	struct toks_split_vector	sargv;
	const struct argv_option *	optv[TOKS_OPTV_ELEMENTS];
	struct argv_meta *		meta;
	struct argv_entry *		entry;
	struct argv_entry *		uuid;
	struct argv_entry *		pid;
	struct argv_entry *		syspid;
	struct argv_entry *		cfpid;
	struct argv_entry *		cspid;
	struct argv_entry *		msecs;
	struct toks_token_string	key;
	size_t				keylen;
	uintptr_t			opdata;
	nt_guid				svcguid;
	const char *			program;
	const char *			refstr;
	int				ntokens;
	int				atokens;
	int				loglevel;
	int32_t				tokpid;
	int32_t				tsyspid;
	int32_t				ctrlpid;
	int32_t				csyspid;
	int64_t				timeout;
	void *				hlog;
	void *				hkernel32;
	char *				targv[TOKS_SARGV_ELEMENTS];

	(void)envp;

	if (toks_init())
		return -1;

	argv_optv_init(toks_default_options,optv);

	sargv.targv = targv;
	sargv.eargv = 0;

	if (toks_split_argv(argv,&sargv))
		return -1;

	if (!(meta = argv_get(
			sargv.targv,optv,
			toks_argv_flags(flags),
			STDERR_FILENO)))
		return -1;

	if (!(flags & TOKS_DRIVER_MODE_CLIENT))
		flags |= TOKS_DRIVER_MODE_SERVER;

	hlog      = 0;
	uuid      = 0;
	tokpid    = 0;
	tsyspid   = 0;
	ctrlpid   = 0;
	csyspid   = 0;
	keylen    = 0;
	refstr    = 0;
	ntokens   = 0;
	loglevel  = 0;
	timeout   = (-1);
	program   = argv_program_name(argv[0]);

	memset(&cctx,0,sizeof(cctx));
	cctx.drvflags = flags;
	cctx.eargv = sargv.eargv;

	if (!argv[1] && (flags & TOKS_DRIVER_VERBOSITY_USAGE))
		return toks_driver_usage(program,0,optv,meta);

	for (entry=meta->entries; entry->fopt || entry->arg; entry++) {
		if (entry->fopt) {
			switch (entry->tag) {
				case TAG_HELP:
					if (flags & TOKS_DRIVER_VERBOSITY_USAGE)
						return toks_driver_usage(program,entry->arg,optv,meta);

				case TAG_VERSION:
					cctx.drvflags |= TOKS_DRIVER_VERSION;
					break;

				case TAG_DAEMON:
					if (!strcmp("always",entry->arg))
						cctx.drvflags |= TOKS_DRIVER_DAEMON_ALWAYS;

					else if (!strcmp("never",entry->arg))
						cctx.drvflags |= TOKS_DRIVER_DAEMON_NEVER;

					break;

				case TAG_SYSROOT:
					cctx.sysroot = entry->arg;
					break;

				case TAG_UUID:
					uuid = entry;
					break;

				case TAG_ROOTUUID:
					cctx.drvflags |= TOKS_DRIVER_ROOT_UUID;
					break;

				case TAG_REFSTR:
					refstr = entry->arg;
					break;

				case TAG_PID:
					tokpid = toks_arg_to_int32((pid=entry));
					break;

				case TAG_SYSPID:
					tsyspid = toks_arg_to_int32((syspid=entry));
					break;

				case TAG_CTRLPID:
					ctrlpid = toks_arg_to_int32((cfpid=entry));
					break;

				case TAG_CSYSPID:
					csyspid = toks_arg_to_int32((cspid=entry));
					break;

				case TAG_LOGLEVEL:
					loglevel = toks_arg_to_int32(entry);
					loglevel = (loglevel > 9) ? (-1) : loglevel;
					cctx.loglevel = loglevel;
					break;

				case TAG_TIMEOUT:
					timeout = toks_arg_to_int64((msecs=entry));
					timeout = (timeout < 0) ? (-2) : timeout;
					break;

				case TAG_TOKENS:
					ntokens = toks_arg_to_int32(entry);
					ntokens = (ntokens > 9999) ? (-1) : ntokens;
					break;

				case TAG_CONNECT:
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
					break;

				case TAG_ABORT:
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
					cctx.drvflags |= TOKS_DRIVER_ACTION_ABORT;
					break;

				case TAG_ACQUIRE:
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
					cctx.drvflags |= TOKS_DRIVER_ACTION_ACQUIRE;
					break;

				case TAG_RELEASE:
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
					cctx.drvflags |= TOKS_DRIVER_ACTION_RELEASE;

					keylen = toks_strlen(entry->arg);

					ntapi->tt_generic_memset(
						&key,0,sizeof(key));

					if (keylen < sizeof(key.token))
						ntapi->tt_generic_memcpy(
							&key.token,entry->arg,
							keylen);
					break;

				case TAG_LOGFILE:
					cctx.logfile = entry->arg;
					break;

				case TAG_NTOKENSGET:
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
					cctx.drvflags |= TOKS_DRIVER_ACTION_NTOKENS_GET;
					break;

				case TAG_NTOKENSSET:
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
					cctx.drvflags |= TOKS_DRIVER_ACTION_NTOKENS_SET;
					ntokens = toks_arg_to_int32(entry);
					break;
			}
		} else
			/* strict */
			return toks_driver_usage(program,0,optv,meta);
	}

	if ((cctx.drvflags & TOKS_DRIVER_MODE_CLIENT) && (tokpid < 0)) {
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
			toks_dprintf(STDERR_FILENO,
				"%s: error: %s is not a valid framework process id.",
				program,pid->arg);
		return toks_get_driver_ctx_fail(meta);
	}

	if ((cctx.drvflags & TOKS_DRIVER_MODE_CLIENT) && (tsyspid < 0)) {
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
			toks_dprintf(STDERR_FILENO,
				"%s: error: %s is not a valid system process id.",
				program,syspid->arg);
		return toks_get_driver_ctx_fail(meta);
	}

	if ((cctx.drvflags & TOKS_DRIVER_MODE_SERVER) && (ctrlpid < 0)) {
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
			toks_dprintf(STDERR_FILENO,
				"%s: error: %s is not a valid controlling "
				"framework process id.",
				program,cfpid->arg);
		return toks_get_driver_ctx_fail(meta);
	}

	if ((cctx.drvflags & TOKS_DRIVER_MODE_SERVER) && (csyspid < 0)) {
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
			toks_dprintf(STDERR_FILENO,
				"%s: error: %s is not a valid controlling "
				"system process id.",
				program,cspid->arg);
		return toks_get_driver_ctx_fail(meta);
	}

	if ((cctx.drvflags & TOKS_DRIVER_MODE_CLIENT) && (timeout < (-1))) {
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
			toks_dprintf(STDERR_FILENO,
				"%s: error: %s is not a valid timeout in milliseconds.",
				program,msecs->arg);
		return toks_get_driver_ctx_fail(meta);
	}

	if ((ntokens == 0) && !(cctx.drvflags & TOKS_DRIVER_VERSION)) {
		ntokens = (-1);
	}

	if (ntokens == 0) {
		cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
	}

	if ((cctx.drvflags & TOKS_DRIVER_MODE_SERVER) && (ntokens < 0)) {
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
			toks_dprintf(STDERR_FILENO,
				"%s: error: number of tokens not set or is invalid.",
				program);
		return toks_get_driver_ctx_fail(meta);
	}

	if ((cctx.drvflags & TOKS_DRIVER_MODE_SERVER) && (loglevel < 0)) {
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
			toks_dprintf(STDERR_FILENO,
				"%s: error: loglevel must be in the range of 0..9.",
				program);
		return toks_get_driver_ctx_fail(meta);
	}

	if (uuid && ntapi->tt_string_to_guid_utf8(uuid->arg,&svcguid)) {
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
			toks_dprintf(STDERR_FILENO,
				"%s: error: '%s' is not a valid service guid (did you forget the braces?)",
				program,uuid->arg);
		return toks_get_driver_ctx_fail(meta);

	}

	if (cctx.sysroot && toks_open_dir(&cctx.hroot,0,cctx.sysroot,false)) {
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
			toks_dprintf(STDERR_FILENO,
				"%s: error: could not open sysroot directory '%s'",
				program,cctx.sysroot);
		return toks_get_driver_ctx_fail(meta);
	}

	if (!(ctx = toks_driver_ctx_alloc(meta,&cctx)))
		return toks_get_driver_ctx_fail(meta);

	if (ctx->rtdata->hroot && cctx.sysroot) {
		ntapi->zw_close(ctx->rtdata->hroot);
		ctx->rtdata->hroot = cctx.hroot;
	} else {
		cctx.hroot = ctx->rtdata->hroot;
	}

	if (cctx.logfile && toks_open_log_file(&hlog,ctx->rtdata->hroot,cctx.logfile,false)) {
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
			toks_dprintf(STDERR_FILENO,
				"%s: error: could not create logfile '%s'",
				program,cctx.logfile);
		return toks_get_driver_ctx_fail(meta);
	}

	if (ctx->rtdata->hlog && cctx.logfile) {
		ntapi->zw_close(ctx->rtdata->hlog);
		ctx->rtdata->hlog = hlog;
	}

	if ((cctx.drvflags & TOKS_DRIVER_ACTION_RELEASE)) {
		if (toks_client_str_to_token(&ctx->ctx,&key)) {
			if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
				toks_dprintf(STDERR_FILENO,
					"%s: error: [%s] is not a valid token.",
					program,key.token);
			return toks_get_driver_ctx_fail(meta);
		}
	}


	if ((toks_ntapi->tt_create_private_event(
			&ctx->hevent,
			NT_NOTIFICATION_EVENT,
			NT_EVENT_NOT_SIGNALED)))
		return toks_get_driver_ctx_fail(meta);


	if (uuid) {
		ntapi->tt_guid_copy(
			&ctx->uuid,
			&svcguid);

	} else if (cctx.drvflags & TOKS_DRIVER_ROOT_UUID) {
		if ((status = toks_set_uuid_from_root(ctx,&svcguid))) {
			if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
				toks_dprintf(STDERR_FILENO,
					"%s: error: could not set the uuid "
					"based on the sysroot directory [0x%08x].",
					program,status);
			return toks_get_driver_ctx_fail(meta);
		}

		ntapi->tt_guid_copy(
			&ctx->uuid,
			&svcguid);
	} else {
		ntapi->tt_guid_copy(
			&ctx->uuid,
			&ctx->rtdata->srv_guid);
	}


	if ((ntapi->tt_open_dev_object_directory(
			&ctx->hsvcdir,
			NT_SEC_READ_CONTROL
				| NT_DIRECTORY_QUERY
				| NT_DIRECTORY_TRAVERSE
				| NT_DIRECTORY_CREATE_OBJECT
				| NT_DIRECTORY_CREATE_SUBDIRECTORY,
			toks_service_name,
			&toks_daemon_default_guid)))
		return toks_get_driver_ctx_fail(meta);

	if ((hkernel32 = pe_get_kernel32_module_handle()))
		if ((ctx->ticks.qpc = pe_get_procedure_address(
				hkernel32,"QueryPerformanceCounter")))
			ntapi->zw_query_performance_counter(
				&(nt_filetime){{0,0}},
				&ctx->ticks.pcfreq);


	atokens  = ntokens;
	atokens += 0x1fff;
	atokens |= 0xfff;
	atokens ^= 0xfff;

	ctx->hlog               = hlog;
	ctx->tokpid             = tokpid;
	ctx->tsyspid            = tsyspid;
	ctx->ctrlpid            = ctrlpid;
	ctx->csyspid            = csyspid;
	ctx->ntokens            = ntokens;
	ctx->atokens            = atokens;
	ctx->timeout            = timeout;
	ctx->ctx.program	= program;
	ctx->ctx.cctx		= &ctx->cctx;
	ctx->cctx.uuid          = &ctx->uuid;

	ctx->rtdata->hroot      = cctx.hroot;
	ctx->rtdata->hlog       = hlog ? hlog : ctx->rtdata->hlog;

	toks_set_driver_refstr(&ctx->ctx,refstr);

	if (cctx.drvflags & TOKS_DRIVER_MODE_SERVER) {
		if ((status = toks_set_tmpfs_from_uuid(ctx))) {
			if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
				toks_dprintf(STDERR_FILENO,
					"%s: error: could not create or open "
					"the instance-specific tmpfs directory [0x%08x].",
					program,status);
			return toks_get_driver_ctx_fail(meta);
		}

		if (!(ctx->waiters = toks_calloc(
				TOKS_MAX_WAITERS,
				sizeof(struct toks_waiter))))
			return toks_get_driver_ctx_fail(meta);

		toks_daemon_ctx.driver_ctx   = &ctx->ctx;
		toks_daemon_ctx.waiter_base  = ctx->waiters;
		toks_daemon_ctx.waiter_first = ctx->waiters;
		toks_daemon_ctx.waiter_next  = ctx->waiters;
		toks_daemon_ctx.waiter_cap   = &ctx->waiters[TOKS_MAX_WAITERS];


		if (!(ctx->tokens = toks_calloc(atokens,sizeof(*ctx->tokens))))
			return toks_get_driver_ctx_fail(meta);

		if (toks_daemon_init(&toks_daemon_ctx,&ctx->uuid))
			return toks_get_driver_ctx_fail(meta);
	}

	if (cctx.drvflags & TOKS_DRIVER_MODE_CLIENT) {
		if (cctx.drvflags & TOKS_DRIVER_DAEMON_ALWAYS) {
			toks_daemon_ctx.driver_ctx = &ctx->ctx;

			switch ((status = toks_daemon_init(&toks_daemon_ctx,0))) {
				case NT_STATUS_SUCCESS:
					break;

				default:
					if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
						toks_dprintf(STDERR_FILENO,
							"%s: error: failed to initialize signal handling "
							"(check the system's documentation) [0x%x].",
							program,status);

					return toks_get_driver_ctx_fail(meta);
			}
		}

		switch ((status = toks_client_connect(&ctx->ctx))) {
			case NT_STATUS_SUCCESS:
				break;

			case NT_STATUS_OBJECT_NAME_NOT_FOUND:
				if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
					toks_dprintf(STDERR_FILENO,
						"%s: error: could not connect "
						"(server not running) [0x%x].",
						program,status);

				return toks_get_driver_ctx_fail(meta);

			case NT_STATUS_CONNECTION_REFUSED:
			case NT_STATUS_PORT_CONNECTION_REFUSED:
				if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
					toks_dprintf(STDERR_FILENO,
						"%s: error: could not connect "
						"(connection refused) [0x%x].",
						program,status);

				return toks_get_driver_ctx_fail(meta);

			case NT_STATUS_ACCESS_DENIED:
				if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
					toks_dprintf(STDERR_FILENO,
						"%s: error: could not connect "
						"(access denied) [0x%x].",
						program,status);

				return toks_get_driver_ctx_fail(meta);

			default:
				if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
					toks_dprintf(STDERR_FILENO,
						"%s: error: could not connect "
						"(check the system's documentation) [0x%x].",
						program,status);

				return toks_get_driver_ctx_fail(meta);
		}

		if (cctx.drvflags & TOKS_DRIVER_ACTION_NTOKENS_GET) {
			status = toks_service_ioctl(
				&ctx->ctx,TOKS_IOCTL_GET_TOKEN_COUNT,
				&opdata,0);

			switch (status) {
				case NT_STATUS_SUCCESS:
					ctx->ntokens = opdata;
					break;

				default:
					if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
						toks_dprintf(STDERR_FILENO,
							"%s: error: could not obtain the current "
							"overall number of tokens "
							"(check the system's documentation) [0x%x].",
							program,status);

					return toks_get_driver_ctx_fail(meta);
			}
		}
	}

	*pctx = &ctx->ctx;
	return TOKS_OK;
}

static void toks_free_driver_ctx_impl(struct toks_driver_ctx_alloc * ictx)
{
	if (ictx->ctx.htmpfs)
		ntapi->zw_close(ictx->ctx.htmpfs);

	if (ictx->ctx.hevent)
		ntapi->zw_close(ictx->ctx.hevent);

	if (ictx->ctx.hsvcdir)
		ntapi->zw_close(ictx->ctx.hsvcdir);

	if (ictx->ctx.hsvclink)
		ntapi->zw_close(ictx->ctx.hsvclink);

	if (ictx->ctx.hservice)
		ntapi->zw_close(ictx->ctx.hservice);

	if (ictx->ctx.hserver)
		ntapi->zw_close(ictx->ctx.hserver);

	if (ictx->ctx.hlog)
		ntapi->zw_close(ictx->ctx.hlog);

	if (ictx->ctx.tokens)
		toks_free(ictx->ctx.tokens);

	if (ictx->ctx.waiters)
		toks_free(ictx->ctx.waiters);

	argv_free(ictx->meta);
	free(ictx);
}

void toks_free_driver_ctx(struct toks_driver_ctx * ctx)
{
	struct toks_driver_ctx_alloc *	ictx;
	uintptr_t			addr;

	if (ctx) {
		addr = (uintptr_t)ctx - offsetof(struct toks_driver_ctx_impl,ctx);
		addr = addr - offsetof(struct toks_driver_ctx_alloc,ctx);
		ictx = (struct toks_driver_ctx_alloc *)addr;
		toks_free_driver_ctx_impl(ictx);
	}
}

void toks_driver_set_timeout(struct toks_driver_ctx * dctx, int64_t millisecs)
{
	toks_set_driver_timeout(dctx,millisecs);
}

void toks_driver_unset_timeout(struct toks_driver_ctx * dctx)
{
	toks_set_driver_timeout(dctx,(-1));
}

const struct toks_source_version * toks_source_version(void)
{
	return &toks_src_version;
}