Blame src/driver/toks_driver_ctx.c

2420c2
/*********************************************************/
2420c2
/*  toksvc: a framework-native token broker service      */
2420c2
/*  Copyright (C) 2020  Z. Gilboa                        */
2420c2
/*  Released under GPLv2 and GPLv3; see COPYING.TOKSVC.  */
2420c2
/*********************************************************/
2420c2
2420c2
#include <ntapi/ntapi.h>
2420c2
#include <ntapi/nt_atomic.h>
236509
#include <ntapi/nt_fsctl.h>
2420c2
2420c2
#include <stdint.h>
2420c2
2420c2
#include <toksvc/toksvc.h>
2420c2
#include "toksvc_init_impl.h"
2420c2
#include "toksvc_nolibc_impl.h"
2420c2
2420c2
#define ARGV_DRIVER
2420c2
2420c2
#include "toksvc_version.h"
2420c2
#include "toksvc_daemon_impl.h"
2420c2
#include "toksvc_dprintf_impl.h"
2420c2
#include "toksvc_driver_impl.h"
2420c2
#include "argv/argv.h"
2420c2
3fc30f
#undef   strlen
3fc30f
2420c2
/* pty integration */
2420c2
#include <psxtypes/section/freestd.h>
2420c2
2420c2
__attr_section_decl__(".freestd")
2420c2
static const nt_tty_affiliation tty_affiliation
2420c2
	__attr_section__(".freestd")
2420c2
	= NT_TTY_AFFILIATION_DEFAULT;
2420c2
2420c2
/* ntapi accessor table */
2420c2
const ntapi_vtbl * toks_ntapi;
2420c2
2420c2
/* daemon */
2420c2
static struct toks_daemon_ctx toks_daemon_ctx;
618937
static const  nt_guid toks_daemon_default_guid = TOKS_PORT_GUID_DAEMON;
00069c
static const  wchar16_t toks_service_name[6]   = TOKS_PORT_NAME_PREFIX;
2420c2
2420c2
/* package info */
2420c2
static const struct toks_source_version toks_src_version = {
2420c2
	TOKS_TAG_VER_MAJOR,
2420c2
	TOKS_TAG_VER_MINOR,
2420c2
	TOKS_TAG_VER_PATCH,
2420c2
	TOKSVC_GIT_VERSION
2420c2
};
2420c2
2420c2
struct toks_driver_ctx_alloc {
2420c2
	struct argv_meta *		meta;
2420c2
	struct toks_driver_ctx_impl	ctx;
2420c2
	uint64_t			guard;
2420c2
};
2420c2
2420c2
struct toks_split_vector {
2420c2
	char **		targv;
2420c2
	char **		eargv;
2420c2
};
2420c2
2420c2
static uint32_t toks_argv_flags(uint32_t flags)
2420c2
{
2420c2
	uint32_t ret = ARGV_CLONE_VECTOR;
2420c2
2420c2
	if (flags & TOKS_DRIVER_VERBOSITY_NONE)
2420c2
		ret |= ARGV_VERBOSITY_NONE;
2420c2
2420c2
	if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
2420c2
		ret |= ARGV_VERBOSITY_ERRORS;
2420c2
2420c2
	if (flags & TOKS_DRIVER_VERBOSITY_STATUS)
2420c2
		ret |= ARGV_VERBOSITY_STATUS;
2420c2
2420c2
	return ret;
2420c2
}
2420c2
2420c2
static int toks_driver_usage(
2420c2
	const char *			program,
2420c2
	const char *			arg,
2420c2
	const struct argv_option **	optv,
2420c2
	struct argv_meta *		meta)
2420c2
{
2420c2
	char header[512];
2420c2
2420c2
	snprintf(header,sizeof(header),
2420c2
		"Usage: %s [options] <file>...\n" "Options:\n",
2420c2
		program);
2420c2
2420c2
	argv_usage(STDOUT_FILENO,header,optv,arg);
2420c2
	argv_free(meta);
2420c2
2420c2
	return TOKS_USAGE;
2420c2
}
2420c2
ff65f1
static int64_t toks_arg_to_int64(struct argv_entry * entry)
ff65f1
{
ff65f1
	int64_t      ret;
ff65f1
	const char * ch;
ff65f1
ff65f1
	for (ret=0, ch=entry->arg; *ch && (ret>=0); ch++) {
ff65f1
		if ((*ch < '0') || (*ch >'9'))
ff65f1
			return (-1);
ff65f1
ff65f1
		ret *= 10;
ff65f1
		ret += (*ch - '0');
ff65f1
	}
ff65f1
ff65f1
	return ret;
ff65f1
}
ff65f1
ff65f1
static int32_t toks_arg_to_int32(struct argv_entry * entry)
ff65f1
{
ff65f1
	int64_t ret = toks_arg_to_int64(entry);
ff65f1
	return (ret >= 0) && (ret <= 0x7fffffff)
ff65f1
		? ret : (-1);
ff65f1
}
ff65f1
2f270e
static int32_t toks_query_performance_counters_failover(nt_filetime * ticks)
2f270e
{
2f270e
	(void)ticks;
2f270e
	return 0;
2f270e
}
2f270e
23494d
static int32_t toks_daemon_library_once;
23494d
23494d
static int32_t toks_daemon_library_init(void)
23494d
{
23494d
	int32_t		status;
23494d
	int		argc;
23494d
	char **		argv;
23494d
	char **		envp;
23494d
	nt_rtdata *	rtdata;
23494d
	nt_timeout	timeout;
23494d
23494d
	switch (at_locked_cas_32(&toks_daemon_library_once,0,1)) {
23494d
		case 0:
23494d
			status = ntapi->tt_get_argv_envp_utf8(
23494d
				&argc,&argv,&envp,
23494d
				0,0,0);
23494d
23494d
			if (status) {
23494d
				at_store_32(&toks_daemon_library_once,3);
23494d
				return status;
23494d
			}
23494d
23494d
			if ((status = ntapi->tt_get_runtime_data(&rtdata,0))) {
23494d
				at_store_32(&toks_daemon_library_once,3);
23494d
				return status;
23494d
			}
23494d
15a6ec
			if (!rtdata->argv) {
15a6ec
				rtdata->argc = argc;
15a6ec
				rtdata->argv = argv;
15a6ec
				rtdata->envp = envp;
15a6ec
			}
15a6ec
23494d
			ntapi->tt_guid_copy(
23494d
				&rtdata->srv_guid,
23494d
				&toks_daemon_default_guid);
23494d
23494d
			at_locked_inc_32(&toks_daemon_library_once);
23494d
			return 0;
23494d
23494d
		case 1:
23494d
			timeout.quad = -10;
23494d
23494d
			for (; (at_locked_cas_32(&toks_daemon_library_once,0,1) == 1); )
23494d
				ntapi->zw_delay_execution(
23494d
					NT_SYNC_ALERTABLE,
23494d
					&timeout);
23494d
23494d
			return (toks_daemon_library_once == 2)
23494d
				? 0 : -1;
23494d
23494d
		case 2:
23494d
			return 0;
23494d
23494d
		case 3:
23494d
		default:
23494d
			return -1;
23494d
	}
23494d
23494d
	return NT_STATUS_INTERNAL_ERROR;
23494d
}
23494d
2420c2
static struct toks_driver_ctx_impl * toks_driver_ctx_alloc(
2420c2
	struct argv_meta *		meta,
2420c2
	const struct toks_common_ctx *	cctx)
