Blob Blame History Raw
/*********************************************************/
/*  ptycon: a pty-console bridge                         */
/*  Copyright (C) 2016  Z. Gilboa                        */
/*  Released under GPLv2 and GPLv3; see COPYING.PTYCON.  */
/*********************************************************/

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

#include <ptycon/ptycon.h>
#include "ptycon_driver_impl.h"

static int32_t __stdcall ptyc_spawn_return(
	nt_runtime_data_block *	rtblock,
	int32_t			status)
{
	nt_runtime_data *	rtdata;

	rtdata = (nt_runtime_data *)rtblock;

	if (rtdata->hready)
		ntapi->zw_close(
			rtdata->hready);

	ntapi->zw_free_virtual_memory(
		NT_CURRENT_PROCESS_HANDLE,
		&rtblock->addr,
		&rtblock->size,
		NT_MEM_RELEASE);

	return status;
}

int __stdcall ptyc_spawn(struct ptyc_driver_ctx * dctx)
{
	int32_t				status;
	char *				patharg;
	void *				hchild[2];
	nt_create_process_params	params;
	struct ptyc_driver_ctx_impl *	ictx;
	struct ptyc_client_ctx *	clctx;
	nt_tty_session_info		session;
	nt_runtime_data_block		rtblock;
	nt_runtime_data *		rdata;
	nt_rtdata *			self;
	nt_peb * 			peb;
	void *				hat;
	void *				hfile;
	char **				rargv;
	char **				renvp;
	wchar16_t **			pwarg;
	wchar16_t *			wch;
	nt_unicode_string *		imgname;
	uint32_t			written;
	uintptr_t			imgbuf[8192/sizeof(uintptr_t)];

	/* init */
	if (!(ictx = ptyc_get_driver_ictx(dctx)))
		return NT_STATUS_INVALID_HANDLE;

	if (!dctx->cctx->eargv)
		return NT_STATUS_SUCCESS;

	self  = ictx->rtdata;
	clctx = &ictx->clctx;

	/* hat */
	if (!(peb = (nt_peb *)pe_get_peb_address()))
		return NT_STATUS_INTERNAL_ERROR;

	if (!peb->process_params)
		return NT_STATUS_INTERNAL_ERROR;

	hat = (dctx->cctx->hroot && (dctx->cctx->eargv[0][0] == '/'))
		? dctx->cctx->hroot
		: self->hcwd
			? self->hcwd
			: peb->process_params->cwd_handle;

	patharg = (dctx->cctx->eargv[0][0] == '/')
		? (dctx->cctx->eargv[0][1] == '?')
			? &dctx->cctx->eargv[0][0]
			: &dctx->cctx->eargv[0][1]
		: &dctx->cctx->eargv[0][0];

	/* hfile */
	if ((status = ptyc_open_file(&hfile,hat,patharg,true)))
		return status;

	/* rtblock */
	rtblock.addr		= 0;
	rtblock.size		= 0x10000;
	rtblock.remote_addr	= 0;
	rtblock.remote_size	= 0;
	rtblock.flags		= 0;

	if ((status = ntapi->zw_allocate_virtual_memory(
			self->hprocess_self,
			&rtblock.addr,0,
			&rtblock.size,
			NT_MEM_COMMIT,
			NT_PAGE_READWRITE)))
		return status;

	ntapi->tt_aligned_block_memset(
		rtblock.addr,0,rtblock.size);

	if ((status = ntapi->zw_query_object(
			hfile,
			NT_OBJECT_NAME_INFORMATION,
			imgbuf,sizeof(imgbuf),
			&written)))
		return ptyc_spawn_return(
			&rtblock,status);

	imgname = (nt_unicode_string *)imgbuf;
	rdata   = (nt_runtime_data *)rtblock.addr;

	/* argv, envp */
	if ((status = ntapi->tt_array_copy_utf8(
			&rdata->argc,
			(const char **)dctx->cctx->eargv,
			(const char **)self->envp,
			0,0,0,
			rtblock.addr,
			rdata->buffer,
			rtblock.size - sizeof(*rdata),
			&rtblock.remote_size)))
		return ptyc_spawn_return(
			&rtblock,status);

	rdata->argv = (char **)&((nt_runtime_data *)0)->buffer;
	rdata->envp = rdata->argv + rdata->argc + 1;

	rdata->wargv = (wchar16_t **)(rdata->buffer + (rtblock.remote_size / sizeof(uintptr_t)) + 1);
	rdata->wenvp = rdata->wargv + rdata->argc + 1;

	rargv = rdata->argv + ((uintptr_t)rtblock.addr / sizeof(char *));
	renvp = rdata->envp + ((uintptr_t)rtblock.addr / sizeof(char *));

	pwarg = rdata->wenvp + self->envc + 1;
	wch = (wchar16_t *)pwarg;

	if ((status = ntapi->tt_array_convert_utf8_to_utf16(
			rargv,
			rdata->wargv,
			rdata,
			wch,
			rtblock.size - sizeof(wchar16_t)*(wch-(wchar16_t *)rdata->buffer),
			&rtblock.remote_size)))
		return ptyc_spawn_return(
			&rtblock,status);

	wch += rtblock.remote_size/sizeof(wchar16_t);

	if ((status = ntapi->tt_array_convert_utf8_to_utf16(
			renvp,
			rdata->wenvp,
			rdata,
			wch,
			rtblock.size - sizeof(wchar16_t)*(wch-(wchar16_t *)rdata->buffer),
			&rtblock.remote_size)))
		return ptyc_spawn_return(
			&rtblock,status);

	rdata->wargv -= (uintptr_t)rtblock.addr / sizeof(wchar16_t *);
	rdata->wenvp -= (uintptr_t)rtblock.addr / sizeof(wchar16_t *);

	/* session */
	if ((status = ntapi->tt_create_inheritable_event(
			&rdata->hready,
			NT_NOTIFICATION_EVENT,
			NT_EVENT_NOT_SIGNALED)))
		return ptyc_spawn_return(
			&rtblock,status);

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

	rdata->hroot = dctx->cctx->hroot;
	rdata->hcwd  = self->hcwd
			? self->hcwd
			: peb->process_params->cwd_handle;

	rdata->srv_keys[0] = self->srv_keys[0];
	rdata->srv_keys[1] = self->srv_keys[1];
	rdata->srv_keys[2] = self->srv_keys[2];
	rdata->srv_keys[3] = self->srv_keys[3];
	rdata->srv_keys[4] = self->srv_keys[4];
	rdata->srv_keys[5] = self->srv_keys[5];

	rdata->hstdin      = NT_INVALID_HANDLE_VALUE;
	rdata->hstdout     = NT_INVALID_HANDLE_VALUE;
	rdata->hstderr     = NT_INVALID_HANDLE_VALUE;

	rdata->stdin_type  = NT_FILE_TYPE_PTY;
	rdata->stdout_type = NT_FILE_TYPE_PTY;
	rdata->stderr_type = NT_FILE_TYPE_PTY;

	rdata->ptyany[0]   = clctx->clinfo.any[0];
	rdata->ptyany[1]   = clctx->clinfo.any[1];
	rdata->ptyany[2]   = clctx->clinfo.any[2];
	rdata->ptyany[3]   = clctx->clinfo.any[3];

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

	params.image_name		= imgname->buffer;
	params.rtblock			= &rtblock;
	params.creation_flags_process	= NT_PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
	params.creation_flags_thread	= NT_PROCESS_CREATE_FLAGS_CREATE_THREAD_SUSPENDED;

	/* hoppla */
	if ((status = ntapi->tt_create_native_process(&params)))
		return ptyc_spawn_return(
			&rtblock,status);

	/* clctx */
	clctx->hprocess		= params.hprocess;
	clctx->hthread		= params.hthread;
	clctx->cid.process_id	= params.cid.process_id;
	clctx->cid.thread_id	= params.cid.thread_id;

	if ((status = ntapi->tty_client_process_register(
			self->hsession,
			params.pbi.unique_process_id,
			0,NT_TTY_INHERIT_HANDLES,0)))
		ntapi->zw_terminate_process(
			params.hprocess,
			status);

	session.pid    = 0;
	session.pgid   = 0;
	session.sid    = 0;
	session.syspid = params.pbi.unique_process_id;

	if ((status = ntapi->tty_client_session_set(0,&session)))
		ntapi->zw_terminate_process(
			params.hprocess,
			status);

	if ((status = ntapi->zw_resume_thread(params.hthread,0)))
		ntapi->zw_terminate_process(
			params.hprocess,
			status);

	hchild[1] = params.hprocess;
	hchild[0] = rdata->hready;

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

	return ptyc_spawn_return(
		&rtblock,status);
}