/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
** File: anonfm.c
** Description: Test anonymous file map
**
** Synopsis: anonfm [options] [dirName]
**
** Options:
** -d   enable debug mode
** -h   display a help message
** -s <n>  size of the anonymous memory map, in KBytes. default: 100KBytes.
** -C 1 Operate this process as ClientOne()
** -C 2 Operate this process as ClientTwo()
**
** anonfn.c contains two tests, corresponding to the two protocols for
** passing an anonymous file map to a child process.
**
** ServerOne()/ClientOne() tests the passing of "raw" file map; it uses
** PR_CreateProcess() [for portability of the test case] to create the
** child process, but does not use the PRProcessAttr structure for
** passing the file map data.
**
** ServerTwo()/ClientTwo() tests the passing of the file map using the
** PRProcessAttr structure.
**
*/
#include <plgetopt.h>
#include <nspr.h>
#include <private/primpl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
** Test harness infrastructure
*/
PRLogModuleInfo *lm;
PRLogModuleLevel msgLevel = PR_LOG_NONE;
PRUint32  failed_already = 0;

PRIntn  debug = 0;
PRIntn  client = 0; /* invoke client, style */
char    dirName[512] = "."; /* directory name to contain anon mapped file */
PRSize  fmSize = (100 * 1024 );
PRUint32 fmMode = 0600;
PRFileMapProtect fmProt = PR_PROT_READWRITE;
const char *fmEnvName = "nsprFileMapEnvVariable";

/*
** Emit help text for this test
*/
static void Help( void )
{
    printf("anonfm [options] [dirName]\n");
    printf("-d -- enable debug mode\n");
    printf("dirName is alternate directory name. Default: . (current directory)\n");
    exit(1);
} /* end Help() */


/*
** ClientOne() --
*/
static void ClientOne( void )
{
    PRFileMap   *fm;
    char        *fmString;
    char        *addr;
    PRStatus    rc;

    PR_LOG(lm, msgLevel,
           ("ClientOne() starting"));

    fmString = PR_GetEnv( fmEnvName );
    if ( NULL == fmString ) {
        failed_already = 1;
        PR_LOG(lm, msgLevel,
               ("ClientOne(): PR_Getenv() failed"));
        return;
    }
    PR_LOG(lm, msgLevel,
           ("ClientOne(): PR_Getenv(): found: %s", fmString));

    fm = PR_ImportFileMapFromString( fmString );
    if ( NULL == fm ) {
        failed_already = 1;
        PR_LOG(lm, msgLevel,
               ("ClientOne(): PR_ImportFileMapFromString() failed"));
        return;
    }
    PR_LOG(lm, msgLevel,
           ("ClientOne(): PR_ImportFileMapFromString(): fm: %p", fm ));

    addr = PR_MemMap( fm, LL_ZERO, fmSize );
    if ( NULL == addr ) {
        failed_already = 1;
        PR_LOG(lm, msgLevel,
               ("ClientOne(): PR_MemMap() failed, OSError: %d", PR_GetOSError() ));
        return;
    }
    PR_LOG(lm, msgLevel,
           ("ClientOne(): PR_MemMap(): addr: %p", addr ));

    /* write to memory map to release server */
    *addr = 1;

    rc = PR_MemUnmap( addr, fmSize );
    PR_ASSERT( rc == PR_SUCCESS );
    PR_LOG(lm, msgLevel,
           ("ClientOne(): PR_MemUnap(): success" ));

    rc = PR_CloseFileMap( fm );
    if ( PR_FAILURE == rc ) {
        failed_already = 1;
        PR_LOG(lm, msgLevel,
               ("ClientOne(): PR_MemUnap() failed, OSError: %d", PR_GetOSError() ));
        return;
    }
    PR_LOG(lm, msgLevel,
           ("ClientOne(): PR_CloseFileMap(): success" ));

    return;
} /* end ClientOne() */

/*
** ClientTwo() --
*/
static void ClientTwo( void )
{
    failed_already = 1;
} /* end ClientTwo() */

