diff --git a/bin/loperIRCLogBot b/bin/loperIRCLogBot index 7c32c12..edca99f 100755 Binary files a/bin/loperIRCLogBot and b/bin/loperIRCLogBot differ diff --git a/daemon.c b/daemon.c new file mode 100644 index 0000000..75739e3 --- /dev/null +++ b/daemon.c @@ -0,0 +1,134 @@ +volatile sig_atomic_t reloadLogSig = 0; +volatile sig_atomic_t terminateSig = 0; + +void logsReopen(){ // logrotation can send SIGHUP to notify that log file should be reopened due it's cleaned up + if (freopen("/var/log/loperIRCLogBot.log", "a", stdout) == NULL ){ + syslog(LOG_ERR, "Failed to reopen /var/log/loperIRCLogBot.log after SIGHUP notificatuib recieved"); + exit(EXIT_FAILURE); + } + setlinebuf(stdout); // line buffer + if (freopen("/var/log/loperIRCLogBot.log", "a", stderr) == NULL ){ + syslog(LOG_ERR, "Failed to reopen /var/log/loperIRCLogBot.log after SIGHUP notificatuib recieved"); + exit(EXIT_FAILURE); + } + setvbuf(stderr, NULL, _IONBF, 0); // no buffering for srderr + reloadLogSig = 0; +} + +void killBySignalRecieved(irc_session_t * session){ + if (irc_cmd_quit(session, "interrupted by signal") != 0) // "Return code 0 means the command was sent to the IRC server successfully. + irc_disconnect(session); // This does not mean the operation succeed, and you need to wait for the appropriate + fclose(stdin); // event or for the error code via event_numeric event."(c)docs. :wSo FUCK THIS SHIT. It would be always EOF. + fclose(stdout); // In case servers stuck, we would have to wait AGES before app dies. No thanks. + fclose(stderr); + if (remove(DEF_PID_FILE) < 0) // delete PIDFile. + syslog(LOG_ERR, "Can't remove PID file: /var/run/loperIRCLogBot.pid"); + syslog(LOG_NOTICE, "Interrupted by SIGTERM"); // change to LOG_NOTICE + closelog(); + exit(EXIT_SUCCESS); +} + +// Signal handler +void signalHandler (int sig){ + switch (sig){ + case SIGHUP: + reloadLogSig = 1; + break; + case SIGTERM: + terminateSig = 1; + break; + case SIGINT: + _exit(2); // TODO: document this behavior as variant of application returned values + break; + default: + break; + } +} +void daemonMode(){ + setlogmask (LOG_UPTO (LOG_ERR)); + openlog("loperIRCLogBot", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_DAEMON); + + FILE * pidFile; + + pid_t pid; + + // PERFORM FIRST FORK + pid = fork(); + if (pid < 0) { + syslog(LOG_ERR, "Failed to make first fork"); + exit(EXIT_FAILURE); // report to log + } + if (pid > 0) // Exit parent process. pid of is not 0, then it's parent. pid = 0 then it's child. + exit(EXIT_SUCCESS); // report to log? + if (chdir("/") < 0) { // change working directory of the process to / + syslog(LOG_ERR, "Failed to change process dir to /"); + exit(EXIT_FAILURE); // it's safe to replace to 'return' statement + } + umask(0); // set umask for using filesystem as we want + if (setsid() < 0) { // set SID + syslog(LOG_ERR, "Failed to set SID"); + exit(EXIT_FAILURE); + } + // PERFORM SECOND FORK + pid = fork(); + if (pid < 0){ + syslog(LOG_ERR, "Failed to make second fork"); + exit(EXIT_FAILURE); + } + if (pid > 0) + exit(EXIT_SUCCESS); + + // Save process PID to file /var/run/loperIRCLogBot.pid // TODO: consider using of 'PIDFile' from libutil.h + if ((pidFile = fopen(DEF_PID_FILE, "w")) != NULL){ // + Consider using HFS3 and /run instead of /var/run. NOTE: could be a problem for embedded system, i.e. OWRT doesn't provide this filder + fprintf(pidFile,"%ld\n", (long) getpid()); + fclose(pidFile); + } + else { + syslog(LOG_ERR, "Failed to create PID file /var/run/loperIRCLogBot.pid"); + exit(EXIT_FAILURE); + } + // ############# DEFINE SIGNAL HANDLERS ####################### + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + + sa.sa_handler = signalHandler; + + if (sigfillset(&sa.sa_mask) < 0){ + syslog(LOG_ERR, "Can't set mask of blocked signals"); + exit(EXIT_FAILURE); + }; // block all signals when execute + sa.sa_flags = SA_RESTART; // restart interrupted system calls + + if (sigaction(SIGHUP, &sa, NULL) < 0) { + syslog(LOG_ERR, "Can't set sigaction() to handle application signals: SIGHUP"); + exit(EXIT_FAILURE); + } + if (sigaction(SIGINT, &sa, NULL) < 0) { + syslog(LOG_ERR, "Can't set sigaction() to handle application signals: SIGINT"); + exit(EXIT_FAILURE); + } + if (sigaction(SIGTERM, &sa, NULL) < 0) { + syslog(LOG_ERR, "Can't set sigaction() to handle application signals: SIGTERM"); + exit(EXIT_FAILURE); + } + + // ############# SIGNAL HANDLERS SET ####################### + //doing it using guidelines form man daemon + if (freopen("/dev/null", "rb", stdin) == NULL ){ + syslog(LOG_ERR, "Failed to close 'stdin'"); + exit(EXIT_FAILURE); + } + if (freopen("/var/log/loperIRCLogBot.log", "a", stdout) == NULL ){ + syslog(LOG_ERR, "Failed to redirect 'stdout' to /var/log/loperIRCLogBot.log"); + exit(EXIT_FAILURE); + } + setlinebuf(stdout); // line buffer + if (freopen("/var/log/loperIRCLogBot.log", "a", stderr) == NULL ){ + syslog(LOG_ERR, "Failed to redirect 'stderr' to /var/log/loperIRCLogBot.log"); + exit(EXIT_FAILURE); + } + setvbuf(stderr, NULL, _IONBF, 0); // no buffering for srderr + + //syslog(LOG_ERR, "SUCCESS"); + syslog(LOG_NOTICE, "loperIRCLogBot successfuly started"); +} diff --git a/defined_values.h b/defined_values.h index d52c8e6..7b93b3c 100644 --- a/defined_values.h +++ b/defined_values.h @@ -5,8 +5,10 @@ // Set if you want to turn mode +x on while connecting #define X_MODE +// Set path to loperIRCLogBot.pid file for daemon mode +#define DEF_PID_FILE "/var/run/loperIRCLogBot.pid" // re-define in configuration file // Current version of the program -#define __CUR_VER__ "1.3.1" +#define __CUR_VER__ "1.4" #define NPING_DEBUG #define NDEBUG @@ -21,7 +23,7 @@ #define DEF_HELP_MSG \ "Avaliable options:\n\n \ - -d, --daemon Start application as daemon (experimental)\n \ + -d, --daemon Start application as daemon. Writes to /var/log/loperIRCLogBot.log and syslog. Stores PID number at /var/run/loperIRCLogBot.pid\n \ -g, --genconf Create configuration file template. Attention! It will overrite your existing configuration file.\n \ -s, --silent Silent mode. All program messages stores to the 'output.txt' file\n \ -v, --version Application version\n\n \ diff --git a/loperIRCLogBot.c b/loperIRCLogBot.c index 7378c42..dcf605b 100644 --- a/loperIRCLogBot.c +++ b/loperIRCLogBot.c @@ -1,7 +1,7 @@ /*********************************************************************************** * Author: Dmitry Isaenko * * License: GNU GPL v.3 * - * Version: 1.3.1 * + * Version: 1.4 * * Site: https://developersu.blogspot.com/ * * 2017, Russia * ***********************************************************************************/ @@ -14,17 +14,17 @@ #include #include #include // only to get PATH_MAX -#include // for using setsid +#include // for using setsid. #include // Use syslog #include // Use signals +#include // Use pselect #include "defined_values.h" +#include "daemon.c" -// TODO after implementing signals set closelog(); -// TODO normal deamon-mode functionality +// TODO after implementing signals set closelog(); +++ use unlink(PID_FILE) to remove PID stored at /var/run/loperIRCLogBot.pid. Make logrotation by SIGHUP. 12.5 // TODO make it multi-channel -// TODO-feachure make an ability to define log path for the bot.conf? Or move it to /etc/ typedef struct conf_sruct { int status; @@ -345,8 +345,8 @@ void event_connect (irc_session_t * session, const char * event, const char * or } void dump_event (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count) -{ -#ifdef DEBUG +{ // TODO: Re-write. Actually, this implementation is also pice of shit +#ifdef DEBUG // It cause too many system calls even we don't need them char buf[512]; int cnt; @@ -393,7 +393,7 @@ void dump_event (irc_session_t * session, const char * event, const char * origi !(strcmp(event,"MODE")) ? strcmp(nickBuf, hostBuf) == 0 ? 0: fprintf(fp,"%s -!- %s [%s] set %s %s\n",nowTime, nickBuf, hostBuf, params[1], params[2] ? params[2]:""): !(strcmp(event,"PART")) ? fprintf(fp,"%s << %s [%s] parted: %s \n",nowTime, nickBuf, hostBuf, params[1]): !(strcmp(event,"TOPIC")) ? fprintf(fp,"%s -!- %s [%s] has changed topic to: %s \n",nowTime, nickBuf, hostBuf, params[1]): - !(strcmp(event,"QUIT")) ? fprintf(fp,"%s << %s [%s] quit: %s \n",nowTime, nickBuf, hostBuf, params[0]): + !(strcmp(event,"QUIT")) ? fprintf(fp,"%s << %s [%s] quit: %s \n",nowTime, nickBuf, hostBuf, params[0]): !(strcmp(event,"NICK")) ? fprintf(fp,"%s -!- %s [%s] changed nick to: %s \n",nowTime, nickBuf, hostBuf, params[0]): 0; if (!strcmp(event,"KICK")){ @@ -608,16 +608,6 @@ void reportSettingsIssues(int sum){ printf("\t'%s'\n", setLst[i]); } } -// not implemented & not used signal handler -void signalHandler (int sig){ - if (sig == SIGHUP){ - syslog(LOG_NOTICE, "loperIRCLogBot got SIGHUP"); - // close all opened files here - exit(EXIT_SUCCESS); - - } - -} void silentMode(){ // close stdin, redirect stdout & stderr @@ -630,65 +620,27 @@ void silentMode(){ if ( freopen("/dev/null", "rb", stdin) == NULL ) exit(EXIT_FAILURE); } -void daemonMode(){ - setlogmask (LOG_UPTO (LOG_ERR)); - openlog("loperIRCLogBot", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_DAEMON); - - pid_t pid; - - // PERFORM FIRST FORK - pid = fork(); - if (pid < 0) { - syslog(LOG_ERR, "Failed to make first fork"); - exit(EXIT_FAILURE); // report to log - } - if (pid > 0) // Exit parent process. pid of is not 0, then it's parent. pid = 0 then it's child. - exit(EXIT_SUCCESS); // report to log? - if (chdir("/") < 0) { // change working directory of the process to / - syslog(LOG_ERR, "Failed to change process dir to /"); - exit(EXIT_FAILURE); // it's safe to replace to 'return' statement - } - umask(0); // set umask for using filesystem as we want - if (setsid() < 0) { // set SID - syslog(LOG_ERR, "Failed to set SID"); - exit(EXIT_FAILURE); // report to log - } - // PERFORM SECOND FORK - pid = fork(); - if (pid < 0){ - syslog(LOG_ERR, "Failed to make second fork"); - exit(EXIT_FAILURE); // report to log - } - if (pid > 0) - exit(EXIT_SUCCESS); // report to log? - //doing it using guidelines form man daemon - if (freopen("/dev/null", "rb", stdin) == NULL ){ - syslog(LOG_ERR, "Failed to close 'stdin'"); - exit(EXIT_FAILURE); - } - if (freopen("/var/log/loperIRCLogBot.log", "a", stdout) == NULL ){ - syslog(LOG_ERR, "Failed to redirect 'stdout' to /var/log/loperIRCLogBot.log"); - exit(EXIT_FAILURE); - } - setlinebuf(stdout); // line buffer - if (freopen("/var/log/loperIRCLogBot.log", "a", stderr) == NULL ){ - syslog(LOG_ERR, "Failed to redirect 'stderr' to /var/log/loperIRCLogBot.log"); - exit(EXIT_FAILURE); - } - setvbuf(stderr, NULL, _IONBF, 0); // no buffering for srderr - - syslog(LOG_NOTICE, "loperIRCLogBot successfuly started"); -} int main(int argc, char *argv[]) { configuration config; irc_callbacks_t callbacks; // The IRC callbacks structure irc_session_t * session; - struct timeval tv; + struct timespec ts; // time structure for pselect(); fd_set in_set, out_set; int maxfd = 0; - + + // ### daemon mode tails. + // Define signals that we will block while using pselect(); See below. pselect set mask provided, then change it to default + sigset_t blockedSignals; // block all signals; to use in pselect() + sigset_t normalSignals; // define default mask + sigfillset(&blockedSignals); + sigdelset(&blockedSignals, SIGINT); + + sigfillset(&normalSignals); // Not sure that it could make impact on anything + sigprocmask(SIG_UNBLOCK, &normalSignals, NULL); + // ### + // Let's find out the path to executable file! It's needed, because when we create config file like ../executable -g // it creates it on the same folder where user located at, not at the folder where the executable is. // Same for logs. @@ -826,8 +778,13 @@ int main(int argc, char *argv[]) { ///////////////////// irc_run() replacement /////////////////// while ( irc_is_connected(session) ) { - tv.tv_usec = 250000; - tv.tv_sec = 0; + if (reloadLogSig == 1) //debugFunction + logsReopen(); + if (terminateSig == 1) //debugFunction + killBySignalRecieved(session); + + ts.tv_sec = 0; + ts.tv_nsec = 250000000; // Init sets FD_ZERO (&in_set); @@ -845,8 +802,8 @@ int main(int argc, char *argv[]) { irc_add_select_descriptors (session, &in_set, &out_set, &maxfd); - - if ( select (maxfd + 1, &in_set, &out_set, 0, &tv) < 0 ) + // Set mask to all signals, while neccessary for us are: SIGHUP, SIGTERM. Exclude SIGINT, 'cause no matter how this app fucks up in case of _exit(2). + if ( pselect (maxfd + 1, &in_set, &out_set, 0, &ts, &blockedSignals) < 0 ) // and errno == EINTR ! { printf ("%s Could not connect or I/O error: LIBIRC_ERR_TERMINATED\n", printTimeStamp()); break; // 1