Source

dr1000-hackx / sysd / src / main.c

/*
 * File Name: main.c
 */

/*
 * This file is part of sysd.
 *
 * sysd is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * sysd is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Copyright (C) 2008 iRex Technologies B.V.
 * All rights reserved.
 */

//----------------------------------------------------------------------------
// Include Files
//----------------------------------------------------------------------------

#include "config.h"

// system include files, between < >
#include <glib.h>
#include <gtk/gtk.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#ifndef _XOPEN_SOURCE 
#define _XOPEN_SOURCE // glibc2 needs this
#endif 
#include <time.h>

// ereader include files, between < >
#include <liberipc/eripc.h>
#include <liberutils/display_utils.h>

// local include files, between " "
#include "log.h"
#include "busy.h"
#include "conf.h"
#include "display.h"
#include "connections.h"
#include "hal.h"
#include "ipc.h"
#include "process.h"
#include "system.h"
#include "tasks.h"
#include "xwindow.h"

#include "logger.h"

//----------------------------------------------------------------------------
// Type Declarations
//----------------------------------------------------------------------------


//----------------------------------------------------------------------------
// Global Constants
//----------------------------------------------------------------------------


//----------------------------------------------------------------------------
// Static Variables
//----------------------------------------------------------------------------

static gboolean do_detach = FALSE;


//============================================================================
// Local Function Definitions
//============================================================================


//============================================================================
// Functions Implementation
//============================================================================

// print usage text and quit
static void usage(const char *argv_0)
{
    static const char *usage_text = 
                        "\n"
                        "usage: %s [options]\n"
                        "\n"
                        "options:\n"
                        "    --help\n"
                        "        Print help text and quit\n"
                        "    --detach\n"
                        "        Detach from console to run as daemon\n";
    printf(usage_text, argv_0);

    exit(1);
}

// get command-line options
static void parse_arguments(int argc, char **argv)
{
    int  i;

    // parse contentLister options
    for (i = 1 ; i < argc ; i++)
    {
        if (strcmp(argv[i], "--help") == 0)
        {
            usage(argv[0]);
        }
        if (strcmp(argv[i], "--detach") == 0)
        {
            do_detach = TRUE;
        }
    }
}


static void on_sigterm(int signo)
{
    WARNPRINTF("    -- entry " PACKAGE_NAME ", my pid [%d]", getpid());

    // stop main process, prepare to quit application
    gtk_main_quit();

    WARNPRINTF("    -- leave " PACKAGE_NAME);
}


static long int read_timestamp (const char* path)
{
    FILE* thefile = NULL; 
    size_t result;
    char tbuffer[20];
    char buf[20];
    const char* timeformat = "%m%d%H%M%Y" ; // current timestamp format
    struct tm mytime;

    long int ts = -1 ; // 0 = epoch 

    thefile = fopen(path, "r");
    if (NULL == thefile)
    {
        // only allow positive time, 0 = 1970-01-01 00:00
        goto error_fopen; 
    }
    bzero(tbuffer, sizeof(tbuffer));
    bzero(buf, sizeof(buf));
    bzero(&mytime, sizeof(struct tm));
    result = fread((void*) tbuffer,  sizeof(char), 20, thefile);
    if ( result > 0 && result <= sizeof(tbuffer) )
    {
        // transform to epoch time
        LOGPRINTF("Read timestamp from file: [%s]", tbuffer);
        
        // timestamp string to time struct
        strptime(tbuffer, timeformat, &mytime);
        
        // time is seconds since epoch
        result = strftime(buf, sizeof(buf), "%s", &mytime);  
        
        if ( result > 0 && result < sizeof(buf) )
        {
            LOGPRINTF("Old time stamp: [%s], New timestamp (secs since epoch) [%s]", tbuffer, buf);
        }

        // buf now holds a time stamp, compare to
        // translate to int
        ts = strtol(buf, NULL, 10);
        goto exit;
    }
    else 
    {
        goto error;
    }
        
error: 
    ts = -1;
exit:
    if (NULL != thefile)
    {
        fclose(thefile);
    }
    return ts;

error_fopen:
    return -1;
}


static gboolean check_set_datetime(void)
{
    gboolean need_set = FALSE; 
    struct timeval tod; 
    long int ts = 0;

    ts = read_timestamp("/etc/timestamp");  

    // now get the current time
    gettimeofday(&tod, NULL); 
    LOGPRINTF("timeofday = %ld", tod.tv_sec);

    if (ts < tod.tv_sec) 
    {
        LOGPRINTF("timestamp < timeofday"); 
        // do nothing
    }
    else  
    {
        need_set = TRUE;
    }

    return need_set;
}


