0

I have written the below program to notify me by email when my Linux system has been rebooted from a power failure.

The way I do this is enableing power-fail recovery in the BIOS, and then register in a SQlite database every time the computer has been started up or shut down.

When the last shut-down time is NULL in the SQlite database, I have discovered a powerfailure, and send the email notification.

Now, I've done most of it, but I still need to add this program to the respective startup and shutdown scripts.

Where do I need to put it? Special attention needs to be given to the fact that it will send powerfail notification via smtp server to my android phone, which means the startup script should only run after the network (perferably wireless, too) has been initialized.

This is a cross-post of my question here.

#ifdef __cplusplus
    #include <cstdio>
    #include <cstdlib>
    #include <ctime>
    //#include <cstdint>
    #include <cstdarg>
    #include <cstring>
    #include <cctype>

    #include <sqlite3.h>
#else
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    //#include <stdint.h>
    #include <stdarg.h>
    #include <string.h>
    #include <ctype.h>

    #include <sqlite3.h>
#endif


#include <unistd.h> // for gethostname


// Requires:
// apt-get install sendemail


// Compile with:
// gcc -Wall -lsqlite3 powerfail.c -o hel
// g++ -Wall -lsqlite3 powerfail.c -o hel

// With stdint:
// g++ -Wall -Wno-write-strings -std=c++0x -lsqlite3 powerfail.c -o hel


// getconf GNU_LIBPTHREAD_VERSION
// getconf PAGESIZE

// Doc:
// http://stackoverflow.com/questions/1409453/how-to-store-and-get-datetime-value-in-sqlite
// http://docs.python.org/library/sqlite3.html
// http://www.devshed.com/c/a/Python/Using-SQLite-in-Python/


int bDebug = 0;
int bStartupRequested = 1;
int bShutdownRequested = 0;
int bSimulatePowerFailure = 0;
int bList = 0;
int bClear = 0;
int bTestMail = 0;


int Debug_Printf(const char* szFormat, ...)
{
    if(!bDebug)
        return 0;

    va_list arglist;
    int iReturnValue;

    int iSize = 100;
    char *p;

    if ((p = (char*) malloc (iSize)) == NULL)
    {
        printf("Error in Debug_Printf: Malloc failed.\n");
        return (int) NULL;
    }


    va_start(arglist, szFormat);
        iReturnValue = vsnprintf (p, iSize, szFormat, arglist);
    va_end(arglist);

    printf("%s", p);

    free(p);
    return iReturnValue;
}


char* ToLower(char* szInputString)
{
    if(szInputString != NULL)
    {
        int iStringLength = strlen(szInputString);
        char* szTempString = (char*) malloc((iStringLength+1) * sizeof(char));

        int i ;
        for(i =0; i < iStringLength; ++i)
            szTempString[i] = (char) tolower(szInputString[i]);

        return szTempString;
    }

    return NULL;
}


/* Callback called when the query is exceuted */
/*
static int callback(void *NotUsed, int argc, char **argv, char **azColName)
{
    printf ("\n ******** Inside Callback\n");
    int i;
    int rowpr=argc-1;
    NotUsed=0;
    printf("\n %s ",__FUNCTION__);

    for(i=0; i<rowpr; i++)
        printf("%s ",azColName[i]);

    printf("%s\n",azColName[rowpr]);



    for(i=0; i<rowpr; i++){
        printf("%s ",  argv[i] ? argv[i] : "NULL");

    }
    printf("%s\n",  argv[rowpr] ? argv[rowpr] : "NULL");

    return 0;
}
*/


void ExecuteNonQuery(char* szSQLstatement, sqlite3 *handle)
{
    Debug_Printf("SQL: %s\n", szSQLstatement);

    char* szErrMsg = NULL;
    // Execute the query
    int iExecutionResult = sqlite3_exec(handle, szSQLstatement, NULL, handle, &szErrMsg);
    if(iExecutionResult != SQLITE_OK)
    {
        if(szErrMsg != NULL)
        {
            Debug_Printf("\n\n******************************************************\n");
            Debug_Printf("* Error while doing ExecuteNonQuery: \n*\t%s\n", szErrMsg);
            Debug_Printf("******************************************************\n\n");
            sqlite3_free(szErrMsg);
        }

    }
}