2420c2
{
2420c2
	struct toks_driver_ctx_alloc *	ictx;
2420c2
	size_t				size;
2420c2
	nt_runtime_data *		rtdata;
2420c2
23494d
	switch (ntapi->tt_get_runtime_data(&rtdata,0)) {
23494d
		case NT_STATUS_SUCCESS:
23494d
			break;
23494d
23494d
		case NT_STATUS_MORE_PROCESSING_REQUIRED:
23494d
			toks_daemon_library_init();
23494d
23494d
			if (ntapi->tt_get_runtime_data(&rtdata,0))
23494d
				return 0;
23494d
75adfe
			break;
75adfe
23494d
		default:
23494d
			return 0;
23494d
	}
2420c2
2420c2
	size =  sizeof(struct toks_driver_ctx_alloc);
2420c2
2420c2
	if (!(ictx = calloc(1,size)))
2420c2
		return 0;
2420c2
2420c2
	if (cctx)
2420c2
		memcpy(&ictx->ctx.cctx,cctx,sizeof(*cctx));
2420c2
2f270e
	ictx->ctx.ticks.qpc         = toks_query_performance_counters_failover;
2f270e
	ictx->ctx.ticks.pcfreq.quad = 0;
2f270e
2420c2
	ictx->meta = meta;
2420c2
	ictx->ctx.rtdata = rtdata;
2420c2
	return &ictx->ctx;
2420c2
}
2420c2
2420c2
static int toks_get_driver_ctx_fail(struct argv_meta * meta)
2420c2
{
2420c2
	argv_free(meta);
2420c2
	return -1;
2420c2
}
2420c2
2420c2
#define TOKS_SARGV_ELEMENTS		1024
2420c2
2420c2
static int toks_split_argv(
2420c2
	char **				argv,
2420c2
	struct toks_split_vector *	sargv)
