Назад | Перейти на главную страницу

Куда поставить задачу запуска и выключения?

Я написал приведенную ниже программу, чтобы уведомлять меня по электронной почте, когда моя система Linux была перезагружена из-за сбоя питания.

Я делаю это так: включаю восстановление после сбоя питания в BIOS, а затем регистрирую в базе данных SQlite каждый раз, когда компьютер запускается или выключается.

Когда время последнего выключения в базе данных SQlite равно NULL, я обнаружил сбой питания и отправил уведомление по электронной почте.

Я уже сделал большую часть этого, но мне все еще нужно добавить эту программу в соответствующие сценарии запуска и завершения работы.

Куда мне его поставить? Особое внимание следует уделить тому факту, что он будет отправлять уведомление о сбое питания через сервер smtp на мой телефон Android, что означает, что сценарий запуска должен запускаться только после инициализации сети (желательно также беспроводной).

Это кросс-пост мой вопрос здесь.

#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 'somebody@example.com'   
    //sendemail -f powerfail.hostname@localhost -t somebody@exymple.com -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[] = "reciever@example.com";
    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;
}

Уже есть стартовый скрипт для запуска локальных команд в /etc/rc.d/local. Вы можете добавить туда строку, которая выполняет вашу программу.

Если ваш сценарий требует возможности запуска / выключения / перезапуска, вы можете добавить свой собственный сценарий в /etc/init.d. Начните с просмотра существующих сценариев, чтобы понять, как указать зависимости. Создав свой скрипт, вы можете добавить его на заданный уровень выполнения с помощью update-rc.

Вы вызываете его из сценария инициализации с соответствующими аргументами при запуске и завершении работы.