void ClearTable(const char* szTableName, sqlite3 *handle)
{
    char* szDeleteStatement = (char*) malloc(1000 * sizeof(char));
    if(szDeleteStatement != NULL)
    {
        sprintf(szDeleteStatement, "DELETE FROM %s", szTableName);
        ExecuteNonQuery(szDeleteStatement, handle);
        free(szDeleteStatement);
    }
    else
        Debug_Printf("Error: Malloc failed. Insufficient RAM for DELETE statement allocation.\n");
}


int SelectFromTable(const char* szTableName, sqlite3 *handle)
{
    //char szSelectStatement[] = "SELECT * FROM T_StartStopLog ";
    char* szSelectStatement = (char*) malloc(1000 * sizeof(char));
    if(szSelectStatement != NULL)
    {
        sprintf(szSelectStatement, "SELECT * FROM %s", szTableName);

        sqlite3_stmt *stmt;
        // select query from the table
        int iRetVal = sqlite3_prepare_v2(handle, szSelectStatement, -1, &stmt, 0);
        if(iRetVal)
        {
            Debug_Printf("Selecting data from DB Failed\n");
            return -1;
        }

        // Read the number of rows fetched
        int cols = sqlite3_column_count(stmt);
        int col = 0;
        while(1)
        {
            // fetch a row's status
            iRetVal = sqlite3_step(stmt);

            if(iRetVal == SQLITE_ROW)
            {
                // SQLITE_ROW means fetched a row

                // sqlite3_column_text returns a const void* , typecast it to const char*
                for(col=0 ; col < cols; col++)
                {
                    const char *val = (const char*)sqlite3_column_text(stmt,col);
                    printf("%s = %s\t",sqlite3_column_name(stmt,col),val);
                }
                printf("\n");
            }
            else if(iRetVal == SQLITE_DONE)
            {
                // All rows finished
                Debug_Printf("All rows fetched\n");
                break;
            }
            else
            {
                // Some error encountered
                Debug_Printf("Some error encountered\n");
                return -1;
            }
        }


        free(szSelectStatement);
    }
    else
    {
        Debug_Printf("Error: Malloc failed. Insufficient RAM for SELECT statement allocation.\n");
        return -1;
    }

    return 0;
}


int GetScalarInt(const char* szSelectStatement, sqlite3 *handle)
{
    //char szSelectStatement[] = "SELECT * FROM T_StartStopLog ";

    int iReturnValue =0 ;

    if(szSelectStatement != NULL)
    {
        sqlite3_stmt *stmt;
        // select query from the table
        int iRetVal = sqlite3_prepare_v2(handle, szSelectStatement, -1, &stmt, 0);
        if(iRetVal)
        {
            Debug_Printf("Selecting data from DB Failed\n");
            return -1;
        }

        // Read the number of rows fetched
        int cols = sqlite3_column_count(stmt);
        int col = 0;



        while(1)
        {
            // fetch a row's status
            iRetVal = sqlite3_step(stmt);

            if(iRetVal == SQLITE_ROW)
            {
                // SQLITE_ROW means fetched a row

                // sqlite3_column_text returns a const void* , typecast it to const char*
                for(col=0 ; col < cols; col++)
                {
                    const char *val = (const char*)sqlite3_column_text(stmt,col);
                    Debug_Printf("%s = %s\n",sqlite3_column_name(stmt,col),val);

                    if(val != NULL)
                    {
                        iReturnValue= atoi(val);
                    }
                    else
                        iReturnValue = 0;
                }
                Debug_Printf("\n");
            }
            else if(iRetVal == SQLITE_DONE)
            {
                // All rows finished
                Debug_Printf("All rows fetched\n");
                break;
            }
            else
            {
                // Some error encountered
                Debug_Printf("Some error encountered\n");
                return -1;
            }
        }


        //free(szSelectStatement);
    }
    else
    {
        Debug_Printf("Error: No valid query-parameter passed to function GetScalarInt.\n");
        return -1;
    }

    return iReturnValue;
}




// http://www.cplusplus.com/reference/clibrary/ctime/tm/    
int GetSequentialDate(int iDay, int iMonth, int iYear, int iHour, int iMinute, int iSecond)
{
    struct tm a_tm_struct ;
    a_tm_struct.tm_year = iYear - 1900;
    a_tm_struct.tm_mon = iMonth - 1;
    a_tm_struct.tm_mday = iDay;
    a_tm_struct.tm_hour = iHour;
    a_tm_struct.tm_min = iMinute;
    a_tm_struct.tm_sec = iSecond;


    //time_t mktime( struct tm * ptm );
    //time_t xy = mktime(&a_tm_struct);
    return  (int) mktime(&a_tm_struct);
}


