You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
heartbeat/logd/ha_logd.c

1151 lines
24 KiB

/*
* ha_logd.c logging daemon
*
* Copyright (C) 2004 Guochun Shi <gshi@ncsa.uiuc.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <lha_internal.h>
#include <glib.h>
#include <clplumbing/cl_log.h>
#include <clplumbing/ipc.h>
#include <clplumbing/GSource.h>
#include <clplumbing/cl_malloc.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <clplumbing/loggingdaemon.h>
#include <netinet/in.h>
#include <clplumbing/lsb_exitcodes.h>
#include <sys/types.h>
#include <signal.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdarg.h>
#include <apphb.h>
#include <clplumbing/Gmain_timeout.h>
#include <clplumbing/coredumps.h>
#include <clplumbing/setproctitle.h>
#include <clplumbing/cl_signal.h>
#include <clplumbing/cl_misc.h>
#include <sys/wait.h>
#include <clplumbing/cl_pidfile.h>
#include <clplumbing/cl_syslog.h>
/*two processes involved
1. parent process which reads messages from all client channels
and writes them to the child process
2. the child process which reads messages from the parent process through IPC
and writes them to syslog/disk
I call the parent process READ process, and the child process WRITE one,
for convenience.
*/
#define DEFAULT_CFG_FILE HA_SYSCONFDIR "/logd.cf"
#define LOGD_PIDFILE HA_VARRUNDIR "/logd.pid"
#define FD_STDIN 0
#define FD_STDOUT 1
#define FD_STDERR 2
#define WRITE_PROC_CHAN 0
#define READ_PROC_CHAN 1
#define LOGD_QUEUE_LEN 128
#define EOS '\0'
#define nullchk(a) ((a) ? (a) : "<null>")
int logd_keepalive_ms = 1000;
int logd_warntime_ms = 5000;
int logd_deadtime_ms = 10000;
gboolean RegisteredWithApphbd = FALSE;
gboolean verbose =FALSE;
pid_t write_process_pid;
IPC_Channel* chanspair[2];
gboolean stop_reading = FALSE;
gboolean needs_shutdown = FALSE;
struct {
char debugfile[MAXLINE];
char logfile[MAXLINE];
char entity[MAXLINE];
int log_facility;
gboolean useapphbd;
mode_t logmode;
gboolean syslogfmtmsgs;
} logd_config =
{
"",
"",
"logd",
HA_LOG_FACILITY,
FALSE,
0644,
FALSE
};
static void logd_log(const char * fmt, ...) G_GNUC_PRINTF(1,2);
static int set_debugfile(const char* option);
static int set_logfile(const char* option);
static int set_facility(const char * value);
static int set_entity(const char * option);
static int set_useapphbd(const char* option);
static int set_sendqlen(const char * option);
static int set_recvqlen(const char * option);
static int set_logmode(const char * option);
static int set_syslogfmtmsgs(const char * option);
static char* cmdname = NULL;
struct directive{
const char* name;
int (*add_func)(const char*);
} Directives[]= {
{"debugfile", set_debugfile},
{"logfile", set_logfile},
{"logfacility", set_facility},
{"entity", set_entity},
{"useapphbd", set_useapphbd},
{"sendqlen", set_sendqlen},
{"recvqlen", set_recvqlen},
{"logmode", set_logmode},
{"syslogmsgfmt",set_syslogfmtmsgs}
};
struct _syslog_code {
const char *c_name;
int c_val;
};
static void
logd_log( const char * fmt, ...)
{
char buf[MAXLINE];
va_list ap;
int nbytes;
buf[MAXLINE-1] = EOS;
va_start(ap, fmt);
nbytes=vsnprintf(buf, sizeof(buf)-1, fmt, ap);
va_end(ap);
fprintf(stderr, "%s", buf);
return;
}
static int
set_debugfile(const char* option)
{
if (!option){
logd_config.debugfile[0] = EOS;
return FALSE;
}
cl_log(LOG_INFO, "setting debug file to %s", option);
strncpy(logd_config.debugfile, option, MAXLINE);
return TRUE;
}
static int
set_logfile(const char* option)
{
if (!option){
logd_config.logfile[0] = EOS;
return FALSE;
}
cl_log(LOG_INFO, "setting log file to %s", option);
strncpy(logd_config.logfile, option, MAXLINE);
return TRUE;
}
/* set syslog facility config variable */
static int
set_facility(const char * value)
{
int i;
i = cl_syslogfac_str2int(value);
if (i >= 0) {
cl_log(LOG_INFO, "setting log facility to %s", value);
logd_config.log_facility = i;
return(TRUE);
}
else {
return(FALSE);
}
}
static int
set_entity(const char * option)
{
if (!option){
logd_config.entity[0] = EOS;
return FALSE;
}
cl_log(LOG_INFO, "setting entity to %s", option);
strncpy(logd_config.entity, option, MAXLINE);
return TRUE;
}
static int
set_useapphbd(const char* option)
{
if (!option){
cl_log(LOG_ERR,"set_useapphbd: option is NULL");
return FALSE;
}
cl_log(LOG_INFO, "setting useapphbd to %s", option);
if (0 == strcmp(option, "yes")){
logd_config.useapphbd = TRUE;
return TRUE;
} else if (0 == strcmp(option, "no")){
logd_config.useapphbd = FALSE;
return TRUE;
} else {
cl_log(LOG_INFO,"invalid useapphbd option");
return FALSE;
}
}
static int
set_sendqlen(const char * option)
{
int length;
if (!option){
cl_log(LOG_ERR, "NULL send queue length");
return FALSE;
}
length = atoi(option);
if (length < 0){
cl_log(LOG_ERR, "negative send queue length");
return FALSE;
}
cl_log(LOG_INFO, "setting send queue length to %d", length);
chanspair[READ_PROC_CHAN]->ops->set_send_qlen(chanspair[READ_PROC_CHAN],
length);
return TRUE;
}
static int
set_recvqlen(const char * option)
{
int length;
if (!option){
cl_log(LOG_ERR, "NULL recv queue length");
return FALSE;
}
length = atoi(option);
if (length < 0){
cl_log(LOG_ERR, "negative recv queue length");
return FALSE;
}
cl_log(LOG_INFO, "setting recv queue length to %d", length);
chanspair[WRITE_PROC_CHAN]->ops->set_recv_qlen(chanspair[WRITE_PROC_CHAN],
length);
return TRUE;
}
static int
set_logmode(const char * option)
{
unsigned long mode;
char * endptr;
if (!option){
cl_log(LOG_ERR, "NULL logmode parameter");
return FALSE;
}
mode = strtoul(option, &endptr, 8);
if (*endptr != EOS) {
cl_log(LOG_ERR, "Invalid log mode [%s]", option);
return FALSE;
}
if (*option != '0') {
/* Whine if mode doesn't start with '0' */
cl_log(LOG_WARNING, "Log mode [%s] assumed to be octal"
, option);
}
logd_config.logmode = (mode_t)mode;
return TRUE;
}
static int
set_syslogfmtmsgs(const char * option)
{
gboolean dosyslogfmt;
if (cl_str_to_boolean(option, &dosyslogfmt) == HA_OK) {
cl_log_enable_syslog_filefmt(dosyslogfmt);
}else{
return FALSE;
}
return TRUE;
}
typedef struct {
char app_name[MAXENTITY];
pid_t pid;
gid_t gid;
uid_t uid;
IPC_Channel* chan;
IPC_Channel* logchan;
GCHSource* g_src;
}ha_logd_client_t;
static GList* logd_client_list = NULL;
static IPC_Message*
getIPCmsg(IPC_Channel* ch)
{
int rc;
IPC_Message* ipcmsg;
/* FIXME: Should we block here?? */
rc = ch->ops->waitin(ch);
switch(rc) {
default:
case IPC_FAIL:
cl_log(LOG_ERR, "getIPCmsg: waitin failure\n");
return NULL;
case IPC_BROKEN:
sleep(1);
return NULL;
case IPC_INTR:
return NULL;
case IPC_OK:
break;
}
ipcmsg = NULL;
rc = ch->ops->recv(ch, &ipcmsg);
if (rc != IPC_OK) {
return NULL;
}
return ipcmsg;
}
/* Flow control all clients off */
static void
logd_suspend_clients(IPC_Channel* notused1, gpointer notused2)
{
GList * gl;
stop_reading = TRUE;
for (gl=g_list_first(logd_client_list); gl != NULL
; gl = g_list_next(gl)) {
ha_logd_client_t* client = gl->data;
if (client && client->g_src) {
G_main_IPC_Channel_pause(client->g_src);
}else if (client) {
cl_log(LOG_ERR, "Could not suspend client [%s] pid %d"
, nullchk(client->app_name), client->pid);
}else{
cl_log(LOG_ERR, "%s: Could not suspend NULL client",
__FUNCTION__);
}
}
}
/* Resume input from clients - Flow control all clients back on */
static void
logd_resume_clients(IPC_Channel* notused1, gpointer notused2)
{
GList * gl;
stop_reading = FALSE;
for (gl=g_list_first(logd_client_list); gl != NULL
; gl = g_list_next(gl)) {
ha_logd_client_t* client = gl->data;
if (client && client->g_src) {
G_main_IPC_Channel_resume(client->g_src);
}else if (client) {
cl_log(LOG_ERR, "Could not resume client [%s] pid %d"
, nullchk(client->app_name), client->pid);
}else{
cl_log(LOG_ERR, "%s: Could not suspend NULL client",
__FUNCTION__);
}
}
}
static gboolean
on_receive_cmd (IPC_Channel* ch, gpointer user_data)
{
IPC_Message* ipcmsg;
ha_logd_client_t* client = (ha_logd_client_t*)user_data;
IPC_Channel* logchan= client->logchan;
if (!ch->ops->is_message_pending(ch)) {
goto getout;
}
ipcmsg = getIPCmsg(ch);
if (ipcmsg == NULL){
if (IPC_ISRCONN(ch)) {
cl_log(LOG_ERR, "%s: read error on connected channel [%s:%d]"
, __FUNCTION__, client->app_name, client->pid);
}
return FALSE;
}
if( ipcmsg->msg_body && ipcmsg->msg_len > 0 ){
if (client->app_name[0] == '\0'){
LogDaemonMsg* logmsg;
logmsg = (LogDaemonMsg*) ipcmsg->msg_body;
strncpy(client->app_name, logmsg->entity, MAXENTITY);
}
if (!IPC_ISWCONN(logchan)){
cl_log(LOG_ERR
, "%s: channel to write process disconnected"
, __FUNCTION__);
return FALSE;
}
if (logchan->ops->send(logchan, ipcmsg) != IPC_OK){
cl_log(LOG_ERR
, "%s: forwarding msg from [%s:%d] to"
" write process failed"
, __FUNCTION__
, client->app_name, client->pid);
cl_log(LOG_ERR, "queue too small? (max=%ld, current len =%ld)",
(long)logchan->send_queue->max_qlen,
(long)logchan->send_queue->current_qlen);
return TRUE;
}
}else {
cl_log(LOG_ERR, "on_receive_cmd:"
" invalid ipcmsg\n");
}
getout:
return TRUE;
}
static void
on_remove_client (gpointer user_data)
{
logd_client_list = g_list_remove(logd_client_list, user_data);
if (user_data){
cl_free(user_data);
}
return;
}
/*
*GLoop Message Handlers
*/
static gboolean
on_connect_cmd (IPC_Channel* ch, gpointer user_data)
{
ha_logd_client_t* client = NULL;
/* check paremeters */
if (NULL == ch) {
cl_log(LOG_ERR, "on_connect_cmd: channel is null");
return TRUE;
}
/* create new client */
if (NULL == (client = cl_malloc(sizeof(ha_logd_client_t)))) {
return FALSE;
}
memset(client, 0, sizeof(ha_logd_client_t));
client->pid = ch->farside_pid;
client->chan = ch;
client->logchan = (IPC_Channel*)user_data;
client->g_src = G_main_add_IPC_Channel(G_PRIORITY_DEFAULT,
ch, FALSE, on_receive_cmd,
(gpointer)client,
on_remove_client);
if (client->g_src <=0){
cl_log(LOG_ERR, "add the client to main loop failed");
cl_free(client);
return TRUE;
}
if (stop_reading){
G_main_IPC_Channel_pause(client->g_src);
}
logd_client_list = g_list_append(logd_client_list, client);
return TRUE;
}
static void
logd_make_daemon(gboolean daemonize)
{
long pid;
const char * devnull = "/dev/null";
if (daemonize){
pid = fork();
if (pid < 0) {
fprintf(stderr, "%s: could not start daemon\n"
, cmdname);
perror("fork");
exit(LSB_EXIT_GENERIC);
}else if (pid > 0) {
exit(LSB_EXIT_OK);
}
}
if (cl_lock_pidfile(LOGD_PIDFILE) < 0 ){
pid = cl_read_pidfile(LOGD_PIDFILE);
fprintf(stderr, "%s: already running [pid %ld].\n",
cmdname, pid);
exit(LSB_EXIT_OK);
}
if (daemonize || !verbose){
cl_log_enable_stderr(FALSE);
}
if (daemonize){
mode_t mask;
/*
* Some sample umask calculations:
*
* logmode = 0644
*
* (~0644)&0777 = 0133
* (0133 & ~0111) = 0022
* => umask will be 022 (the expected result)
*
* logmode = 0600
* (~0600)&0777 = 0177
* (0177 & ~0111) = 0066
*/
mask = (mode_t)(((~logd_config.logmode) & 0777) & (~0111));
umask(mask);
close(FD_STDIN);
(void)open(devnull, O_RDONLY); /* Stdin: fd 0 */
close(FD_STDOUT);
(void)open(devnull, O_WRONLY); /* Stdout: fd 1 */
close(FD_STDERR);
(void)open(devnull, O_WRONLY); /* Stderr: fd 2 */
}
}
static void
logd_stop(void)
{
long running_logd_pid = cl_read_pidfile(LOGD_PIDFILE);
int err;
if (running_logd_pid < 0) {
fprintf(stderr, "ha_logd already stopped.\n");
cl_log(LOG_INFO, "ha_logd already stopped.");
exit(LSB_EXIT_OK);
}
cl_log(LOG_DEBUG, "Stopping ha_logd with pid %ld", running_logd_pid);
if (kill((pid_t)running_logd_pid, SIGTERM) >= 0) {
/* Wait for the running logd to die */
cl_log(LOG_INFO, "Waiting for pid=%ld to exit",
running_logd_pid);
alarm(0);
do {
sleep(1);
}while (kill((pid_t)running_logd_pid, 0) >= 0);
}
err = errno;
if(errno == ESRCH) {
cl_log(LOG_INFO, "Pid %ld exited", running_logd_pid);
exit(LSB_EXIT_OK);
} else {
cl_perror("Pid %ld not killed", running_logd_pid);
exit((err == EPERM || err == EACCES)
? LSB_EXIT_EPERM
: LSB_EXIT_GENERIC);
}
}
static int
get_dir_index(const char* directive)
{
int j;
for(j=0; j < DIMOF(Directives); j++){
if (0 == strcasecmp(directive, Directives[j].name)){
return j;
}
}
return -1;
}
/* Adapted from parse_config in config.c */
static gboolean
parse_config(const char* cfgfile)
{
FILE* f;
char buf[MAXLINE];
char* bp;
char* cp;
char directive[MAXLINE];
int dirlength;
int optionlength;
char option[MAXLINE];
int dir_index;
gboolean ret = TRUE;
if ((f = fopen(cfgfile, "r")) == NULL){
cl_perror("Cannot open config file [%s]", cfgfile);
return(FALSE);
}
while(fgets(buf, MAXLINE, f) != NULL){
bp = buf;
/* Skip over white space*/
bp += strspn(bp, " \t\n\r\f");
/* comments */
if ((cp = strchr(bp, '#')) != NULL){
*cp = EOS;
}
if (*bp == EOS){
continue;
}
dirlength = strcspn(bp, " \t\n\f\r");
strncpy(directive, bp, dirlength);
directive[dirlength] = EOS;
if ((dir_index = get_dir_index(directive)) == -1){
fprintf(stderr, "Illegal directive [%s] in %s\n"
, directive, cfgfile);
ret = FALSE;
continue;
}
bp += dirlength;
/* skip delimiters */
bp += strspn(bp, " ,\t\n\f\r");
/* Set option */
optionlength = strcspn(bp, " ,\t\n\f\r");
strncpy(option, bp, optionlength);
option[optionlength] = EOS;
if (!(*Directives[dir_index].add_func)(option)) {
ret = FALSE;
}
}/*while*/
fclose(f);
return ret;
}
#define APPLOGDINSTANCE "logging daemon"
static void
logd_init_register_with_apphbd(void)
{
static int failcount = 0;
if (!logd_config.useapphbd || RegisteredWithApphbd) {
return;
}
if (apphb_register(cmdname, APPLOGDINSTANCE) != 0) {
/* Log attempts once an hour or so... */
if ((failcount % 60) == 0) {
cl_log(LOG_INFO, "Unable to register with apphbd."
"Continuing to try and register.\n");
}
++failcount;
return;
}
RegisteredWithApphbd = TRUE;
cl_log(LOG_INFO, "Registered with apphbd as %s/%s.\n",
cmdname, APPLOGDINSTANCE);
if (apphb_setinterval(logd_deadtime_ms) < 0
|| apphb_setwarn(logd_warntime_ms) < 0) {
cl_log(LOG_ERR, "Unable to setup with apphbd.\n");
apphb_unregister();
RegisteredWithApphbd = FALSE;
++failcount;
}else{
failcount = 0;
}
}
static gboolean
logd_reregister_with_apphbd(gpointer dummy)
{
if (logd_config.useapphbd) {
logd_init_register_with_apphbd();
}
return logd_config.useapphbd;
}
static gboolean
logd_apphb_hb(gpointer dummy)
{
if (logd_config.useapphbd) {
if (RegisteredWithApphbd) {
if (apphb_hb() < 0) {
/* apphb_hb() will fail if apphbd exits */
cl_log(LOG_ERR, "apphb_hb() failed.\n");
apphb_unregister();
RegisteredWithApphbd = FALSE;
}
}
/*
* Our timeout job (hb_reregister_with_apphbd) will
* reregister us if we become unregistered somehow...
*/
}
return TRUE;
}
static gboolean
logd_term_action(int sig, gpointer userdata)
{
GList *log_iter = logd_client_list;
GMainLoop *mainloop = (GMainLoop*)userdata;
ha_logd_client_t *client = NULL;
cl_log(LOG_DEBUG, "logd_term_action: received SIGTERM");
if (mainloop == NULL){
cl_log(LOG_ERR, "logd_term_action: invalid arguments");
return FALSE;
}
stop_reading = TRUE;
while(log_iter != NULL) {
client = log_iter->data;
log_iter = log_iter->next;
cl_log(LOG_DEBUG, "logd_term_action:"
" waiting for %d messages to be read for process %s",
(int)client->logchan->send_queue->current_qlen,
client->app_name);
client->logchan->ops->waitout(client->logchan);
}
cl_log(LOG_DEBUG, "logd_term_action"
": waiting for %d messages to be read by write process"
, (int)chanspair[WRITE_PROC_CHAN]->send_queue->current_qlen);
chanspair[WRITE_PROC_CHAN]->ops->waitout(chanspair[WRITE_PROC_CHAN]);
cl_log(LOG_DEBUG, "logd_term_action: sending SIGTERM to write process");
if (CL_KILL(write_process_pid, SIGTERM) >= 0){
pid_t pid;
pid = wait4(write_process_pid, NULL, 0, NULL);
if (pid < 0){
cl_log(LOG_ERR, "wait4 for write process failed");
}
}
g_main_quit(mainloop);
return TRUE;
}
static void
read_msg_process(IPC_Channel* chan)
{
GHashTable* conn_cmd_attrs;
IPC_WaitConnection* conn_cmd = NULL;
char path[] = "path";
char socketpath[] = HA_LOGDAEMON_IPC;
GMainLoop* mainloop;
mainloop = g_main_new(FALSE);
G_main_add_SignalHandler(G_PRIORITY_HIGH, SIGTERM,
logd_term_action,mainloop, NULL);
conn_cmd_attrs = g_hash_table_new(g_str_hash, g_str_equal);
g_hash_table_insert(conn_cmd_attrs, path, socketpath);
conn_cmd = ipc_wait_conn_constructor(IPC_ANYTYPE, conn_cmd_attrs);
g_hash_table_destroy(conn_cmd_attrs);
if (conn_cmd == NULL){
fprintf(stderr, "ERROR: create waiting connection failed");
exit(1);
}
/*Create a source to handle new connect rquests for command*/
G_main_add_IPC_WaitConnection( G_PRIORITY_HIGH, conn_cmd, NULL, FALSE
, on_connect_cmd, chan, NULL);
chan->ops->set_high_flow_callback(chan, logd_suspend_clients, NULL);
chan->ops->set_low_flow_callback(chan, logd_resume_clients, NULL);
chan->high_flow_mark = chan->send_queue->max_qlen;
chan->low_flow_mark = (chan->send_queue->max_qlen*3)/4;
G_main_add_IPC_Channel(G_PRIORITY_DEFAULT, chan, FALSE,NULL,NULL,NULL);
if (logd_config.useapphbd) {
logd_reregister_with_apphbd(NULL);
Gmain_timeout_add_full(G_PRIORITY_LOW,
60* 1000,
logd_reregister_with_apphbd,
NULL, NULL);
Gmain_timeout_add_full(G_PRIORITY_LOW,
logd_keepalive_ms,
logd_apphb_hb,
NULL, NULL);
}
g_main_run(mainloop);
return;
}
static gboolean
direct_log(IPC_Channel* ch, gpointer user_data)
{
IPC_Message* ipcmsg;
GMainLoop* loop;
loop =(GMainLoop*)user_data;
while(ch->ops->is_message_pending(ch)){
if (ch->ch_status == IPC_DISCONNECT){
cl_log(LOG_ERR, "read channel is disconnected:"
"something very wrong happened");
return FALSE;
}
ipcmsg = getIPCmsg(ch);
if (ipcmsg == NULL){
return TRUE;
}
if( ipcmsg->msg_body
&& ipcmsg->msg_len > 0 ){
LogDaemonMsg* logmsg;
LogDaemonMsg copy;
logmsg = (LogDaemonMsg*) ipcmsg->msg_body;
#define COPYFIELD(copy, msg, field) memcpy(((u_char*)&copy.field), ((u_char*)&msg->field), sizeof(copy.field))
COPYFIELD(copy, logmsg, use_pri_str);
COPYFIELD(copy, logmsg, entity);
COPYFIELD(copy, logmsg, entity_pid);
COPYFIELD(copy, logmsg, timestamp);
COPYFIELD(copy, logmsg, priority);
/* Don't want to copy logmsg->message */
cl_direct_log(copy.priority, logmsg->message
, copy.use_pri_str
, copy.entity, copy.entity_pid
, copy.timestamp);
(void)logd_log;
/*
if (verbose){
logd_log("%s[%d]: %s %s\n",
logmsg->entity[0]=='\0'?
"unknown": copy.entity,
copy.entity_pid,
ha_timestamp(copy.timestamp),
logmsg->message);
}
*/
if (ipcmsg->msg_done){
ipcmsg->msg_done(ipcmsg);
}
}
}
if(needs_shutdown) {
cl_log(LOG_INFO, "Exiting write process");
g_main_quit(loop);
return FALSE;
}
return TRUE;
}
static gboolean
logd_term_write_action(int sig, gpointer userdata)
{
/* as a side-effect, the log message makes sure we enter direct_log()
* one last time (so we always exit)
*/
needs_shutdown = TRUE;
cl_log(LOG_INFO, "logd_term_write_action: received SIGTERM");
cl_log(LOG_DEBUG, "Writing out %d messages then quitting",
(int)chanspair[WRITE_PROC_CHAN]->recv_queue->current_qlen);
direct_log(chanspair[WRITE_PROC_CHAN], userdata);
return TRUE;
}
static void
write_msg_process(IPC_Channel* readchan)
{
GMainLoop* mainloop;
IPC_Channel* ch = readchan;
mainloop = g_main_new(FALSE);
G_main_add_IPC_Channel(G_PRIORITY_DEFAULT,
ch, FALSE,
direct_log, mainloop, NULL);
G_main_add_SignalHandler(G_PRIORITY_HIGH, SIGTERM,
logd_term_write_action, mainloop, NULL);
g_main_run(mainloop);
}
static void
usage(void)
{
printf("usage: \n"
"%s [options]\n\n"
"options: \n"
"-d make the program a daemon\n"
"-k stop the logging daemon if it is already running\n"
"-s return logging daemon status \n"
"-c use this config file\n"
"-v verbosely print debug messages"
"-h print out this message\n\n",
cmdname);
return;
}
int
main(int argc, char** argv, char** envp)
{
int c;
gboolean daemonize = FALSE;
gboolean stop_logd = FALSE;
gboolean ask_status= FALSE;
const char* cfgfile = NULL;
pid_t pid;
cmdname = argv[0];
while ((c = getopt(argc, argv, "c:dksvh")) != -1){
switch(c){
case 'd': /* daemonize */
daemonize = TRUE;
break;
case 'k': /* stop */
stop_logd = TRUE;
break;
case 's': /* status */
ask_status = TRUE;
break;
case 'c': /* config file*/
cfgfile = optarg;
break;
case 'v':
verbose = TRUE;
break;
case 'h': /*help message */
default:
usage();
exit(1);
}
}
set_ipc_time_debug_flag(FALSE);
cl_log_set_uselogd(FALSE);
if (!cfgfile && access(DEFAULT_CFG_FILE, F_OK) == 0) {
cfgfile = DEFAULT_CFG_FILE;
}
/* default one set to "logd"
* by setting facility, we enable syslog
*/
cl_log_enable_stderr(TRUE);
cl_log_set_entity(logd_config.entity);
cl_log_set_facility(logd_config.log_facility);
if (ask_status){
long pid;
if( (pid = cl_read_pidfile(LOGD_PIDFILE)) > 0 ){
printf("logging daemon is running [pid = %ld].\n", pid);
exit(LSB_EXIT_OK);
}else{
if (pid == - LSB_STATUS_VAR_PID) {
printf("logging daemon is stopped: %s exists.\n"
, LOGD_PIDFILE);
}else{
printf("logging daemon is stopped.\n");
}
}
exit(-pid);
}
if (stop_logd){
logd_stop();
exit(LSB_EXIT_OK);
}
logd_make_daemon(daemonize);
if (ipc_channel_pair(chanspair) != IPC_OK){
cl_perror("cannot create channel pair IPC");
return -1;
}
if (cfgfile && !parse_config(cfgfile)) {
cl_log(LOG_ERR, "Config file [%s] is incorrect."
, cfgfile);
exit(LSB_EXIT_NOTCONFIGED);
}
if (strlen(logd_config.debugfile) > 0) {
cl_log_set_debugfile(logd_config.debugfile);
}
if (strlen(logd_config.logfile) > 0) {
cl_log_set_logfile(logd_config.logfile);
}
cl_log_set_entity(logd_config.entity);
cl_log_set_facility(logd_config.log_facility);
cl_log(LOG_INFO, "logd started with %s.",
cfgfile ? cfgfile : "default configuration");
if (cl_enable_coredumps(TRUE) < 0){
cl_log(LOG_ERR, "enabling core dump failed");
}
cl_cdtocoredir();
chanspair[WRITE_PROC_CHAN]->ops->set_recv_qlen(chanspair[WRITE_PROC_CHAN],
LOGD_QUEUE_LEN);
chanspair[READ_PROC_CHAN]->ops->set_send_qlen(chanspair[READ_PROC_CHAN],
LOGD_QUEUE_LEN);
if (init_set_proc_title(argc, argv, envp) < 0) {
cl_log(LOG_ERR, "Allocation of proc title failed.");
return -1;
}
switch(pid = fork()){
case -1:
cl_perror("Can't fork child process!");
return -1;
case 0:
/*child*/
set_proc_title("ha_logd: write process");
write_msg_process(chanspair[WRITE_PROC_CHAN]);
break;
default:
/*parent*/
set_proc_title("ha_logd: read process");
write_process_pid = pid;
read_msg_process(chanspair[READ_PROC_CHAN]);
break;
}
return 0;
}