/*
** ServerOne() --
*/
static void ServerOne( void )
{
    PRFileMap   *fm;
    PRStatus    rc;
    PRIntn      i;
    char        *addr;
    char        fmString[256];
    char        envBuf[256];
    char        *child_argv[8];
    PRProcess   *proc;
    PRInt32     exit_status;

    PR_LOG(lm, msgLevel,
           ("ServerOne() starting"));

    fm = PR_OpenAnonFileMap( dirName, fmSize, fmProt );
    if ( NULL == fm )      {
        failed_already = 1;
        PR_LOG(lm, msgLevel,
               ("PR_OpenAnonFileMap() failed"));
        return;
    }
    PR_LOG(lm, msgLevel,
           ("ServerOne(): FileMap: %p", fm ));

    rc = PR_ExportFileMapAsString( fm, sizeof(fmString), fmString );
    if ( PR_FAILURE == rc )  {
        failed_already = 1;
        PR_LOG(lm, msgLevel,
               ("PR_ExportFileMap() failed"));
        return;
    }

    /*
    ** put the string into the environment
    */
    PR_snprintf( envBuf, sizeof(envBuf), "%s=%s", fmEnvName, fmString);
    putenv( envBuf );

    addr = PR_MemMap( fm, LL_ZERO, fmSize );
    if ( NULL == addr ) {
        failed_already = 1;
        PR_LOG(lm, msgLevel,
               ("PR_MemMap() failed"));
        return;
    }

    /* set initial value for client */
    for (i = 0; i < (PRIntn)fmSize ; i++ ) {
        *(addr+i) = 0x00;
    }

    PR_LOG(lm, msgLevel,
           ("ServerOne(): PR_MemMap(): addr: %p", addr ));

    /*
    ** set arguments for child process
    */
    child_argv[0] = "anonfm";
    child_argv[1] = "-C";
    child_argv[2] = "1";
    child_argv[3] = NULL;

    proc = PR_CreateProcess(child_argv[0], child_argv, NULL, NULL);
    PR_ASSERT( proc );
    PR_LOG(lm, msgLevel,
           ("ServerOne(): PR_CreateProcess(): proc: %x", proc ));

    /*
    ** ClientOne() will set the memory to 1
    */
    PR_LOG(lm, msgLevel,
           ("ServerOne(): waiting on Client, *addr: %x", *addr ));
    while( *addr == 0x00 ) {
        if ( debug ) {
            fprintf(stderr, ".");
        }
        PR_Sleep(PR_MillisecondsToInterval(300));
    }
    if ( debug ) {
        fprintf(stderr, "\n");
    }
    PR_LOG(lm, msgLevel,
           ("ServerOne(): Client responded" ));

    rc = PR_WaitProcess( proc, &exit_status );
    PR_ASSERT( PR_FAILURE != rc );

    rc = PR_MemUnmap( addr, fmSize);
    if ( PR_FAILURE == rc ) {
        failed_already = 1;
        PR_LOG(lm, msgLevel,
               ("PR_MemUnmap() failed"));
        return;
    }
    PR_LOG(lm, msgLevel,
           ("ServerOne(): PR_MemUnmap(): success" ));

    rc = PR_CloseFileMap(fm);
    if ( PR_FAILURE == rc ) {
        failed_already = 1;
        PR_LOG(lm, msgLevel,
               ("PR_CloseFileMap() failed"));
        return;
    }
    PR_LOG(lm, msgLevel,
           ("ServerOne(): PR_CloseFileMap() success" ));

    return;
} /* end ServerOne() */

/*
** ServerTwo() --
*/
static void ServerTwo( void )
{
    PR_LOG(lm, msgLevel,
           ("ServerTwo(): Not implemented yet" ));
} /* end ServerTwo() */


int main(int argc, char **argv)
{
    {
        /*
        ** Get command line options
        */
        PLOptStatus os;
        PLOptState *opt = PL_CreateOptState(argc, argv, "hdC:");

        while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
        {
            if (PL_OPT_BAD == os) {
                continue;
            }
            switch (opt->option)
            {
                case 'C':  /* Client style */
                    client = atol(opt->value);
                    break;
                case 's':  /* file size */
                    fmSize = atol( opt->value ) * 1024;
                    break;
                case 'd':  /* debug */
                    debug = 1;
                    msgLevel = PR_LOG_DEBUG;
                    break;
                case 'h':  /* help message */
                    Help();
                    break;
                default:
                    strcpy(dirName, opt->value);
                    break;
            }
        }
        PL_DestroyOptState(opt);
    }

    lm = PR_NewLogModule("Test");       /* Initialize logging */

    if ( client == 1 ) {
        ClientOne();
    } else if ( client == 2 )  {
        ClientTwo();
    } else {
        ServerOne();
        if ( failed_already ) {
            goto Finished;
        }
        ServerTwo();
    }

Finished:
    if ( debug ) {
        printf("%s\n", (failed_already)? "FAIL" : "PASS");
    }
    return( (failed_already == PR_TRUE )? 1 : 0 );
}  /* main() */
/* end anonfm.c */