int Now()
{
    /*
    time_t now = time(NULL);
    char szTime[100] ;
    sprintf(szTime, "%d", (int) now);
    printf("Time: %s\n", szTime);
    */

    return (int) time(NULL);
}


// http://www.gamedev.net/community/forums/topic.asp?topic_id=399702
char* GetLocalTime(int iSequentialDate)
{
    time_t tThisTime = (time_t) iSequentialDate;

    struct tm  *ts;
    ts = localtime(&tThisTime);
    //ts = gmtime ( &tThisTime );

    char* szBuffer = (char*) malloc(80 * sizeof(char));
    //strftime(buf, sizeof(buf), "%a %Y-%m-%d %H:%M:%S %Z", ts);
    strftime(szBuffer, 80, "%A %d.%m.%Y %H:%M:%S %Z", ts);

    Debug_Printf("%s\n", szBuffer);
    return szBuffer;
}


// apt-get install mailutils
// apt-get install sendemail
void SendMail()
{
    char szHostname[128];
    gethostname(szHostname, sizeof szHostname);
    Debug_Printf("Current hostname: %s\n", szHostname);

     // /usr/bin/mail ~'TEST' -s 'MailMngr Message' -t '[email protected]'   
    //sendemail -f powerfail.hostname@localhost -t [email protected] -u "subj" -m "hello" -s "smtp.example.com"
    char* szMailCommand = (char*) malloc(1000* sizeof(char));
    char szSender[250] ;
    sprintf(szSender, "powerfail.%s@localhost", szHostname);
    char szReceiver[] = "[email protected]";
    char szSubject[] = "Power-failure";

    char* szCurrentTime = GetLocalTime(Now());
    char* szMessage = (char*) malloc(1000);
    sprintf(szMessage, "Your humble server has suffered a fatal powerfailure and recovered now (%s).", szCurrentTime);
    char szServer[] = "smtp.example.com";


    sprintf(szMailCommand, "sendemail -f %s -t %s -u \"%s\" -m \"%s\" -s \"%s\"", szSender, szReceiver, szSubject, szMessage, szServer);
    free(szCurrentTime);
    free(szMessage);
    Debug_Printf("szMailCommand: %s\n", szMailCommand); 

    FILE* mFile = popen(szMailCommand, "w");
    pclose(mFile);

    free(szMailCommand);
}


int OnStartup()
{
    Debug_Printf("Entering OnStartup.\n");

    sqlite3 *handle;
    int iSQLconnectionError = sqlite3_open("PowerlossDetectionDB.sqlite3", &handle);
    if(iSQLconnectionError)
    {
        // If connection failed, handle returns NULL
        Debug_Printf("Database connection failed\n");
        return -1;
    }
    Debug_Printf("Connection successful\n");


    char szCreateTableStatement[] = "CREATE TABLE IF NOT EXISTS T_StartStopLog (SSL_Session_UID INTEGER PRIMARY KEY, SSL_StartTime INTEGER NOT NULL, SSL_StopTime INTEGER)";    
    ExecuteNonQuery(szCreateTableStatement, handle);



    int iLastStartupEntry = GetScalarInt("SELECT MAX(SSL_StartTime) FROM T_StartStopLog", handle);
    if(iLastStartupEntry!= 0)
    {
        char* szLastStartupTime = GetLocalTime(iLastStartupEntry);
        printf("Last startup-date:\t%s\n", szLastStartupTime);
        free(szLastStartupTime);
    }
    else
        printf("This is the first-time startup with PowerFailure tracking!\n");

    char* szQuery = (char*) malloc(1000* sizeof(char));
        sprintf(szQuery, "SELECT SSL_Session_UID FROM T_StartStopLog WHERE SSL_StartTime = %d", iLastStartupEntry);
        int iLastSession = GetScalarInt(szQuery, handle);
        Debug_Printf("Last session: %d\n", iLastSession);

        sprintf(szQuery, "SELECT SSL_StopTime FROM T_StartStopLog WHERE SSL_Session_UID = %d", iLastSession);
        int iLastShutdownEntry = GetScalarInt(szQuery, handle);
    free(szQuery);

    if(iLastSession != 0)
    {
        if(iLastShutdownEntry == 0)
        {
            Debug_Printf("Sending powerfail notification.\n");
            SendMail();
            Debug_Printf("Powerfail notification sent.\n");
        }
        else
        {
            char* szLastShutdownTime = GetLocalTime(iLastShutdownEntry);
            printf("Last shutdown-date:\t%s\n", szLastShutdownTime);
            free(szLastShutdownTime);
        }
    }


    char szInsertStatement[1000];
    int iUID = ++iLastSession;
    Debug_Printf("iUID: %d\n", iUID);

    if(iUID != 0)
    {
            sprintf(szInsertStatement, "INSERT INTO T_StartStopLog VALUES(%d, %d, NULL)", iUID, Now());
            ExecuteNonQuery(szInsertStatement, handle);
    }
    else
        Debug_Printf("Error, last UID could not be determined...");

    if(!iSQLconnectionError)
        sqlite3_close(handle);
    else
        Debug_Printf("Not closing connection.\n");

    Debug_Printf("Exiting OnStartup.\n");
    return EXIT_SUCCESS;    
}


