User Tools

Site Tools


Differences

This shows you the differences between two versions of the page.

Link to this comparison view

c:daemon-example [2017-11-02 06:42] (current)
Line 1: Line 1:
 +======Pure C unix daemon example======
 +
 +The code excluded from mini-project of mail log parser. ​
 +
 +<code c maillog.c>​
 +
 +
 +
 +/* $Id: main.c,v 1.1 2016/08/14 23:47:57 root Exp root $ */
 +
 +#include <​stdarg.h>​
 +#include <​stdio.h>​
 +#include <​stdlib.h>​
 +#include <​err.h>​
 +#include <​syslog.h>​
 +#include <​signal.h>​
 +#include <​syslog.h>​
 +#include <​errno.h>​
 +#include <​fcntl.h>​
 +#include <​unistd.h>​
 +#include <​string.h>​
 +#include <​sys/​stat.h>​
 +#include <​libgen.h>​
 +
 +#include <​libpq-fe.h>​
 +#include <​confuse.h>​
 +
 +
 +#include "​config.h"​
 +#include "​parser.h"​
 +#include "​lexer.h"​
 +#include "​maillogd.h"​
 +
 +extern record_t _record;
 +
 +#define DEFAULT_PID_FILE "/​var/​run/​maillogd.pid"​
 +
 +char pid_file[FILENAME_MAX];​
 +int pid_file_h;
 +
 +
 +void shutdown();
 +//void signal_handler(int sig);
 +///void daemonize();​
 +
 +#ifndef MAILLOG_DB_SERVER
 +#define MAILLOG_DB_SERVER "​localhost"​
 +#endif
 +#ifndef MAILLOG_DB_USERNAME
 +#define MAILLOG_DB_USERNAME "​maillog"​
 +#endif
 +#ifndef MAILLOG_DB_PASSWORD
 +#define MAILLOG_DB_PASSWORD "​maillog"​
 +#endif
 +#ifndef MAILLOG_DBNAME
 +#define MAILLOG_DBNAME "​maillog"​
 +#endif
 +
 +#ifndef MAILLOG_LOGFILE
 +#define MAILLOG_LOGFILE "/​var/​log/​exim/​main.log"​
 +#endif
 +
 +#ifndef MAILLOG_SYSCON
 +#define MAILLOG_SYSCONF "/​usr/​local/​etc/​maillog.conf"​
 +#endif
 +
 +static char *dbserver = MAILLOG_DB_SERVER;​
 +static char *username = MAILLOG_DB_USERNAME;​
 +static char *password = MAILLOG_DB_PASSWORD;​
 +static char *dbname = MAILLOG_DBNAME;​
 +static char *logfile = MAILLOG_LOGFILE;​
 +static long int debug = 0;
 +
 +cfg_opt_t opts[] = {
 + CFG_SIMPLE_STR("​dbserver",​ &​dbserver),​
 + CFG_SIMPLE_STR("​username",​ &​username),​
 + CFG_SIMPLE_STR("​password",​ &​password),​
 + CFG_SIMPLE_STR("​dbname",​ &​dbname),​
 + CFG_SIMPLE_STR("​logfile",​ &​logfile),​
 + CFG_SIMPLE_INT("​debug",​ &​debug),​
 + CFG_END()
 +};
 +
 +cfg_t *config;
 +
 +PGconn *pgconn;
 +char* pgconnstr = NULL;
 +
 +
 +void handle_config_error(cfg_t *cfg, char *fmt, va_list ap) {
 +// if (cfg && cfg->​filename && cfg->​line)
 +//​ fprintf(stderr,​ "​%s:​%d:​ ", cfg->​filename,​ cfg->​line);​
 +// else if (cfg && cfg->​filename)
 +//​ fprintf(stderr,​ "%s: ", cfg->​filename);​
 +//​ vfprintf(stderr,​ fmt, ap);
 +//​ fprintf(stderr,​ "​\n"​);​
 +}
 +
 +void handle_signal(int sig){
 + switch (sig) {
 + case SIGHUP:
 + syslog(LOG_WARNING,​ "​Received %s signal.",​ strsignal(sig));​
 + break;
 + case SIGINT:
 + case SIGTERM:
 + syslog(LOG_INFO,​ "​Received %s, exiting.",​ strsignal(sig));​
 + shutdown();​
 + exit(EXIT_SUCCESS);​
 + break;
 + default:
 + syslog(LOG_WARNING,​ "​Unhandled signal %s.", strsignal(sig));​
 + break;
 + }
 +}
 +
 +void shutdown() {
 + if (pgconn != NULL) {
 + syslog(LOG_INFO,​ "​Finish PGconn"​);​
 +   PQfinish(pgconn);​
 + }
 + if (pid_file_h > 0)
 + close(pid_file_h);​
 + if (pid_file != NULL) 
 + unlink(pid_file);​
 + closelog();​
 +}
 +
 +
 +void underground_self() {
 + int pid, sid, i;
 + struct sigaction new_sig_action;​
 + sigset_t new_sig_set;​
 +
 + if (getppid() == 1) {
 + return;
 + }
 + sigemptyset(&​new_sig_set);​
 + sigaddset(&​new_sig_set,​ SIGCHLD);
 + sigaddset(&​new_sig_set,​ SIGTSTP);
 + sigaddset(&​new_sig_set,​ SIGTTOU);
 + sigaddset(&​new_sig_set,​ SIGTTIN);
 + sigprocmask(SIG_BLOCK,​ &​new_sig_set,​ NULL);
 +
 + new_sig_action.sa_handler = handle_signal;​
 + sigemptyset(&​new_sig_action.sa_mask);​
 + new_sig_action.sa_flags = 0;
 +
 + sigaction(SIGHUP,​ &​new_sig_action,​ NULL);
 + sigaction(SIGTERM,​ &​new_sig_action,​ NULL);
 + sigaction(SIGINT,​ &​new_sig_action,​ NULL);
 +
 + pid = fork();
 +
 + if (pid < 0) {
 + syslog(LOG_ERR,​ "Could not fork"​);​
 + exit(EXIT_FAILURE);​
 + }
 + if (pid > 0) {
 + syslog(LOG_INFO,​ "Child process created: %d\n", pid);
 + exit(EXIT_SUCCESS);​
 + }
 + umask(027);​
 + sid = setsid();
 + if (sid < 0) {
 + syslog(LOG_INFO,​ "Could not process session"​);​
 + exit(EXIT_FAILURE);​
 + }
 + for (i = getdtablesize();​ i >= 0; --i) {
 + close(i);
 + }
 + i = open("/​dev/​null",​ O_RDWR);
 + dup(i);
 + dup(i);
 + chdir("/"​);​
 +}
 +
 +int write_pidfile (char* pid_file) {
 +
 +    char str[FILENAME_MAX];​
 + int file_h = open(pid_file,​ O_RDWR | O_CREAT, 0600);
 +
 + if (file_h == -1) {
 + syslog(LOG_INFO,​ "Could not open PID lock file %s, exiting",​ pid_file);
 + exit(EXIT_FAILURE);​
 + }
 +
 + if (lockf(file_h,​ F_TLOCK, 0) == -1) {
 + syslog(LOG_INFO,​ "Could not lock PID lock file %s, exiting",​ pid_file);
 + exit(EXIT_FAILURE);​
 + }
 + sprintf(str,​ "​%d\n",​ getpid());
 + write(file_h,​ str, strlen(str));​
 + return file_h;
 +}
 +
 +#define MICRO_SLEEP_TIME 250000
 +#define START_SLEEP_TIME 1000000
 +
 +
 +
 +void read_logfile(char *logfile, char *pgconnstr, PGconn *pgconn) {
 +
 + FILE* logfile_p;
 + struct stat logfile_st;
 + struct stat st2;
 +
 + char bufstr[BUFSIZ];​
 + fpos_t pos;
 +
 + /* open file, read stat and write it to struct file */
 +
 + for (;;) { 
 + if ((logfile_p = fopen(logfile,​ "​r"​)) == NULL || fstat(fileno(logfile_p),​ &​logfile_st)) {
 + if (logfile_p != NULL) {
 + fclose(logfile_p);​
 + logfile_p = NULL;
 + syslog(LOG_ERR,​ "​Cannot open file: %s\n", logfile);
 + (void)usleep(START_SLEEP_TIME);​
 + continue;​
 + }
 + }
 + syslog(LOG_INFO,​ "Try open file: %s\n", logfile);
 + if ((logfile_p = fopen(logfile,​ "​r"​)) == NULL) {
 + syslog(LOG_ERR,​ "​Cannot open file: %s\n", logfile);
 + fclose(logfile_p);​
 + logfile_p = NULL;
 + (void)usleep(START_SLEEP_TIME);​
 + continue;​
 + }
 + if (fseek(logfile_p,​ 0, SEEK_END) != 0) {
 + syslog(LOG_ERR,​ "​Cannot read file: %s\n", logfile);
 + fclose(logfile_p);​
 + logfile_p = NULL;
 + (void)usleep(START_SLEEP_TIME);​
 + continue;​
 + }
 + if (fgetpos(logfile_p,​ &pos) == -1) {
 + syslog(LOG_ERR,​ "​Cannot position file: %s\n", logfile);
 + fclose(logfile_p);​
 + logfile_p = NULL;
 + (void)usleep(START_SLEEP_TIME);​
 + continue;​
 + }
 + break;
 + }
 +
 +// pgconn = PQconnectdb(pgconnstr);​
 +// if (PQstatus(pgconn) == CONNECTION_BAD) {
 +//​ syslog(LOG_ERR,​ "​Connection to database failed: %s", PQerrorMessage(pgconn));​
 +//​ PQfinish(pgconn);​
 +// } else {
 +//​ syslog(LOG_INFO,​ "​Connection to database success"​);​
 +// }
 +
 +
 + for (;;) {
 +
 + if (stat(logfile,​ &st2) == -1) {
 + syslog(LOG_INFO,​ "Ups, maybe file %s is closed",​ logfile);
 + if (logfile_p != NULL) {
 + fclose(logfile_p);​
 + logfile_p = NULL;
 + }
 + (void)usleep(MICRO_SLEEP_TIME);​
 + continue;​
 + }
 +
 + if (st2.st_ino != logfile_st.st_ino ||  st2.st_dev != logfile_st.st_dev || st2.st_nlink == 0) {
 +
 + syslog(LOG_INFO,​ "Ups, logfile %s changed",​ logfile);
 +
 + if (logfile_p != NULL) {
 + syslog(LOG_INFO,​ "Close logfile"​);​
 + fclose(logfile_p);​
 + logfile_p = NULL;
 + }
 +
 + logfile_p = fopen(logfile,​ "​r"​);​
 + syslog(LOG_INFO,​ "Again open logfile"​);​
 +
 + if (logfile_p != NULL) {
 + memcpy(&​logfile_st,​ &st2, sizeof(struct stat));
 + } else {
 + syslog(LOG_INFO,​ "​Cannot reopen file %s", logfile);
 + (void) usleep(MICRO_SLEEP_TIME);​
 + continue;​
 + }
 + if (fseek(logfile_p,​ 0, SEEK_END) != 0) {
 + syslog(LOG_ERR,​ "​Cannot read file: %s\n", logfile);
 + (void) usleep(MICRO_SLEEP_TIME);​
 + continue;​
 + }
 + if (fgetpos(logfile_p,​ &pos) == -1) {
 + syslog(LOG_ERR,​ "​Cannot position file: %s\n", logfile);
 + (void) usleep(MICRO_SLEEP_TIME);​
 + continue;​
 + }
 + }
 +
 + fgets(bufstr,​ BUFSIZ, logfile_p);
 +
 + if (ferror(logfile_p)) {
 + syslog(LOG_INFO,​ "Error reading file %s", logfile);
 + (void) usleep(MICRO_SLEEP_TIME);​
 + continue;​
 + }
 + if (feof(logfile_p)) {
 + fsetpos(logfile_p,​ &pos);
 + clearerr(logfile_p);​
 + (void) usleep(MICRO_SLEEP_TIME);​
 + continue;​
 + }
 + fgetpos(logfile_p,​ &pos);
 +
 + syslog(LOG_INFO,​ "I read from log: %s", bufstr);
 +
 + }
 +
 + if (logfile_p != NULL) {
 + syslog(LOG_INFO,​ "Close logfile"​);​
 + fclose(logfile_p);​
 + logfile_p = NULL;
 + }
 +}
 +
 +
 +int main(int argc, char **argv) {
 +
 + /* change process name */
 + char cur_wdir[FILENAME_MAX];​
 + char base_pname[FILENAME_MAX];​
 + char new_pname[FILENAME_MAX];​
 +
 + basename_r(argv[0],​ base_pname);​
 + getwd(cur_wdir);​
 + sprintf(new_pname,​ "​%s/​%s",​ cur_wdir, base_pname);​
 +
 + argv[0] = new_pname;
 +
 + /* init syslog parameters */
 + setlogmask(LOG_UPTO(LOG_INFO));​
 + openlog(base_pname,​ LOG_CONS | LOG_PERROR, LOG_DAEMON);​
 +
 + /* daemonize */
 + syslog(LOG_INFO,​ "Try start daemon\n"​);​
 + underground_self();​
 +
 +
 + /* write pid file with process id */
 + strncpy(pid_file,​ DEFAULT_PID_FILE,​ sizeof(DEFAULT_PID_FILE));​
 +
 + pid_file_h = write_pidfile(pid_file);​
 +
 + /* read configuration */
 + config = cfg_init(opts,​ 0);
 + (void)cfg_set_error_function(config,​ (cfg_errfunc_t)handle_config_error);​
 +
 + cfg_parse(config,​ MAILLOG_SYSCONF);​
 +
 +#define MAX_PGCONN_LEN 1024
 +
 +    /* prepare db connection config */
 + pgconnstr = malloc(MAX_PGCONN_LEN);​
 + if (pgconnstr == NULL) {
 + syslog(LOG_ERR,​ "​Cannot allocate memory for pgconnstr\n"​);​
 + exit(1);
 + }
 +
 + (void)sprintf(pgconnstr,​ "​user=%s password=%s dbname=%s hostname=%s",​
 + username, password, dbname, dbserver);
 +
 + /* main work cicle */
 +// while (1) {
 +//​ syslog(LOG_INFO,​ "​daemon says hello and %s", pgconnstr);
 +//​ usleep(1000000);​
 +// }
 +
 + read_logfile(logfile,​ pgconnstr, pgconn);
 +
 +
 + free(dbserver);​
 + free(username);​
 + free(password);​
 + free(dbname);​
 + free(logfile);​
 +
 + cfg_free(config);​
 +
 + shutdown();​
 + return 0;
 +}
 +
 +/* EOF */
 +</​code>​
 +
 +
 +----
 +[<>]
 +
 +
 +
 +