User Tools

Site Tools


Pure C unix daemon example

The code excluded from mini-project of mail log parser.

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 */

First PagePrevious PageBack to overviewNext PageLast Page