/*
 * Server main function
 *
 * Copyright (C) 1998 Alexandre Julliard
 * Copyright (C) 2002,2004 TransGaming Technologies
 */

#include "config.h"

#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#include <unistd.h>
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif

#include "object.h"
#include "thread.h"
#include "request.h"
#include "server_memory.h"

#include "cdrom_eject.h"

/* preloader */
#include "wine/main.h"

/* command-line options */
extern int debug_level;
extern int persistent_server;
extern int wineserver_is_daemon;
static int shared_memory_server = 1; /* PH: FIXME: Temp */

/* this symbol is exported.
 * The preloader will locate it and set it to point to
 * a NULL terminated list of mmap reserved areas.
 */
const struct wine_preload_info *wine_main_preload_info = NULL;

/* parse-line args */
/* FIXME: should probably use getopt, and add a (more complete?) help option */

static void usage(const char *exeName)
{
    fprintf(stderr, "\nusage: %s [options]\n\n", exeName);
    fprintf(stderr, "options:\n");
    fprintf(stderr, "   -d<n>  set debug level to <n>\n");
    fprintf(stderr, "   -p     make server persistent\n");
    fprintf(stderr, "   -h     display this help message\n");
    fprintf(stderr, "   -s     server will use shared memory where possible rather than pipe communication\n");
    fprintf(stderr, "   -f     server will will not run as daemon\n");
    fprintf(stderr, "\n");
}

static void parse_args( int argc, char *argv[] )
{
    int i;
    for (i = 1; i < argc; i++)
    {
        if (argv[i][0] == '-')
        {
            switch(argv[i][1])
            {
            case 'd':
                if (isdigit(argv[i][2])) debug_level = atoi( argv[i] + 2 );
                else debug_level++;
                break;
            case 'h':
                usage( argv[0] );
                exit(0);
                break;
            case 'p':
                persistent_server = 1;
                break;
            case 's':
                shared_memory_server = 1;
                break;
            case 'f':
                wineserver_is_daemon = 0;
                break;
            default:
                fprintf( stderr, "Unknown option '%s'\n", argv[i] );
                usage( argv[0] );
                exit(1);
            }
        }
        else
        {
            fprintf( stderr, "Unknown argument '%s'. Your version of wine may be too old.\n", argv[i] );
            usage(argv[0]);
            exit(1);
        }
    }
}

static void sigterm_handler()
{
    exit(1);  /* make sure atexit functions get called */
}

static void sig_unexpected_handler()
{
    fprintf(stderr, "ERROR: wineserver exiting unexpectantly!\n");
    raise(SIGSTOP);
    exit(1);
}

/* initialize signal handling */
static void signal_init(void)
{
/* PH: FIXME: Do we need to worry about wineserver expecting certain signal handling behaviour
 *            when running in client context? */
    signal( SIGPIPE, SIG_IGN );
    signal( SIGHUP, sigterm_handler );
    signal( SIGINT, sigterm_handler );
    signal( SIGQUIT, sigterm_handler );
    signal( SIGTERM, sigterm_handler );

    signal( SIGABRT, sig_unexpected_handler );
    signal( SIGILL, sig_unexpected_handler );
    signal( SIGBUS, sig_unexpected_handler );
    signal( SIGFPE, sig_unexpected_handler );
    signal( SIGSEGV, sig_unexpected_handler );
}


static void memory_alloc(void)
{
    /* if the preloader has set the info stuff,
     * we need to pass it to libwineserver */
    if (wine_main_preload_info)
    {
      int i;
      for (i = 0; !(wine_main_preload_info[i].info & PRELOAD_INFO_NULL); i++)
      {
          if (wine_main_preload_info[i].info & PRELOAD_INFO_SERVERADDR &&
                  wine_main_preload_info[i].size)
          {
              set_shm_reserved(wine_main_preload_info[i].addr,
                               wine_main_preload_info[i].size);
              break;
          }
      }
    }

    if( shared_memory_server )
        server_memory_block = create_server_shared_memory_area( get_config_dir(), get_registry_size() );

    /* Fall back to regular memory if we can't allocate shared memory */
    if( server_memory_block == NULL )
    {
        shared_memory_server = 0;
        server_memory_block = regular_memory_alloc( get_config_dir(), get_registry_size() );
    }

    if( !server_memory_block )
    {
        fatal_error( "Unable to create server memory area\n" );
    }
                                                                                                                                                                              
    init_memory_block( shared_memory_server );
}

#if defined(linux)
extern int wait4__WALL;

/* __WALL is defined in 2.2.16 and later kernels. Test to see if it's present in this one */
void set_wait4__WALL_value(void)
{
    pid_t child;
    if (!(child = fork()))
    {
        /* child but don't call atexit handlers */;
        _exit(0);
    }
    else if (child == -1)
    {
        fprintf(stderr, "Server: (WARN): fork failed, presuming wait4 has __WALL support\n");
        wait4__WALL = __WALL;
    }
    else
    {
        if (wait4(child, NULL, __WALL, NULL) < 0)
        {
            if (debug_level) fprintf( stderr, "Server: no support for __WALL in wait4\n");
            wait4__WALL = 0;
        }
        else
        {
            if (debug_level) fprintf( stderr, "Server: support for __WALL in wait4 found\n");
            wait4__WALL = __WALL;
        }
    }
}
#else
static inline set_wait4__WALL_value(void) {}
#endif /* defined(linux) */

int main( int argc, char *argv[] )
{
// PH: FIXME: REMOVE ME:
is_wineserver = 1;

    parse_args( argc, argv );
    memory_alloc();

    /* Setup some kernel version specific flags */
    set_wait4__WALL_value();

    signal_init();
    sock_init();
    open_master_socket();
    create_distributed_command();
    setvbuf( stderr, NULL, _IOLBF, 0 );

    if (debug_level) fprintf( stderr, "Server: starting (pid=%ld)\n", (long) getpid() );
    init_registry();
    cdrom_eject_init();

    select_loop();
    close_registry();
    cdrom_eject_stop();

    if (debug_level) fprintf( stderr, "Server: exiting (pid=%ld)\n", (long) getpid() );

#ifdef DEBUG_OBJECTS
    close_atom_table();
    dump_objects();  /* dump any remaining objects */
#endif

    return 0;
}
