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

#include <psxtypes/psxtypes.h>
#include <ntapi/ntapi.h>

#include <toksvc/toksvc.h>
#include "toksvc_driver_impl.h"
#include "toksvc_daemon_impl.h"

#define TOKS_STR(x)  #x
#define TOKS_XSTR(x) TOKS_STR(x)

static char toks_service_image[] = TOKS_XSTR(TOKS_SERVICE_IMAGE);

static int32_t toks_spawn_child_exit_code(void * hprocess)
{
	nt_process_basic_information pbi;

	if (ntapi->zw_query_information_process(
			hprocess,
			NT_PROCESS_BASIC_INFORMATION,
			&pbi,sizeof(pbi),
			&(uint32_t){0}))
		return NT_STATUS_GENERIC_COMMAND_FAILED;

	return pbi.exit_status;
}

static int toks_spawn_impl(
	struct toks_driver_ctx *        dctx,
	struct toks_common_ctx *        cctx,
	int                             ntokens,
	int32_t                         ctrlpid,
	int32_t                         csyspid)
{
	int32_t				status;
	nt_spawn_process_params		sparams;
	nt_runtime_data			rtctx;
	nt_rtdata *			self;
	char *				img;
	void *				hroot;
	void *				hlog;
	const char *			logfile;
	char **				pearg;
	char *				eargv[10];
	char				tokbuf[32];
	char				pidbuf[32];
	char				logbuf[32];
	char				guidstr[40];
	void *				hduo[2];

	/* init */
	self = toks_get_driver_rtdata(dctx);
	img  = toks_service_image;

	if (!(hroot = cctx->hroot) && cctx->sysroot)
		if ((status = toks_open_dir(&hroot,0,cctx->sysroot,false)))
			return status;

	if (hroot && (img[0] == '/'))
		img++;

	logfile = cctx->logfile;

	if (hroot && logfile && (logfile[0] == '/'))
		logfile++;

	if (logfile && (status = toks_open_log_file(&hlog,hroot,logfile,false)))
		return status;

	/* eargv */
	pearg    = eargv;
	*pearg++ = img;

	ntapi->sprintf(tokbuf,"%d",ntokens);
	*pearg++ = "-t";
	*pearg++ = tokbuf;

	ntapi->sprintf(logbuf,"%d",cctx->loglevel);
	*pearg++ = "-O";
	*pearg++ = logbuf;

	if (cctx->uuid) {
		toks_uuid_to_string(cctx->uuid,&guidstr);
		*pearg++ = "-u";
		*pearg++ = guidstr;
	}

	if (ctrlpid) {
		ntapi->sprintf(pidbuf,"%d",ctrlpid);
		*pearg++ = "-l";
		*pearg++ = pidbuf;
	} else if (csyspid) {
		ntapi->sprintf(pidbuf,"%d",csyspid);
		*pearg++ = "-L";
		*pearg++ = pidbuf;
	}

	*pearg++ = 0;

	/* sparams */
	ntapi->tt_aligned_block_memset(
		&sparams,0,sizeof(sparams));

	sparams.rtctx    = &rtctx;
	sparams.patharg  = img;
	sparams.argv     = eargv;
	sparams.envp     = self->envp;
	sparams.hroot    = hroot ? hroot : self->hroot;

	if ((status = toks_open_file(&sparams.himage,hroot,img,true)))
		return status;

	/* rtctx */
	ntapi->tt_aligned_block_memset(
		&rtctx,0,sizeof(rtctx));

	ntapi->tt_aligned_block_memcpy(
		(uintptr_t *)&rtctx.cid_parent,
		(uintptr_t *)&self->cid_self,
		sizeof(nt_cid));

	rtctx.hlog  = hlog;
	rtctx.hcwd  = self->hcwd;
	rtctx.hroot = sparams.hroot;
	rtctx.flags = self->flags | NT_RUNTIME_DATA_TTY_TOP_LEVEL;

	rtctx.dbg_level = 0;
	rtctx.log_level = self->log_level;

	rtctx.hstdin      = NT_INVALID_HANDLE_VALUE;
	rtctx.hstdout     = NT_INVALID_HANDLE_VALUE;
	rtctx.hstderr     = NT_INVALID_HANDLE_VALUE;

	rtctx.stdin_type  = NT_FILE_TYPE_UNKNOWN;
	rtctx.stdout_type = NT_FILE_TYPE_UNKNOWN;
	rtctx.stderr_type = NT_FILE_TYPE_UNKNOWN;

	/* service synchronization */
	if ((status = ntapi->tt_create_inheritable_event(
			&rtctx.hsync,
			NT_NOTIFICATION_EVENT,
			NT_EVENT_NOT_SIGNALED)))
		return status;

	/* hoppla */
	status = ntapi->tt_spawn_native_process(&sparams);

	if (sparams.himage)
		ntapi->zw_close(sparams.himage);

	if (status)
		return status;

	/* child ready? */
	if (!(sparams.eready.signal_state))
		status = toks_spawn_child_exit_code(sparams.hprocess);

	/* service synchronization */
	hduo[0] = sparams.hprocess;
	hduo[1] = rtctx.hsync;

	ntapi->zw_wait_for_multiple_objects(
		2,hduo,
		NT_WAIT_ANY,
		NT_SYNC_NON_ALERTABLE,
		0);

	/* finalize */
	ntapi->zw_close(sparams.hprocess);
	ntapi->zw_close(sparams.hthread);

	/* all done */
	return status;
}

int32_t toks_service_start(
	struct toks_common_ctx *    cctx,
	int                         ntokens,
	int32_t                     ctrlpid,
	int32_t                     csyspid)
{
	int32_t                     status;
	struct toks_driver_ctx *    dctx;
	char *                      eargv[2];

	eargv[0] = "toksvc.driver";
	eargv[1] = 0;

	status = toks_get_driver_ctx(eargv,0,TOKS_DRIVER_VERSION,&dctx);

	if (status == NT_STATUS_SUCCESS) {
		status = toks_spawn_impl(dctx,cctx,ntokens,ctrlpid,csyspid);
		toks_free_driver_ctx(dctx);
	}

	return status;
}