static void on_phase_3_complete(void)
{
    LOGPRINTF("entry");

    // all apps started, stop busy and allow display updates again
    busy_remove_foreground(0);
    display_return_control();
    
    sys_starting_finished();
}


static void start_phase_3(void)
{
    LOGPRINTF("entry");

    // detect hardware, mount volumes
    hal_add_devices();
     
    process_add("/usr/bin/ctb", NULL, G_CALLBACK(on_phase_3_complete), NULL, PS_WAIT_STARTED | PS_RESPAWN);
}


static void on_phase_2_complete(void)
{
    LOGPRINTF("entry");

    busy_add_foreground(0, BUSY_DIALOG_NONE, NULL);
    display_gain_control();
    sys_request_popup("localunblock");
    
    sys_set_device_state(STATE_DEVICE_STARTED);
    
    start_phase_3();
}


static void start_phase_2(void)
{
    LOGPRINTF("entry");

    // complex solution to make sure the menu is blocked before the 
    // first app starts and unblocked after the last app exists
    gboolean run_firstboot = FALSE;
    gboolean run_gtktscal  = FALSE;
    gboolean run_datetime  = FALSE;
    
    if (conf_get_first_boot())
    {
        if (conf_is_multi_language())
        {
            run_firstboot = TRUE;
        }
        
        if (sys_has_stylus() && !sys_is_emulator())
        {
            run_gtktscal = TRUE;
        }
    }

    if (check_set_datetime())
    {
        run_datetime = TRUE;
    }

    if (run_firstboot || run_gtktscal || run_datetime)
    {
        sys_request_popup("localblock");
        busy_remove_foreground(0);
        display_return_control();

        if (run_firstboot)
        {
            process_add("/usr/bin/settings --firstboot", NULL, NULL, run_gtktscal || run_datetime ? NULL : G_CALLBACK(on_phase_2_complete), PS_WAIT_EXIT);
        }
        
        if (run_gtktscal)
        {
            process_add("/usr/bin/gtktscal -firstboot", NULL, NULL, run_datetime ? NULL : G_CALLBACK(on_phase_2_complete), PS_WAIT_EXIT);
        }
        
        if (run_datetime)
        {
            // set busy to make sure the system does not suspend
            busy_add_background(0);
            sys_spawn_sync("date `cat /etc/timestamp` && /sbin/hwclock --systohc --utc");
            busy_remove_background(0);
            
            // show time/date settings dialog
            process_add("/usr/bin/settings --fdatetime", NULL, NULL, G_CALLBACK(on_phase_2_complete), PS_WAIT_EXIT);
        }
    }
    else
    {
        start_phase_3();
    }
}


static void on_phase_1_complete(void)
{
    LOGPRINTF("entry");

#if MACHINE_IS_DR1000S || MACHINE_IS_DR1000SW    
    conf_disabled_sensor_lock();
#endif
    conn_update_statusbar();
    
    if (!sys_is_emulator())
    {
        ipc_send_volume_mounted(MOUNTPOINT_CARD);
    }

    start_phase_2();
}


int main(int argc, char *argv[])
{
    struct sigaction on_term;
    
    loggerLogString("PB");
    // parse command-line arguments
    parse_arguments(argc, argv);

    // catch the SIGTERM signal
    memset(&on_term, 0x00, sizeof(on_term));
    on_term.sa_handler = on_sigterm;
    sigaction(SIGTERM, &on_term, NULL);

    if (do_detach)
    {
        if (daemon(0, 0) !=0)
        {
            LOGPRINTF("Daemonize error: %s", strerror(errno));
            exit (1);
        }
    }

    // init gdk (for windowing)
    g_type_init();
    gtk_init(&argc, &argv);
    
    // prepare IPC, system (micro) and display
    ipc_set_services();
    window_set_services(); 
    sys_set_services();     /* relies on ipc */
    display_set_services(); /* relies on system/micro and window*/
    busy_add_foreground(0, BUSY_DIALOG_NONE, NULL);
    
    // prepare GConf
    conf_set_services();

    // prepare HAL and mountpoints
    hal_set_services();
    display_gain_control();

    // spawn applications 
    process_add("/usr/bin/popupmenu", NULL, NULL, NULL, PS_WAIT_STARTED | PS_RESPAWN);
    process_add("/usr/bin/uds",       NULL, G_CALLBACK(on_phase_1_complete), NULL, PS_WAIT_STARTED | PS_RESPAWN);
    
    // run the main loop
    LOGPRINTF("before gtk_main");
    gtk_main();
    LOGPRINTF("after gtk_main");

    return 0;
}