/* -*- 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 */