2420c2
{
2420c2
	ptrdiff_t	argc;
2420c2
	char **		parg;
2420c2
2420c2
	/* argc */
2420c2
	for (parg=argv; *parg; )
2420c2
		parg++;
2420c2
2420c2
	if ((argc = parg - argv) >= TOKS_SARGV_ELEMENTS)
2420c2
		return -1;
2420c2
2420c2
	/* clone argv into targv */
2420c2
	ntapi->tt_aligned_block_memset(
2420c2
		(uintptr_t *)sargv->targv,
2420c2
		0,TOKS_SARGV_ELEMENTS*sizeof(char *));
2420c2
2420c2
	ntapi->tt_aligned_block_memcpy(
2420c2
		(uintptr_t *)sargv->targv,
2420c2
		(uintptr_t *)argv,
2420c2
		argc*sizeof(char *));
2420c2
2420c2
	/* eargv */
2420c2
	for (parg=sargv->targv; *parg; parg++) {
2420c2
		if (!(strcmp(*parg,"-e")) || !(strcmp(*parg,"--exec"))) {
2420c2
			sargv->eargv = &argv[parg-sargv->targv];
2420c2
			sargv->eargv++;
2420c2
			*parg = 0;
2420c2
			return 0;
2420c2
		}
2420c2
	}
2420c2
2420c2
	return 0;
2420c2
}
2420c2
3fc30f
static int32_t toks_set_tmpfs_from_uuid(struct toks_driver_ctx_impl * ctx)
3fc30f
{
3fc30f
	int32_t						status;
3fc30f
	void *						htmproot;
3fc30f
	void *						hsvcroot;
3fc30f
	nt_oa						oa;
3fc30f
	nt_iosb						iosb;
3fc30f
	nt_unicode_string				path;
3fc30f
	uint32_t					access;
3fc30f
	nt_sd_common_buffer				sd;
3fc30f
	nt_large_integer				alloc_size;
3fc30f
	nt_guid_str_utf16				guidstr;
3fc30f
	uint32_t					access_owner;
3fc30f
	uint32_t					access_group;
3fc30f
	uint32_t					access_other;
3fc30f
	uint32_t					access_admin;
3fc30f
	wchar16_t					tmproot[3] = {'t','m','p'};
3fc30f
	wchar16_t					toksvc[6]  = {'t','o','k','s','v','c'};
3fc30f
3fc30f
	/* sd */
3fc30f
	access_owner  = NT_GENERIC_ALL|NT_SEC_SPECIFIC_RIGHTS_ALL;
3fc30f
	access_admin  = access_owner;
3fc30f
3fc30f
	access_group  = NT_GENERIC_READ | NT_GENERIC_WRITE | NT_GENERIC_EXECUTE;
3fc30f
	access_group |= NT_SEC_READ_CONTROL | NT_FILE_READ_ATTRIBUTES;
3fc30f
3fc30f
	access_other  = NT_GENERIC_READ | NT_GENERIC_EXECUTE;
3fc30f
	access_other |= NT_SEC_READ_CONTROL | NT_FILE_READ_ATTRIBUTES;
3fc30f
3fc30f
	ntapi->acl_init_common_descriptor(
3fc30f
		&sd,0,0,0,0,
3fc30f
		access_owner,access_group,access_other,
3fc30f
		access_admin,access_owner,
3fc30f
		0);
3fc30f
3fc30f
	/* oa */
3fc30f
	oa.len      = sizeof(nt_oa);
3fc30f
	oa.root_dir = 0;
3fc30f
	oa.obj_name = &pat;;
3fc30f
	oa.obj_attr = 0;
3fc30f
	oa.sec_desc = &sd.sd;
3fc30f
	oa.sec_qos  = 0;
3fc30f
3fc30f
	/* alloc_size (always zero) */
3fc30f
	alloc_size.quad = 0;
3fc30f
3fc30f
	/* access */
3fc30f
	access  = NT_SEC_SYNCHRONIZE | NT_SEC_READ_CONTROL | NT_FILE_READ_ATTRIBUTES;
3fc30f
	access |= NT_FILE_LIST_DIRECTORY | NT_FILE_ADD_FILE | NT_FILE_ADD_SUBDIRECTORY;
3fc30f
	access |= NT_FILE_TRAVERSE | NT_FILE_DELETE_CHILD;
3fc30f
3fc30f
	/* /tmp or alternative */
3fc30f
	if (ctx->rtdata->htmpfslroot) {
3fc30f
		if ((status = ntapi->zw_duplicate_object(
3fc30f
				ctx->rtdata->hself,
3fc30f
				ctx->rtdata->htmpfslroot,
3fc30f
				ctx->rtdata->hself,
3fc30f
				&htmproot,
3fc30f
				0,0,
3fc30f
				NT_DUPLICATE_SAME_ATTRIBUTES|NT_DUPLICATE_SAME_ACCESS)))
3fc30f
			return status;
3fc30f
	} else {
3fc30f
		path.maxlen = 0;
3fc30f
		path.strlen = sizeof(tmproot);
3fc30f
		path.buffer = tmproot;
3fc30f
3fc30f
		oa.root_dir = ctx->rtdata->hroot;
3fc30f
3fc30f
		if ((status = ntapi->zw_create_file(
3fc30f
				&htmproot,
3fc30f
				access,&oa,&iosb,
3fc30f
				&alloc_size,
3fc30f
				NT_FILE_ATTRIBUTE_DIRECTORY,
3fc30f
				NT_FILE_SHARE_READ | NT_FILE_SHARE_WRITE | NT_FILE_SHARE_DELETE,
3fc30f
				NT_FILE_OPEN_IF,
3fc30f
				NT_FILE_DIRECTORY_FILE | NT_FILE_SYNCHRONOUS_IO_ALERT,
3fc30f
				0,0)))
3fc30f
			return status;
3fc30f
	}
3fc30f
3fc30f
	/* service tmpfs root */
3fc30f
	path.maxlen = 0;
3fc30f
	path.strlen = sizeof(toksvc);
3fc30f
	path.buffer = toksvc;
3fc30f
3fc30f
	oa.root_dir = htmproot;
3fc30f
3fc30f
	status = ntapi->zw_create_file(
3fc30f
		&hsvcroot,
3fc30f
		access,&oa,&iosb,
3fc30f
		&alloc_size,
3fc30f
		NT_FILE_ATTRIBUTE_DIRECTORY,
3fc30f
		NT_FILE_SHARE_READ | NT_FILE_SHARE_WRITE | NT_FILE_SHARE_DELETE,
3fc30f
		NT_FILE_OPEN_IF,
3fc30f
		NT_FILE_DIRECTORY_FILE | NT_FILE_SYNCHRONOUS_IO_ALERT,
3fc30f
		0,0);
3fc30f
3fc30f
	if (status) {
3fc30f
		ntapi->zw_close(htmproot);
3fc30f
		return status;
3fc30f
	}
3fc30f
3fc30f
	/* instance-specific tmpfs root */
3fc30f
	ntapi->tt_guid_to_string_utf16(
0021e3
		&ctx->guid,
3fc30f
		&guidstr);
3fc30f
3fc30f
	path.maxlen = 0;
3fc30f
	path.strlen = sizeof(guidstr);
3fc30f
	path.buffer = &guidstr.lbrace;
3fc30f
3fc30f
	oa.root_dir = hsvcroot;
3fc30f
3fc30f
	status = ntapi->zw_create_file(
3fc30f
		&ctx->htmpfs,
3fc30f
		access,&oa,&iosb,
3fc30f
		&alloc_size,
3fc30f
		NT_FILE_ATTRIBUTE_DIRECTORY,
3fc30f
		NT_FILE_SHARE_READ | NT_FILE_SHARE_WRITE | NT_FILE_SHARE_DELETE,
3fc30f
		NT_FILE_OPEN_IF,
3fc30f
		NT_FILE_DIRECTORY_FILE | NT_FILE_SYNCHRONOUS_IO_ALERT,
3fc30f
		0,0);
3fc30f
3fc30f
	ntapi->zw_close(hsvcroot);
3fc30f
	ntapi->zw_close(htmproot);
3fc30f
3fc30f
	return status;
3fc30f
}
3fc30f
236509
static int32_t toks_set_uuid_from_root(
236509
	struct toks_driver_ctx_impl *   ctx,
236509
	struct _nt_guid *               svcguid)
