/*
 * This program executes the mbxnet_daemon program as a detached process
 * with its standard input set to a mailbox.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <descrip.h>
#include <uaidef.h>
#include <iodef.h>
#include <dvidef.h>
#include <ssdef.h>
#include <prcdef.h>

int SYS$CREMBX(), LIB$GETDVI(), SYS$QIOW(), SYS$SETIMR(), LIB$EMUL();
int LIB$DO_COMMAND(), SYS$SETEF(), SYS$CLREF(), SYS$CANTIM(), SYS$SYNCH();
int SYS$CREPRC(), LIB$SET_SYMBOL(), SYS$GETUAI(), SYS$EXIT();
#define DAEMON_TIMEOUT 30
#define USERNAME_MAX 39

static $DESCRIPTOR(daemon_image, "mbxnet_system:mbxnet_daemon.exe" );
static $DESCRIPTOR(daemon_procname, "MBXNET Daemon" );
static long daemon_pid;

struct io_status_block { unsigned short status, length; long pid; };

typedef struct { short length, code; char *buffer; int *ret_len; } item_list;

static void daemon_ready ( struct io_status_block *iosb )
{
    iosb->status = 1;
    SYS$SETEF ( iosb->pid );
}

static int spawn_daemon(  
	char *outfile, 			/* sys$output for created process */
	int timeout,			/* Seconds */
	char **mailbox_name, 		/* Mailbox for sys$input (returned) */
	int *channel )			/* I/O channel assigned to mailbox */
{
    /*
     * Create mailbox that becomes sys$input for created process.
     */
    int code, namlen, status;
    long delta[2], factor, addend;
    static char nambuf[64];
    static $DESCRIPTOR(mailbox,nambuf);
    static $DESCRIPTOR(outfile_dx,"_NL:");
    struct io_status_block iosb, iosb2;

    *channel = namlen = 0;
    *mailbox_name = nambuf;
    status = SYS$CREMBX ( 0, channel, 256, 280, 0x0ff00, 0, 0, 0 );
    if ( (status&1) == 0 ) return status;
    code = DVI$_DEVNAM;
    status = LIB$GETDVI ( &code, channel, 0, 0, &mailbox, &namlen );
    if ( (status&1) == 0 ) return status;
    mailbox.dsc$w_length = namlen;
    nambuf[namlen] = '\0';
    if ( outfile ) {
	outfile_dx.dsc$a_pointer = outfile;
	outfile_dx.dsc$w_length = strlen ( outfile_dx.dsc$a_pointer );
    }
    /*
     * Create process with mailbox as stdin.
     */
    status = SYS$CREPRC ( &daemon_pid, &daemon_image, &mailbox, &outfile_dx, 
	&outfile_dx, 0, 0, &daemon_procname, 4, 0, 0, PRC$M_DETACH );
    if ( (status&1) == 0 ) {
	fprintf(stderr,"Error creating MBXNET Daemon process, status=%x",status );
	return status;
    }
    /*
     * Set timer and read attention AST, ASTs will set status in iosb2 and
     * set event flag.
     */
    iosb2.status = 0;
    iosb2.pid = 22;
    SYS$CLREF ( iosb2.pid );
    factor = -10000000;
    addend = 0;
    LIB$EMUL ( &timeout, &factor, &addend, delta );
    status = SYS$SETIMR ( iosb2.pid, delta, SYS$EXIT, SS$_TIMEOUT, 0 );
    if ( (status&1) == 0 ) return status;

    status = SYS$QIOW ( 23, *channel, IO$_SETMODE|IO$M_READATTN, &iosb, 0, 0,
	daemon_ready, &iosb2, 0, 0, 0, 0 );
    if ( (status&1) == 1 ) status = iosb.status;
    if ( (status&1) == 1 ) status = SYS$SYNCH ( iosb2.pid, &iosb2 );
    if ( (status&1) == 1 ) status = iosb2.status;

    return status;
}
/*****************************************************************************/
/* Command input lines to daemon mailbox to setup default parameters,
 * setting mode to detached, the username, and the logfile name.
 */
