/*
 * Copyright (c) 2001-2003 Shiman Associates Inc. All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _POSIX_C_SOURCE
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <errno.h>
#include <syslog.h>
#endif
#include "mas/mas.h"

#define WAIT_FOR_SEC  10

void
send_sch_priority( void )
{
    struct mas_event    event;
    static struct mas_package  package;
    static int initialized = 0;

    if ( ! initialized )
    {
        masc_setup_event( &event );
        event.action_name = "mas_sch_priority";
        event.device_instance = MAS_SCH_INSTANCE;
        masc_setup_package( &package, NULL, 0, 0 );
        masc_pack_event( &package, &event );
        initialized = TRUE;
        /* do not strike the package; we'll re-use it on subsequent calls */
    }
    
    mas_send_package_to_local( &package );
}

void
receive_priority_response( int32* priority, int32* pid )
{
    struct mas_package package;
    
    /* recv package calls setup for us */
    mas_recv_package_from_local( &package );
    masc_pullk_int32( &package, "pri", priority );
    masc_pullk_int32( &package, "pid", pid );
    masc_strike_package( &package );
}

void
update_priority( int32* our_priority, int32 *priority, int32* maspid )
{
    int32 err;

    receive_priority_response( priority, maspid );
    if ( *priority == 0 )
    {
        syslog( LOG_INFO, "MAS is running without real-time scheduling.");
        syslog( LOG_INFO, "I'm not needed.  Exiting.");
        closelog();
        exit(0);
    }

    if ( *priority > *our_priority )
    {
        syslog(LOG_INFO, "mas[%d] is running with real-time priority %d.", *maspid, *priority );
        *our_priority = *priority + 1;
        syslog( LOG_INFO, "Setting my priority to %d.", *our_priority);
        err = masc_set_real_time_priority( *our_priority );
        if ( err < 0 )
        {
            syslog( LOG_ERR, "masc_set_real_time_priority(%d) failed.", *our_priority);
            syslog( LOG_ERR, "Exiting." );
            closelog();
            exit(1);
        }

        syslog( LOG_INFO, "Polling MAS every %d seconds.", WAIT_FOR_SEC );
        syslog( LOG_INFO, "I will kill MAS if it fails to respond within each polling interval." );
    }
}


int main()
{
    int32 priority;
    int   our_priority = 0;
    int32 err;
    int32 maspid = -1;
    int giveup = WAIT_FOR_SEC;
    
    switch ( fork() )
    {
    case -1:
        /* I can't fork.  I exit. */
        perror ("MAS Watchdog: Could not fork()");
        exit(3);
        break;
    case 0:
        /* I am a child */
         if ( setsid() < 0)
         {
             /* But I cannot take control.  I exit */
             exit(4);
         }
         break;
    default:
        /* I am the parent - I exit. */
        exit(0);
    }
    
    openlog( "maswatchdog", LOG_PID, LOG_DAEMON );
    
    syslog( LOG_INFO, "Starting.  Waiting one second for MAS to start.");
    sleep(1);
    syslog( LOG_INFO, "Determining MAS's priority.");
    while ( ( err = mas_init() ) < 0 && giveup )
    {
        syslog( LOG_INFO, "Could not contact MAS.  Trying again in one second." );
        giveup--;
        sleep(1);
    }
    
    if ( err < 0 && giveup == 0 ) 
    {
        syslog( LOG_ERR, "I couldn't establish contact with MAS." );
        syslog( LOG_ERR, "I'm exiting.  MAS is unusable.");
        exit(1);
    }
    
    send_sch_priority();
    update_priority( &our_priority, &priority, &maspid );
    
    while (1)
    {
        
        send_sch_priority();

        sleep(WAIT_FOR_SEC);

        if ( mas_test_recv_data_waiting_from_local() != 1 )
        {
            syslog( LOG_ERR, "MAS is not responding.");
            syslog( LOG_ERR, "Shutting down MAS.");
            if ( maspid > 0 )
            {
                if ( kill( maspid, SIGSEGV ) < 0 )
                {
                    syslog( LOG_ERR, "kill(%d) failed.", maspid );
                    syslog( LOG_ERR, "Exiting.");
                    closelog();
                    exit(1);
                }
            }
            else
            {
                syslog( LOG_ERR, "Oops!  I don't have the process ID of MAS.");
            }
        }
        else
        {
            update_priority( &our_priority, &priority, &maspid );
        }
    }
    
    closelog();
    return 0;
}