236509
{
236509
	int32_t                         status;
236509
	nt_iosb                         iosb;
236509
	nt_file_object_id_buffer        objid;
236509
236509
	if (!ctx->rtdata->hroot)
236509
		return NT_STATUS_INVALID_PARAMETER;
236509
236509
	iosb.status = NT_STATUS_PENDING;
236509
236509
	status = ntapi->zw_fs_control_file(
236509
		ctx->rtdata->hroot,
236509
		ctx->hevent,0,0,
236509
		&iosb,
236509
		NT_FSCTL_CREATE_OR_GET_OBJECT_ID,
236509
		0,0,&objid,sizeof(objid));
236509
236509
	if (status == NT_STATUS_PENDING)
236509
		ntapi->zw_wait_for_single_object(
236509
			ctx->hevent,
236509
			NT_SYNC_NON_ALERTABLE,
236509
			0);
236509
236509
	if (iosb.status)
236509
		return iosb.status;
236509
236509
	svcguid->data1    = (objid.obj_id[0]);
236509
	svcguid->data1   |= (objid.obj_id[1] << 8);
236509
	svcguid->data1   |= (objid.obj_id[2] << 16);
236509
	svcguid->data1   |= (objid.obj_id[3] << 24);
236509
236509
	svcguid->data2    = (objid.obj_id[4]);
236509
	svcguid->data2   |= (objid.obj_id[5] << 8);
236509
236509
	svcguid->data3    = (objid.obj_id[6]);
236509
	svcguid->data3   |= (objid.obj_id[7] << 8);
236509
236509
	svcguid->data4[0] = (objid.obj_id[8]);
236509
	svcguid->data4[1] = (objid.obj_id[9]);
236509
	svcguid->data4[2] = (objid.obj_id[10]);
236509
	svcguid->data4[3] = (objid.obj_id[11]);
236509
	svcguid->data4[4] = (objid.obj_id[12]);
236509
	svcguid->data4[5] = (objid.obj_id[13]);
236509
	svcguid->data4[6] = (objid.obj_id[14]);
236509
	svcguid->data4[7] = (objid.obj_id[15]);
236509
236509
	return NT_STATUS_SUCCESS;
236509
}
236509
2420c2
int toks_get_driver_ctx(
2420c2
	char **				argv,
2420c2
	char **				envp,
2420c2
	uint32_t			flags,
2420c2
	struct toks_driver_ctx **	pctx)