int OnShutDown(int bSimulatePowerFail)
{
    Debug_Printf("Entering OnShutDown.\n");

    sqlite3 *handle;
    int iSQLconnectionError = sqlite3_open("PowerlossDetectionDB.sqlite3",&handle);
    if(iSQLconnectionError)
    {
        // If connection failed, handle returns NULL
        Debug_Printf("Database connection failed\n");
        return -1;
    }
    Debug_Printf("Connection successful\n");

    char szInsertStatement[1000];
    int iUID = GetScalarInt("SELECT MAX(SSL_Session_UID) FROM T_StartStopLog", handle);

    if(iUID == -1)
    {
        Debug_Printf("Error, last UID could not be determined...");

        if(!iSQLconnectionError)
            sqlite3_close(handle);

        return EXIT_FAILURE;
    }

    if(!bSimulatePowerFail)
    {
        sprintf(szInsertStatement, "UPDATE T_StartStopLog SET SSL_StopTime = %d WHERE SSL_Session_UID = %d", Now(), iUID);
        ExecuteNonQuery(szInsertStatement, handle); 
    }
    else
        Debug_Printf("Simulated power failure !\n");


    if(!iSQLconnectionError)
        sqlite3_close(handle);
    else
        Debug_Printf("Not closing connection.\n");


    Debug_Printf("Exiting OnShutDown.\n");
    return EXIT_SUCCESS;
}


int Clear()
{
    Debug_Printf("Entering Clear.\n");

    sqlite3 *handle;
    int iSQLconnectionError = sqlite3_open("PowerlossDetectionDB.sqlite3",&handle);
    if(iSQLconnectionError)
    {
        // If connection failed, handle returns NULL
        Debug_Printf("Database connection failed\n");
        return EXIT_FAILURE;
    }
    Debug_Printf("Connection successful\n");

    ClearTable("T_StartStopLog", handle);

    if(!iSQLconnectionError)
        sqlite3_close(handle);
    else
        Debug_Printf("Not closing connection.\n");

    Debug_Printf("Exiting Clear.\n");
    return EXIT_SUCCESS;
}


int List()
{
    Debug_Printf("Entering List.\n");

    sqlite3 *handle;
    int iSQLconnectionError = sqlite3_open("PowerlossDetectionDB.sqlite3",&handle);
    if(iSQLconnectionError)
    {
        // If connection failed, handle returns NULL
        Debug_Printf("Database connection failed\n");
        return EXIT_FAILURE;
    }
    Debug_Printf("Connection successful\n");

    SelectFromTable("T_StartStopLog", handle);

    if(!iSQLconnectionError)
        sqlite3_close(handle);
    else
        Debug_Printf("Not closing connection.\n");


    Debug_Printf("Exiting List.\n");
    return EXIT_SUCCESS;
}