static int set_daemon_parameters ( int channel, char *username, 
	char *login_dir )
{
    int status;
    struct io_status_block iosb, iosb2;
    char line[256];

    status = SYS$QIOW ( 23, channel, IO$_WRITEVBLK, &iosb, 0, 0,
	"detach=1", 8, 0, 0, 0, 0 );
    if ( (status&1) == 1 ) status = iosb.status;
    if ( (status&1) == 0 ) return status;

    status = SYS$QIOW ( 23, channel, IO$_WRITEVBLK, &iosb, 0, 0,
	"command=exit", 12, 0, 0, 0, 0 );
    if ( (status&1) == 1 ) status = iosb.status;
    if ( (status&1) == 0 ) return status;

    sprintf ( line, "username=%s", username );
    status = SYS$QIOW ( 23, channel, IO$_WRITEVBLK, &iosb, 0, 0,
	line, strlen(line), 0, 0, 0, 0 );
    if ( (status&1) == 1 ) status = iosb.status;
    if ( (status&1) == 0 ) return status;

    sprintf ( line, "logfile=%smbxnet_server.log", login_dir );
    status = SYS$QIOW ( 23, channel, IO$_WRITEVBLK, &iosb, 0, 0,
	line, strlen(line), 0, 0, 0, 0 );
    if ( (status&1) == 1 ) status = iosb.status;
    if ( (status&1) == 0 ) return status;

    sprintf ( line, "command_out=%smbxnet_command.log", login_dir );
    status = SYS$QIOW ( 23, channel, IO$_WRITEVBLK, &iosb, 0, 0,
	line, strlen(line), 0, 0, 0, 0 );
    if ( (status&1) == 1 ) status = iosb.status;
    if ( (status&1) == 0 ) return status;

    sprintf ( line, "timeout=100" );
    status = SYS$QIOW ( 23, channel, IO$_WRITEVBLK, &iosb, 0, 0,
	line, strlen(line), 0, 0, 0, 0 );
    if ( (status&1) == 1 ) status = iosb.status;
    if ( (status&1) == 0 ) return status;

    return status;
}
/*****************************************************************************/
/*  Lookup username in sysuaf and set DCL symbol login_dir to the value
 * of that username's login directory.
 */
static int get_login_dir ( char *username, char *login_dir, int maxlen )
{
    int i, flags, devlen, dirlen, status, length, user_len;
    int delta, table;
    item_list item[5];
    char device[40], defdir[64], userkey[USERNAME_MAX+1];
    static $DESCRIPTOR (username_dx,"");
    static $DESCRIPTOR ( symbol, "login_dir" );
    static $DESCRIPTOR ( value, "" );

    user_len = strlen ( username );
    if ( user_len > USERNAME_MAX ) return 20;	/* invalid name */
    if ( user_len <= 0 ) return 20;			/* Null username */
    strcpy ( userkey, username);
    username_dx.dsc$w_length = user_len;
    username_dx.dsc$a_pointer = userkey;
    /*
     * Make item list for getuai.
     */
    item[0].code = UAI$_FLAGS; item[0].length = sizeof(flags);
    item[0].buffer = (char *) &flags; item[0].ret_len = (int *) 0;

    item[1].code = UAI$_DEFDEV; item[1].length = sizeof(device);
    item[1].buffer = device; item[1].ret_len = &devlen; 
    devlen = 0; device[0] = '\0';

    item[2].code = UAI$_DEFDIR; item[2].length = sizeof(defdir);
    item[2].buffer = defdir; item[2].ret_len = &dirlen; 
    dirlen = 0; defdir[0] = '\0';

    item[3].code = 0; item[3].length = 0;	/* terminate list */
    /*
     * Fetch information about user.  Block other activity and set privilege.
     */
    status = SYS$GETUAI ( 0, 0, &username_dx, item, 0, 0, 0 );
    /*
     * Return if error or account not enabled.
     */
    if ( (status&1) == 0 ) return status;		/* GETUAI error */
    if ( (flags&UAI$M_DISACNT) != 0 ) return 20;	/* Disusered account */
    /*
     * Generate login directory and set login_dir symbol.
     */
    devlen = device[0];		/* counted ascii */
    if ( device[devlen] != ':' ) {
	devlen = devlen + 1;	/* add colon */
	device[devlen] = ':';
    }
    dirlen = defdir[0];
    if ( (devlen + dirlen + 1) > maxlen ) return 20;
    strncpy ( &login_dir[0], &device[1], devlen );
    strncpy ( &login_dir[devlen], &defdir[1], dirlen );
    login_dir[devlen+dirlen] = '\0';
    value.dsc$a_pointer = login_dir;
    value.dsc$w_length = devlen+dirlen;

    table = 0;
    status = LIB$SET_SYMBOL ( &symbol, &value ); 

    return status;
}
/*****************************************************************************/
int main ( int argc, char **argv )
{
    int status, channel;
    char *mailbox;
    static char open_command[200], login_dir[220];
    static $DESCRIPTOR ( command_dx, open_command );
    if ( argc < 2 ) {
	fprintf(stderr, 
	   "Missing argument, usage: mbxnet_daemon_create username [outfile]\n");
	return 44;
    }

    status = get_login_dir ( argv[1], login_dir, sizeof(login_dir) );
    if ( (status&1) == 0 ) return status;	/* abort if error */

    status = spawn_daemon ( argc < 3 ? "mbxnet_daemon.log" : argv[2],
	DAEMON_TIMEOUT, &mailbox, &channel );
    if ( (status&1) == 0 ) return status;	/* abort if error */

    status = set_daemon_parameters ( channel, argv[1], login_dir );
    if ( (status&1) == 0 ) return status;	/* abort if error */
    /*
     * Exit program by chaining to DCL open command.
     */
    sprintf ( open_command, "open/read/write mbxnet_param %s", mailbox );
    status = LIB$DO_COMMAND ( &command_dx );
    return status;
}
