/*********************************************************/
/* ptycon: a pty-console bridge */
/* Copyright (C) 2016 Z. Gilboa */
/* Released under GPLv2 and GPLv3; see COPYING.PTYCON. */
/*********************************************************/
#include <ntcon/ntcon.h>
#include <ntapi/ntapi.h>
#include <ntapi/nt_atomic.h>
#include <gdi/gdi.h>
#include <stdint.h>
#include <ptycon/ptycon.h>
#include "ptycon_init_impl.h"
#include "ptycon_nolibc_impl.h"
#define ARGV_DRIVER
#include "ptycon_version.h"
#include "ptycon_daemon_impl.h"
#include "ptycon_driver_impl.h"
#include "argv/argv.h"
/* 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 * ptyc_ntapi;
/* ntcon accessor table */
static ntcon_vtbl ptyc_ntcon_vtbl;
const ntcon_vtbl * const ptyc_ntcon = &ptyc_ntcon_vtbl;
/* gdi accessor table */
static gdi_vtbl ptyc_gdi_vtbl;
const gdi_vtbl * const ptyc_gdi = &ptyc_gdi_vtbl;
/* daemon */
static struct ptyc_daemon_ctx ptyc_daemon_ctx;
/* package info */
static const struct ptyc_source_version ptyc_src_version = {
PTYC_TAG_VER_MAJOR,
PTYC_TAG_VER_MINOR,
PTYC_TAG_VER_PATCH,
PTYCON_GIT_VERSION
};
struct ptyc_driver_ctx_alloc {
struct argv_meta * meta;
struct ptyc_driver_ctx_impl ctx;
uint64_t guard;
const char * units[];
};
struct ptyc_split_vector {
char ** targv;
char ** eargv;
};
static int32_t ptyc_once = 0;
static int32_t ptyc_vtbl_init(void)
{
int32_t status;
nt_timeout timeout;
(void)tty_affiliation;
switch (at_locked_cas_32(&ptyc_once,0,1)) {
case 0:
if ((status = ntcon_vtbl_init(&ptyc_ntcon_vtbl))) {
at_locked_add_32(&ptyc_once,2);
return status;
}
if ((status = gdi_vtbl_init(&ptyc_gdi_vtbl))) {
at_locked_add_32(&ptyc_once,2);
return status;
}
at_locked_inc_32(&ptyc_once);
return 0;
case 1:
timeout.quad = -10;
for (; (at_locked_cas_32(&ptyc_once,0,1) == 1); )
ntapi->zw_delay_execution(
NT_SYNC_ALERTABLE,
&timeout);
return (ptyc_once == 2)
? 0 : -1;
case 2:
return 0;
case 3:
default:
return -1;
}
}
static uint32_t ptyc_argv_flags(uint32_t flags)
{
uint32_t ret = 0;
if (flags & PTYC_DRIVER_VERBOSITY_NONE)
ret |= ARGV_VERBOSITY_NONE;
if (flags & PTYC_DRIVER_VERBOSITY_ERRORS)
ret |= ARGV_VERBOSITY_ERRORS;
if (flags & PTYC_DRIVER_VERBOSITY_STATUS)
ret |= ARGV_VERBOSITY_STATUS;
return ret;
}
static int ptyc_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,header,optv,arg);
argv_free(meta);
return PTYC_USAGE;
}
static struct ptyc_driver_ctx_impl * ptyc_driver_ctx_alloc(
struct argv_meta * meta,
const struct ptyc_common_ctx * cctx,
size_t nunits)
{
struct ptyc_driver_ctx_alloc * ictx;
size_t size;
struct argv_entry * entry;
const char ** units;
nt_runtime_data * rtdata;
if (ntapi->tt_get_runtime_data(&rtdata,0))
return 0;
size = sizeof(struct ptyc_driver_ctx_alloc);
size += (nunits+1)*sizeof(const char *);
if (!(ictx = calloc(1,size)))
return 0;
if (cctx)
memcpy(&ictx->ctx.cctx,cctx,sizeof(*cctx));
for (entry=meta->entries,units=ictx->units; entry->fopt || entry->arg; entry++)
if (entry->tag == TAG_CAT)
*units++ = entry->arg;
ictx->meta = meta;
ictx->ctx.ctx.units = ictx->units;
ictx->ctx.rtdata = rtdata;
return &ictx->ctx;
}
static int ptyc_get_driver_ctx_fail(struct argv_meta * meta)
{
argv_free(meta);
return -1;
}
#define PTYC_SARGV_ELEMENTS 1024
static int ptyc_split_argv(
char ** argv,
struct ptyc_split_vector * sargv)
{
ptrdiff_t argc;
char ** parg;
/* argc */
for (parg=argv; *parg; )
parg++;
if ((argc = parg - argv) >= PTYC_SARGV_ELEMENTS)
return -1;
/* clone argv into targv */
ntapi->tt_aligned_block_memset(
(uintptr_t *)sargv->targv,
0,PTYC_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;
}
int ptyc_get_driver_ctx(
char ** argv,
char ** envp,
uint32_t flags,
struct ptyc_driver_ctx ** pctx)
{
struct ptyc_driver_ctx_impl * ctx;
struct ptyc_common_ctx cctx;
struct ptyc_split_vector sargv;
const struct argv_option * optv[PTYC_OPTV_ELEMENTS];
struct argv_meta * meta;
struct argv_entry * entry;
size_t nunits;
const char * program;
char * targv[PTYC_SARGV_ELEMENTS];
(void)envp;
if (ptyc_init())
return -1;
if (ptyc_vtbl_init())
return -1;
argv_optv_init(ptyc_default_options,optv);
sargv.targv = targv;
sargv.eargv = 0;
if (ptyc_split_argv(argv,&sargv))
return -1;
if (!(meta = argv_get(sargv.targv,optv,ptyc_argv_flags(flags))))
return -1;
nunits = 0;
program = argv_program_name(argv[0]);
memset(&cctx,0,sizeof(cctx));
cctx.drvflags = flags;
cctx.eargv = sargv.eargv;
if (!argv[1] && (flags & PTYC_DRIVER_VERBOSITY_USAGE))
return ptyc_driver_usage(program,0,optv,meta);
/* get options, count units */
for (entry=meta->entries; entry->fopt || entry->arg; entry++) {
if (entry->fopt) {
switch (entry->tag) {
case TAG_HELP:
if (flags & PTYC_DRIVER_VERBOSITY_USAGE)
return ptyc_driver_usage(program,entry->arg,optv,meta);
case TAG_VERSION:
cctx.drvflags |= PTYC_DRIVER_VERSION;
break;
case TAG_DAEMON:
if (!strcmp("always",entry->arg))
cctx.drvflags |= PTYC_DRIVER_DAEMON_ALWAYS;
else if (!strcmp("never",entry->arg))
cctx.drvflags |= PTYC_DRIVER_DAEMON_NEVER;
break;
case TAG_DEBUG:
if (!entry->arg) {
cctx.drvflags |= PTYC_DRIVER_DBG_EVENT;
} else if (!strcmp("event",entry->arg)) {
cctx.drvflags |= PTYC_DRIVER_DBG_EVENT;
} else if (!strcmp("oven",entry->arg)) {
cctx.drvflags &= ~(uint64_t)PTYC_DRIVER_DBG_RAW;
cctx.drvflags |= PTYC_DRIVER_DBG_OVEN;
} else if (!strcmp("raw",entry->arg)) {
cctx.drvflags &= ~(uint64_t)PTYC_DRIVER_DBG_OVEN;
cctx.drvflags |= PTYC_DRIVER_DBG_RAW;
}
break;
case TAG_BELL:
if ((!entry->arg) || !strcmp("on",entry->arg))
cctx.drvflags |= PTYC_DRIVER_BELL;
break;
case TAG_CAT:
nunits++;
break;
case TAG_SYSROOT:
cctx.sysroot = entry->arg;
break;
case TAG_WAIT:
cctx.drvflags |= PTYC_DRIVER_DBG_WAIT;
break;
}
} else
/* strict */
return ptyc_driver_usage(program,0,optv,meta);
}
if (cctx.sysroot && ptyc_open_dir(&cctx.hroot,0,cctx.sysroot,false)) {
if (flags & PTYC_DRIVER_VERBOSITY_ERRORS)
fprintf(stderr,
"%s: error: could not open sysroot directory '%s'",
program,cctx.sysroot);
return ptyc_get_driver_ctx_fail(meta);
}
if (!(ctx = ptyc_driver_ctx_alloc(meta,&cctx,nunits)))
return ptyc_get_driver_ctx_fail(meta);
if (ptyc_daemon_init(&ptyc_daemon_ctx,cctx.drvflags))
return ptyc_get_driver_ctx_fail(meta);
ctx->ctx.program = program;
ctx->ctx.cctx = &ctx->cctx;
*pctx = &ctx->ctx;
return PTYC_OK;
}
int ptyc_create_driver_ctx(
const struct ptyc_common_ctx * cctx,
struct ptyc_driver_ctx ** pctx)
{
const struct argv_option * optv[PTYC_OPTV_ELEMENTS];
struct argv_meta * meta;
struct ptyc_driver_ctx_impl * ctx;
char * argv[] = {"ptycon_driver",0};
if (ptyc_init())
return -1;
if (ptyc_vtbl_init())
return -1;
argv_optv_init(ptyc_default_options,optv);
if (!(meta = argv_get(argv,optv,0)))
return -1;
if (!(ctx = ptyc_driver_ctx_alloc(meta,cctx,0)))
return ptyc_get_driver_ctx_fail(0);
ctx->ctx.cctx = &ctx->cctx;
memcpy(&ctx->cctx,cctx,sizeof(*cctx));
*pctx = &ctx->ctx;
return PTYC_OK;
}
static void ptyc_free_driver_ctx_impl(struct ptyc_driver_ctx_alloc * ictx)
{
ptyc_free_pty(&ictx->ctx.ctx);
argv_free(ictx->meta);
free(ictx);
}
void ptyc_free_driver_ctx(struct ptyc_driver_ctx * ctx)
{
struct ptyc_driver_ctx_alloc * ictx;
uintptr_t addr;
if (ctx) {
addr = (uintptr_t)ctx - offsetof(struct ptyc_driver_ctx_impl,ctx);
addr = addr - offsetof(struct ptyc_driver_ctx_alloc,ctx);
ictx = (struct ptyc_driver_ctx_alloc *)addr;
ptyc_free_driver_ctx_impl(ictx);
}
}
const struct ptyc_source_version * ptyc_source_version(void)
{
return &ptyc_src_version;
}