int SwitchArgs(int argc, char* argv[])
{
    int i;
    for(i=0; i < argc; ++i)
    {
        if(i==0)
            continue;

        Debug_Printf("argv[%d] = %s\n", i, argv[i]);
        char* szThisArgument = ToLower(argv[i]);
        Debug_Printf("szThisArgument: %s\n", szThisArgument);

        if(!strcmp(szThisArgument, "--debug"))
        {
            Debug_Printf("Debug requested...\n");
            bDebug = 1;
            free(szThisArgument);
            continue;
        }
        else if(!strcmp(szThisArgument, "-dg"))
        {
            Debug_Printf("Debug requested...\n");
            bDebug = 1;
            free(szThisArgument);
            continue;
        }

        if(!strcmp(szThisArgument, "-shutdown"))
        {
            Debug_Printf("Shutdown requested...\n");
            bShutdownRequested = 1;
            bStartupRequested = 0;
            free(szThisArgument);
            continue;
        }
        else if(!strcmp(szThisArgument, "-d"))
        {
            Debug_Printf("Shutdown requested...\n");
            bShutdownRequested = 1;
            bStartupRequested = 0;
            free(szThisArgument);
            continue;
        }


        if(!strcmp(szThisArgument, "-s"))
        {
            Debug_Printf("Startup requested...\n");
            bStartupRequested = 1;
            free(szThisArgument);
            continue;
        }
        else if(!strcmp(szThisArgument, "--startup"))
        {
            Debug_Printf("Startup requested...\n");
            bStartupRequested = 1;
            free(szThisArgument);
            continue;
        }

        if(!strcmp(szThisArgument, "-s"))
        {
            Debug_Printf("Powerfail-simulation requested...\n");
            bSimulatePowerFailure = 1;
            free(szThisArgument);
            continue;
        }
        else if(!strcmp(szThisArgument, "--simulate"))
        {
            Debug_Printf("Powerfail-simulation requested...\n");
            bSimulatePowerFailure = 1;
            free(szThisArgument);
            continue;
        }

        if(!strcmp(szThisArgument, "-l"))
        {
            Debug_Printf("List requested...\n");
            bList = 1;
            free(szThisArgument);
            continue;
        }
        else if(!strcmp(szThisArgument, "--list"))
        {
            Debug_Printf("List requested...\n");
            bList = 1;
            free(szThisArgument);
            continue;
        }

        if(!strcmp(szThisArgument, "-c"))
        {
            Debug_Printf("Clear requested...\n");
            bClear = 1;
            free(szThisArgument);
            continue;
        }
        else if(!strcmp(szThisArgument, "--clear"))
        {
            Debug_Printf("Clear requested...\n");
            bClear = 1;
            free(szThisArgument);
            continue;
        }


        if(!strcmp(szThisArgument, "-t"))
        {
            Debug_Printf("Testmail requested...\n");
            bTestMail = 1;
            free(szThisArgument);
            continue;
        }
        else if(!strcmp(szThisArgument, "--testmail"))
        {
            Debug_Printf("Testmail requested...\n");
            bTestMail = 1;
            free(szThisArgument);
            continue;
        }

        printf("Usage error: Argument %s is not a valid argument.\nTerminating program.\n\n", szThisArgument);
        free(szThisArgument);
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;    
}




int main(int argc, char* argv[])
{
    SwitchArgs(argc, argv);

    /*
    int iSeqDate = GetSequentialDate(31, 12, 2010, 23, 59, 59);
    char* szDate = GetLocalTime(iSeqDate);
    printf("Sequential date: %s\n", szDate);

    time_t result = time(NULL);
    printf("%s%ju secs since the Epoch\n", asctime(localtime(&result)), (uintmax_t) result);
    */

    if(bTestMail)
    {
        SendMail();
        return EXIT_SUCCESS;
    }


    if(bClear)
    {
        Debug_Printf("Calling Clear\n");
        Clear();
    }


    if(bStartupRequested)
    {
        Debug_Printf("Calling OnStartup\n");
        OnStartup();
    }


    if(bShutdownRequested)
    {
        Debug_Printf("Calling OnShutDown\n");
        OnShutDown(bSimulatePowerFailure);
    }


    if(bList)
    {
        Debug_Printf("Calling List\n");
        List();
    }


    Debug_Printf("Powerfail notification finished !\n");
    return EXIT_SUCCESS;
}
1
  • 2
    Voted to move to Server Fault; This has a lot more to do with system administration than programming.
    – user229044
    Nov 15, 2010 at 17:37

2 Answers 2

3

There is already a start-up script for running local commands in /etc/rc.d/local. You can add a line there which executes your program.

If your script requires startup/shutdown/restart capabilities, you can add your own script to /etc/init.d. Start by looking at the existing scripts to figure out how to specify dependencies. Once you've created your script, you can add it to a given run level with update-rc.

1
  • Does rc.d also apply to shutdown scripts ?
    – Quandary
    Nov 15, 2010 at 18:06
0

You call it from an initscript with appropriate arguments upon startup and shutdown.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .