Я написал приведенную ниже программу, чтобы уведомлять меня по электронной почте, когда моя система 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
.
Вы вызываете его из сценария инициализации с соответствующими аргументами при запуске и завершении работы.