2420c2
{
2c9219
	int32_t				status;
2420c2
	struct toks_driver_ctx_impl *	ctx;
2420c2
	struct toks_common_ctx		cctx;
2420c2
	struct toks_split_vector	sargv;
2420c2
	const struct argv_option *	optv[TOKS_OPTV_ELEMENTS];
2420c2
	struct argv_meta *		meta;
2420c2
	struct argv_entry *		entry;
618937
	struct argv_entry *		uuid;
e3e583
	struct argv_entry *		pid;
201fd7
	struct argv_entry *		syspid;
35e873
	struct argv_entry *		cfpid;
35e873
	struct argv_entry *		cspid;
b27e56
	struct argv_entry *		msecs;
867b30
	struct toks_token_string	key;
867b30
	size_t				keylen;
bd4f0c
	uintptr_t			opdata;
618937
	nt_guid				svcguid;
2420c2
	const char *			program;
79252f
	const char *			refstr;
c847e3
	int				ntokens;
8f6a66
	int				atokens;
ee5c9c
	int				loglevel;
e3e583
	int32_t				tokpid;
201fd7
	int32_t				tsyspid;
35e873
	int32_t				ctrlpid;
35e873
	int32_t				csyspid;
b27e56
	int64_t				timeout;
ed52e5
	void *				hlog;
2f270e
	void *				hkernel32;
2420c2
	char *				targv[TOKS_SARGV_ELEMENTS];
2420c2
2420c2
	(void)envp;
2420c2
2420c2
	if (toks_init())
2420c2
		return -1;
2420c2
2420c2
	argv_optv_init(toks_default_options,optv);
2420c2
2420c2
	sargv.targv = targv;
2420c2
	sargv.eargv = 0;
2420c2
2420c2
	if (toks_split_argv(argv,&sargv))
2420c2
		return -1;
2420c2
2420c2
	if (!(meta = argv_get(
2420c2
			sargv.targv,optv,
2420c2
			toks_argv_flags(flags),
2420c2
			STDERR_FILENO)))
2420c2
		return -1;
2420c2
618937
	if (!(flags & TOKS_DRIVER_MODE_CLIENT))
618937
		flags |= TOKS_DRIVER_MODE_SERVER;
618937
ed52e5
	hlog      = 0;
6832b9
	uuid      = 0;
6832b9
	tokpid    = 0;
6832b9
	tsyspid   = 0;
35e873
	ctrlpid   = 0;
35e873
	csyspid   = 0;
6832b9
	keylen    = 0;
35c599
	refstr    = 0;
6832b9
	ntokens   = 0;
ee5c9c
	loglevel  = 0;
6832b9
	timeout   = (-1);
6832b9
	program   = argv_program_name(argv[0]);
618937
2420c2
	memset(&cctx,0,sizeof(cctx));
2420c2
	cctx.drvflags = flags;
2420c2
	cctx.eargv = sargv.eargv;
2420c2
2420c2
	if (!argv[1] && (flags & TOKS_DRIVER_VERBOSITY_USAGE))
2420c2
		return toks_driver_usage(program,0,optv,meta);
2420c2
2420c2
	for (entry=meta->entries; entry->fopt || entry->arg; entry++) {
2420c2
		if (entry->fopt) {
2420c2
			switch (entry->tag) {
2420c2
				case TAG_HELP:
2420c2
					if (flags & TOKS_DRIVER_VERBOSITY_USAGE)
2420c2
						return toks_driver_usage(program,entry->arg,optv,meta);
2420c2
2420c2
				case TAG_VERSION:
2420c2
					cctx.drvflags |= TOKS_DRIVER_VERSION;
2420c2
					break;
2420c2
2420c2
				case TAG_DAEMON:
2420c2
					if (!strcmp("always",entry->arg))
2420c2
						cctx.drvflags |= TOKS_DRIVER_DAEMON_ALWAYS;
2420c2
2420c2
					else if (!strcmp("never",entry->arg))
2420c2
						cctx.drvflags |= TOKS_DRIVER_DAEMON_NEVER;
2420c2
2420c2
					break;
2420c2
2420c2
				case TAG_SYSROOT:
2420c2
					cctx.sysroot = entry->arg;
2420c2
					break;
618937
618937
				case TAG_UUID:
618937
					uuid = entry;
618937
					break;
486fcd
236509
				case TAG_ROOTUUID:
236509
					cctx.drvflags |= TOKS_DRIVER_ROOT_UUID;
236509
					break;
236509
79252f
				case TAG_REFSTR:
79252f
					refstr = entry->arg;
79252f
					break;
79252f
e3e583
				case TAG_PID:
ff65f1
					tokpid = toks_arg_to_int32((pid=entry));
e3e583
					break;
e3e583
201fd7
				case TAG_SYSPID:
ff65f1
					tsyspid = toks_arg_to_int32((syspid=entry));
201fd7
					break;
201fd7
35e873
				case TAG_CTRLPID:
35e873
					ctrlpid = toks_arg_to_int32((cfpid=entry));
35e873
					break;
35e873
35e873
				case TAG_CSYSPID:
35e873
					csyspid = toks_arg_to_int32((cspid=entry));
35e873
					break;
35e873
ee5c9c
				case TAG_LOGLEVEL:
ee5c9c
					loglevel = toks_arg_to_int32(entry);
ee5c9c
					loglevel = (loglevel > 9) ? (-1) : loglevel;
ee5c9c
					cctx.loglevel = loglevel;
ee5c9c
					break;
ee5c9c
b27e56
				case TAG_TIMEOUT:
ff65f1
					timeout = toks_arg_to_int64((msecs=entry));
ff65f1
					timeout = (timeout < 0) ? (-2) : timeout;
b27e56
					break;
b27e56
c847e3
				case TAG_TOKENS:
ff65f1
					ntokens = toks_arg_to_int32(entry);
ff65f1
					ntokens = (ntokens > 9999) ? (-1) : ntokens;
c847e3
					break;
c847e3
486fcd
				case TAG_CONNECT:
486fcd
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
486fcd
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
486fcd
					break;
02451d
520f51
				case TAG_ABORT:
520f51
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
520f51
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
520f51
					cctx.drvflags |= TOKS_DRIVER_ACTION_ABORT;
520f51
					break;
520f51
02451d
				case TAG_ACQUIRE:
02451d
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
02451d
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
02451d
					cctx.drvflags |= TOKS_DRIVER_ACTION_ACQUIRE;
02451d
					break;
867b30
867b30
				case TAG_RELEASE:
867b30
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
867b30
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
867b30
					cctx.drvflags |= TOKS_DRIVER_ACTION_RELEASE;
867b30
867b30
					keylen = toks_strlen(entry->arg);
867b30
867b30
					ntapi->tt_generic_memset(
867b30
						&key,0,sizeof(key));
867b30
867b30
					if (keylen < sizeof(key.token))
867b30
						ntapi->tt_generic_memcpy(
867b30
							&key.token,entry->arg,
867b30
							keylen);
867b30
					break;
ed52e5
ed52e5
				case TAG_LOGFILE:
ed52e5
					cctx.logfile = entry->arg;
ed52e5
					break;
bd4f0c
bd4f0c
				case TAG_NTOKENSGET:
bd4f0c
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
bd4f0c
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
bd4f0c
					cctx.drvflags |= TOKS_DRIVER_ACTION_NTOKENS_GET;
bd4f0c
					break;
0f44d8
0f44d8
				case TAG_NTOKENSSET:
0f44d8
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
0f44d8
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
0f44d8
					cctx.drvflags |= TOKS_DRIVER_ACTION_NTOKENS_SET;
0f44d8
					ntokens = toks_arg_to_int32(entry);
0f44d8
					break;
0b7161
0b7161
				case TAG_GETSVCINFO:
0b7161
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
0b7161
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
0b7161
					cctx.drvflags |= TOKS_DRIVER_ACTION_SVCINFO_GET;
0b7161
					break;
0b7161
0b7161
				case TAG_LOGSVCINFO:
0b7161
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
0b7161
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
0b7161
					cctx.drvflags |= TOKS_DRIVER_ACTION_SVCINFO_LOG;
0b7161
					break;
7f4116
eb3ecd
				case TAG_GETTOKINFO:
eb3ecd
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
eb3ecd
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
eb3ecd
					cctx.drvflags |= TOKS_DRIVER_ACTION_TOKINFO_GET;
eb3ecd
					break;
eb3ecd
5e0b43
				case TAG_LOGTOKINFO:
5e0b43
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
5e0b43
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
5e0b43
					cctx.drvflags |= TOKS_DRIVER_ACTION_TOKINFO_LOG;
5e0b43
					break;
5e0b43
7f4116
				case TAG_LOGLEVELGET:
7f4116
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
7f4116
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
7f4116
					cctx.drvflags |= TOKS_DRIVER_ACTION_LOGLEVEL_GET;
7f4116
					break;
7f4116
7f4116
				case TAG_LOGLEVELSET:
7f4116
					cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
7f4116
					cctx.drvflags |= TOKS_DRIVER_MODE_CLIENT;
7f4116
					cctx.drvflags |= TOKS_DRIVER_ACTION_LOGLEVEL_SET;
7f4116
7f4116
					loglevel = toks_arg_to_int32(entry);
7f4116
					loglevel = (loglevel > 9) ? (-1) : loglevel;
7f4116
					cctx.loglevel = loglevel;
7f4116
7f4116
					break;
2420c2
			}
2420c2
		} else
2420c2
			/* strict */
2420c2
			return toks_driver_usage(program,0,optv,meta);
2420c2
	}
2420c2
e3e583
	if ((cctx.drvflags & TOKS_DRIVER_MODE_CLIENT) && (tokpid < 0)) {
e3e583
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
e3e583
			toks_dprintf(STDERR_FILENO,
201fd7
				"%s: error: %s is not a valid framework process id.",
e3e583
				program,pid->arg);
e3e583
		return toks_get_driver_ctx_fail(meta);
e3e583
	}
e3e583
201fd7
	if ((cctx.drvflags & TOKS_DRIVER_MODE_CLIENT) && (tsyspid < 0)) {
201fd7
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
201fd7
			toks_dprintf(STDERR_FILENO,
201fd7
				"%s: error: %s is not a valid system process id.",
201fd7
				program,syspid->arg);
201fd7
		return toks_get_driver_ctx_fail(meta);
201fd7
	}
201fd7
35e873
	if ((cctx.drvflags & TOKS_DRIVER_MODE_SERVER) && (ctrlpid < 0)) {
35e873
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
35e873
			toks_dprintf(STDERR_FILENO,
35e873
				"%s: error: %s is not a valid controlling "
35e873
				"framework process id.",
35e873
				program,cfpid->arg);
35e873
		return toks_get_driver_ctx_fail(meta);
35e873
	}
35e873
35e873
	if ((cctx.drvflags & TOKS_DRIVER_MODE_SERVER) && (csyspid < 0)) {
35e873
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
35e873
			toks_dprintf(STDERR_FILENO,
35e873
				"%s: error: %s is not a valid controlling "
35e873
				"system process id.",
35e873
				program,cspid->arg);
35e873
		return toks_get_driver_ctx_fail(meta);
35e873
	}
35e873
b27e56
	if ((cctx.drvflags & TOKS_DRIVER_MODE_CLIENT) && (timeout < (-1))) {
b27e56
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
b27e56
			toks_dprintf(STDERR_FILENO,
b27e56
				"%s: error: %s is not a valid timeout in milliseconds.",
b27e56
				program,msecs->arg);
b27e56
		return toks_get_driver_ctx_fail(meta);
b27e56
	}
b27e56
d5c778
	if ((ntokens == 0) && !(cctx.drvflags & TOKS_DRIVER_VERSION)) {
d5c778
		ntokens = (-1);
d5c778
	}
d5c778
d5c778
	if (ntokens == 0) {
d5c778
		cctx.drvflags &= ~(uint64_t)TOKS_DRIVER_MODE_SERVER;
d5c778
	}
d5c778
d5c778
	if ((cctx.drvflags & TOKS_DRIVER_MODE_SERVER) && (ntokens < 0)) {
c847e3
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
c847e3
			toks_dprintf(STDERR_FILENO,
c847e3
				"%s: error: number of tokens not set or is invalid.",
c847e3
				program);
c847e3
		return toks_get_driver_ctx_fail(meta);
c847e3
	}
c847e3
ee5c9c
	if ((cctx.drvflags & TOKS_DRIVER_MODE_SERVER) && (loglevel < 0)) {
ee5c9c
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
ee5c9c
			toks_dprintf(STDERR_FILENO,
ee5c9c
				"%s: error: loglevel must be in the range of 0..9.",
ee5c9c
				program);
ee5c9c
		return toks_get_driver_ctx_fail(meta);
ee5c9c
	}
ee5c9c
618937
	if (uuid && ntapi->tt_string_to_guid_utf8(uuid->arg,&svcguid)) {
618937
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
618937
			toks_dprintf(STDERR_FILENO,
618937
				"%s: error: '%s' is not a valid service guid (did you forget the braces?)",
618937
				program,uuid->arg);
618937
		return toks_get_driver_ctx_fail(meta);
618937
618937
	}
618937
2420c2
	if (cctx.sysroot && toks_open_dir(&cctx.hroot,0,cctx.sysroot,false)) {
2420c2
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
2420c2
			toks_dprintf(STDERR_FILENO,
2420c2
				"%s: error: could not open sysroot directory '%s'",
2420c2
				program,cctx.sysroot);
2420c2
		return toks_get_driver_ctx_fail(meta);
2420c2
	}
2420c2
2420c2
	if (!(ctx = toks_driver_ctx_alloc(meta,&cctx)))
2420c2
		return toks_get_driver_ctx_fail(meta);
2420c2
a830d3
	if (ctx->rtdata->hroot && cctx.sysroot) {
ed52e5
		ntapi->zw_close(ctx->rtdata->hroot);
ed52e5
		ctx->rtdata->hroot = cctx.hroot;
866f59
	} else if (cctx.sysroot) {
866f59
		ctx->rtdata->hroot = cctx.hroot;
a830d3
	} else {
a830d3
		cctx.hroot = ctx->rtdata->hroot;
ed52e5
	}
ed52e5
ed52e5
	if (cctx.logfile && toks_open_log_file(&hlog,ctx->rtdata->hroot,cctx.logfile,false)) {
ed52e5
		if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
ed52e5
			toks_dprintf(STDERR_FILENO,
ed52e5
				"%s: error: could not create logfile '%s'",
ed52e5
				program,cctx.logfile);
ed52e5
		return toks_get_driver_ctx_fail(meta);
ed52e5
	}
ed52e5
ed52e5
	if (ctx->rtdata->hlog && cctx.logfile) {
ed52e5
		ntapi->zw_close(ctx->rtdata->hlog);
ed52e5
		ctx->rtdata->hlog = hlog;
ed52e5
	}
ed52e5
867b30
	if ((cctx.drvflags & TOKS_DRIVER_ACTION_RELEASE)) {
867b30
		if (toks_client_str_to_token(&ctx->ctx,&key)) {
867b30
			if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
867b30
				toks_dprintf(STDERR_FILENO,
867b30
					"%s: error: [%s] is not a valid token.",
867b30
					program,key.token);
867b30
			return toks_get_driver_ctx_fail(meta);
867b30
		}
867b30
	}
867b30
618937
2f270e
	if ((toks_ntapi->tt_create_private_event(
2f270e
			&ctx->hevent,
2f270e
			NT_NOTIFICATION_EVENT,
2f270e
			NT_EVENT_NOT_SIGNALED)))
2f270e
		return toks_get_driver_ctx_fail(meta);
2f270e
236509
236509
	if (uuid) {
236509
		ntapi->tt_guid_copy(
0021e3
			&ctx->guid,
236509
			&svcguid);
236509
236509
	} else if (cctx.drvflags & TOKS_DRIVER_ROOT_UUID) {
236509
		if ((status = toks_set_uuid_from_root(ctx,&svcguid))) {
236509
			if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
236509
				toks_dprintf(STDERR_FILENO,
236509
					"%s: error: could not set the uuid "
236509
					"based on the sysroot directory [0x%08x].",
236509
					program,status);
236509
			return toks_get_driver_ctx_fail(meta);
236509
		}
236509
236509
		ntapi->tt_guid_copy(
0021e3
			&ctx->guid,
236509
			&svcguid);
236509
	} else {
236509
		ntapi->tt_guid_copy(
0021e3
			&ctx->guid,
236509
			&ctx->rtdata->srv_guid);
236509
	}
236509
236509
00069c
	if ((ntapi->tt_open_dev_object_directory(
00069c
			&ctx->hsvcdir,
2c26a1
			NT_SEC_READ_CONTROL
2c26a1
				| NT_DIRECTORY_QUERY
2c26a1
				| NT_DIRECTORY_TRAVERSE
2c26a1
				| NT_DIRECTORY_CREATE_OBJECT
2c26a1
				| NT_DIRECTORY_CREATE_SUBDIRECTORY,
00069c
			toks_service_name,
00069c
			&toks_daemon_default_guid)))
00069c
		return toks_get_driver_ctx_fail(meta);
00069c
2f270e
	if ((hkernel32 = pe_get_kernel32_module_handle()))
2f270e
		if ((ctx->ticks.qpc = pe_get_procedure_address(
2f270e
				hkernel32,"QueryPerformanceCounter")))
2f270e
			ntapi->zw_query_performance_counter(
2f270e
				&(nt_filetime){{0,0}},
2f270e
				&ctx->ticks.pcfreq);
2f270e
ed52e5
8f6a66
	atokens  = ntokens;
8f6a66
	atokens += 0x1fff;
8f6a66
	atokens |= 0xfff;
8f6a66
	atokens ^= 0xfff;
8f6a66
ed52e5
	ctx->hlog               = hlog;
e3e583
	ctx->tokpid             = tokpid;
201fd7
	ctx->tsyspid            = tsyspid;
35e873
	ctx->ctrlpid            = ctrlpid;
35e873
	ctx->csyspid            = csyspid;
c847e3
	ctx->ntokens            = ntokens;
8f6a66
	ctx->atokens            = atokens;
b27e56
	ctx->timeout            = timeout;
2420c2
	ctx->ctx.program	= program;
2420c2
	ctx->ctx.cctx		= &ctx->cctx;
618937
	ctx->cctx.uuid          = &ctx->uuid;
7fcdf2
	ctx->rtdata->hlog       = hlog ? hlog : ctx->rtdata->hlog;
ed52e5
79252f
	toks_set_driver_refstr(&ctx->ctx,refstr);
0021e3
	toks_guid_to_uuid(&ctx->guid,&ctx->uuid);
79252f
c847e3
	if (cctx.drvflags & TOKS_DRIVER_MODE_SERVER) {
3fc30f
		if ((status = toks_set_tmpfs_from_uuid(ctx))) {
3fc30f
			if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
3fc30f
				toks_dprintf(STDERR_FILENO,
3fc30f
					"%s: error: could not create or open "
3fc30f
					"the instance-specific tmpfs directory [0x%08x].",
3fc30f
					program,status);
3fc30f
			return toks_get_driver_ctx_fail(meta);
3fc30f
		}
3fc30f
681a45
		if (!(ctx->waiters = toks_calloc(
681a45
				TOKS_MAX_WAITERS,
681a45
				sizeof(struct toks_waiter))))
d73408
			return toks_get_driver_ctx_fail(meta);
d73408
681a45
		toks_daemon_ctx.driver_ctx   = &ctx->ctx;
681a45
		toks_daemon_ctx.waiter_base  = ctx->waiters;
681a45
		toks_daemon_ctx.waiter_first = ctx->waiters;
681a45
		toks_daemon_ctx.waiter_next  = ctx->waiters;
681a45
		toks_daemon_ctx.waiter_cap   = &ctx->waiters[TOKS_MAX_WAITERS];
681a45
681a45
8f6a66
		if (!(ctx->tokens = toks_calloc(atokens,sizeof(*ctx->tokens))))
c847e3
			return toks_get_driver_ctx_fail(meta);
c847e3
0021e3
		if (toks_daemon_init(&toks_daemon_ctx,&ctx->guid))
f2a689
			return toks_get_driver_ctx_fail(meta);
c847e3
	}
f2a689
2c9219
	if (cctx.drvflags & TOKS_DRIVER_MODE_CLIENT) {
3e9813
		if (cctx.drvflags & TOKS_DRIVER_DAEMON_ALWAYS) {
3e9813
			toks_daemon_ctx.driver_ctx = &ctx->ctx;
3e9813
3e9813
			switch ((status = toks_daemon_init(&toks_daemon_ctx,0))) {
3e9813
				case NT_STATUS_SUCCESS:
3e9813
					break;
3e9813
3e9813
				default:
3e9813
					if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
3e9813
						toks_dprintf(STDERR_FILENO,
3e9813
							"%s: error: failed to initialize signal handling "
3e9813
							"(check the system's documentation) [0x%x].",
3e9813
							program,status);
3e9813
3e9813
					return toks_get_driver_ctx_fail(meta);
3e9813
			}
3e9813
		}
3e9813
2c9219
		switch ((status = toks_client_connect(&ctx->ctx))) {
2c9219
			case NT_STATUS_SUCCESS:
2c9219
				break;
2c9219
2c9219
			case NT_STATUS_OBJECT_NAME_NOT_FOUND:
2c9219
				if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
2c9219
					toks_dprintf(STDERR_FILENO,
2c9219
						"%s: error: could not connect "
2c9219
						"(server not running) [0x%x].",
2c9219
						program,status);
2c9219
2c9219
				return toks_get_driver_ctx_fail(meta);
2c9219
2c9219
			case NT_STATUS_CONNECTION_REFUSED:
2c9219
			case NT_STATUS_PORT_CONNECTION_REFUSED:
2c9219
				if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
2c9219
					toks_dprintf(STDERR_FILENO,
2c9219
						"%s: error: could not connect "
2c9219
						"(connection refused) [0x%x].",
2c9219
						program,status);
2c9219
2c9219
				return toks_get_driver_ctx_fail(meta);
2c9219
2c9219
			case NT_STATUS_ACCESS_DENIED:
2c9219
				if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
2c9219
					toks_dprintf(STDERR_FILENO,
2c9219
						"%s: error: could not connect "
2c9219
						"(access denied) [0x%x].",
2c9219
						program,status);
2c9219
2c9219
				return toks_get_driver_ctx_fail(meta);
2c9219
2c9219
			default:
2c9219
				if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
2c9219
					toks_dprintf(STDERR_FILENO,
2c9219
						"%s: error: could not connect "
2c9219
						"(check the system's documentation) [0x%x].",
2c9219
						program,status);
2c9219
2c9219
				return toks_get_driver_ctx_fail(meta);
2c9219
		}
bd4f0c
bd4f0c
		if (cctx.drvflags & TOKS_DRIVER_ACTION_NTOKENS_GET) {
bd4f0c
			status = toks_service_ioctl(
bd4f0c
				&ctx->ctx,TOKS_IOCTL_GET_TOKEN_COUNT,
bd4f0c
				&opdata,0);
bd4f0c
bd4f0c
			switch (status) {
bd4f0c
				case NT_STATUS_SUCCESS:
bd4f0c
					ctx->ntokens = opdata;
bd4f0c
					break;
bd4f0c
bd4f0c
				default:
bd4f0c
					if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
bd4f0c
						toks_dprintf(STDERR_FILENO,
bd4f0c
							"%s: error: could not obtain the current "
bd4f0c
							"overall number of tokens "
bd4f0c
							"(check the system's documentation) [0x%x].",
bd4f0c
							program,status);
bd4f0c
bd4f0c
					return toks_get_driver_ctx_fail(meta);
bd4f0c
			}
bd4f0c
		}
7f4116
7f4116
		if (cctx.drvflags & TOKS_DRIVER_ACTION_LOGLEVEL_GET) {
7f4116
			status = toks_service_ioctl(
7f4116
				&ctx->ctx,TOKS_IOCTL_GET_LOG_LEVEL,
7f4116
				&opdata,0);
7f4116
7f4116
			switch (status) {
7f4116
				case NT_STATUS_SUCCESS:
7f4116
					ctx->cctx.loglevel = opdata;
7f4116
					break;
7f4116
7f4116
				default:
7f4116
					if (flags & TOKS_DRIVER_VERBOSITY_ERRORS)
7f4116
						toks_dprintf(STDERR_FILENO,
7f4116
							"%s: error: could not obtain the server's "
7f4116
							"current log-level parameter "
7f4116
							"(check the system's documentation) [0x%x].",
7f4116
							program,status);
7f4116
7f4116
					return toks_get_driver_ctx_fail(meta);
7f4116
			}
7f4116
		}
2c9219
	}
486fcd
2420c2
	*pctx = &ctx->ctx;
2420c2
	return TOKS_OK;
2420c2
}
2420c2
2420c2
static void toks_free_driver_ctx_impl(struct toks_driver_ctx_alloc * ictx)
2420c2
{
3fc30f
	if (ictx->ctx.htmpfs)
3fc30f
		ntapi->zw_close(ictx->ctx.htmpfs);
3fc30f
2f270e
	if (ictx->ctx.hevent)
2f270e
		ntapi->zw_close(ictx->ctx.hevent);
2f270e
00069c
	if (ictx->ctx.hsvcdir)
00069c
		ntapi->zw_close(ictx->ctx.hsvcdir);
00069c
00069c
	if (ictx->ctx.hsvclink)
00069c
		ntapi->zw_close(ictx->ctx.hsvclink);
00069c
aba5a2
	if (ictx->ctx.hservice)
aba5a2
		ntapi->zw_close(ictx->ctx.hservice);
aba5a2
83e502
	if (ictx->ctx.hserver)
83e502
		ntapi->zw_close(ictx->ctx.hserver);
83e502
ed52e5
	if (ictx->ctx.hlog)
ed52e5
		ntapi->zw_close(ictx->ctx.hlog);
ed52e5
c847e3
	if (ictx->ctx.tokens)
c847e3
		toks_free(ictx->ctx.tokens);
c847e3
d73408
	if (ictx->ctx.waiters)
d73408
		toks_free(ictx->ctx.waiters);
d73408
2420c2
	argv_free(ictx->meta);
2420c2
	free(ictx);
2420c2
}
2420c2
2420c2
void toks_free_driver_ctx(struct toks_driver_ctx * ctx)
2420c2
{
2420c2
	struct toks_driver_ctx_alloc *	ictx;
2420c2
	uintptr_t			addr;
2420c2
2420c2
	if (ctx) {
2420c2
		addr = (uintptr_t)ctx - offsetof(struct toks_driver_ctx_impl,ctx);
2420c2
		addr = addr - offsetof(struct toks_driver_ctx_alloc,ctx);
2420c2
		ictx = (struct toks_driver_ctx_alloc *)addr;
2420c2
		toks_free_driver_ctx_impl(ictx);
2420c2
	}
2420c2
}
2420c2
b27e56
void toks_driver_set_timeout(struct toks_driver_ctx * dctx, int64_t millisecs)
b27e56
{
b27e56
	toks_set_driver_timeout(dctx,millisecs);
b27e56
}
b27e56
b27e56
void toks_driver_unset_timeout(struct toks_driver_ctx * dctx)
b27e56
{
b27e56
	toks_set_driver_timeout(dctx,(-1));
b27e56
}
b27e56
f4d6f1
void toks_driver_set_refstr(struct toks_driver_ctx * dctx, const char * refstr)
f4d6f1
{
f4d6f1
	toks_set_driver_refstr(dctx,refstr);
f4d6f1
}
f4d6f1
f4d6f1
void toks_driver_unset_refstr(struct toks_driver_ctx * dctx)
f4d6f1
{
f4d6f1
	toks_set_driver_refstr(dctx,0);
f4d6f1
}
f4d6f1
2420c2
const struct toks_source_version * toks_source_version(void)
2420c2
{
2420c2
	return &toks_src_version;
2420c2
}