Skip to content

Commit

Permalink
Improved support for logrotate daemon.
Browse files Browse the repository at this point in the history
1. Treats sighup as signal to close and reopen log file
2. Candump only supports autogenerated log file name by default.
   For use with logrotation daemon it expects consistent filename.
   I updated -l option to accept optional filename.
3. Since interface is a required argument, getopt skips final arg.
   Made -l take optional filename.  Without filename maintains existing behavior.
4. Since sighup can interupt the select system call, now checking epoll error code
   for interupted syscal and logfile enabled.  If those conditions are met, the errorcode is non critical.
  • Loading branch information
Richard Young committed Dec 7, 2020
1 parent 682e01a commit 3b5cbc0
Showing 1 changed file with 167 additions and 37 deletions.
204 changes: 167 additions & 37 deletions candump.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,18 @@ static const int canfd_on = 1;
static const char anichar[MAXANI] = { '|', '/', '-', '\\' };
static const char extra_m_info[4][4] = { "- -", "B -", "- E", "B E" };

#define MAXLOGNAMESZ 100
static FILE *logfile = NULL;
static char log_filename[MAXLOGNAMESZ];

static unsigned char silent = SILENT_INI;

extern int optind, opterr, optopt;

static volatile int running = 1;
static volatile int flag_reopen_file;
static int is_auto_log_name;
static volatile unsigned long sighup_count;

static void print_usage(char *prg)
{
Expand All @@ -133,7 +142,7 @@ static void print_usage(char *prg)
fprintf(stderr, " -a (enable additional ASCII output)\n");
fprintf(stderr, " -S (swap byte order in printed CAN data[] - marked with '%c' )\n", SWAP_DELIMITER);
fprintf(stderr, " -s <level> (silent mode - %d: off (default) %d: animation %d: silent)\n", SILENT_OFF, SILENT_ANI, SILENT_ON);
fprintf(stderr, " -l (log CAN-frames into file. Sets '-s %d' by default)\n", SILENT_ON);
fprintf(stderr, " -l <name> (log CAN-frames into file. Sets '-s %d' by default)\n", SILENT_ON);
fprintf(stderr, " -L (use log file format on stdout)\n");
fprintf(stderr, " -n <count> (terminate after reception of <count> CAN frames)\n");
fprintf(stderr, " -r <size> (set socket receive buffer to <size>)\n");
Expand Down Expand Up @@ -171,9 +180,16 @@ static void sigterm(int signo)
running = 0;
}

static int idx2dindex(int ifidx, int socket)
static void sighup(int signo)
{
if (signo == SIGHUP && running) {
flag_reopen_file = 1;
sighup_count++;
}
}

static int idx2dindex(int ifidx, int socket)
{
int i;
struct ifreq ifr;

Expand Down Expand Up @@ -221,6 +237,89 @@ static int idx2dindex(int ifidx, int socket)
return i;
}

static int sprint_auto_filename_format(char *buffer)
{
time_t currtime;
struct tm now;

if (time(&currtime) == (time_t)-1) {
perror("time");
return 1;
}

localtime_r(&currtime, &now);

sprintf(buffer, "candump-%04d-%02d-%02d_%02d%02d%02d.log",
now.tm_year + 1900,
now.tm_mon + 1,
now.tm_mday,
now.tm_hour,
now.tm_min,
now.tm_sec);

fprintf(stderr, "Enabling Logfile '%s'\n", buffer);

return 0;
}

/* opens file using global var logfile */
static int open_log_file(const char *file_name)
{
if (silent != SILENT_ON)
fprintf(stderr, "Warning: Console output active while logging!\n");

logfile = fopen(file_name, "w");

if (!logfile) {
perror("logfile");
return 1;
}

return 0;
}

static int reopen_file(FILE *file_handle)
{
const char *fopen_opts = (sighup_count > 0 && !is_auto_log_name) ? "a" : "w";

if (!file_handle)
return 1;

if (is_auto_log_name == 1) {
const int errcode = sprint_auto_filename_format(log_filename);

if (errcode != 0) {
return 1;
}
}

/* close existing filehandle, and open new one if necessary */
logfile = freopen(log_filename, fopen_opts, file_handle);

flag_reopen_file = 0;

return logfile == 0;
}

static int process_logname_arg(const char *arg)
{
if (arg != 0) {
const size_t len = strnlen(arg, MAXLOGNAMESZ);

if (len > 0 && len < MAXLOGNAMESZ) {
strncpy(log_filename, arg, MAXLOGNAMESZ - 1);
} else {
return 1;
}
is_auto_log_name = 0;
} else {
is_auto_log_name = 1;
sprint_auto_filename_format(log_filename);
}

return 0;
}

int main(int argc, char **argv)
{
int fd_epoll;
Expand All @@ -233,7 +332,7 @@ int main(int argc, char **argv)
unsigned char down_causes_exit = 1;
unsigned char dropmonitor = 0;
unsigned char extra_msg_info = 0;
unsigned char silent = SILENT_INI;

unsigned char silentani = 0;
unsigned char color = 0;
unsigned char view = 0;
Expand All @@ -257,16 +356,32 @@ int main(int argc, char **argv)
struct ifreq ifr;
struct timeval tv, last_tv;
int timeout_ms = -1; /* default to no timeout */
FILE *logfile = NULL;

signal(SIGTERM, sigterm);
signal(SIGHUP, sigterm);
signal(SIGINT, sigterm);
sigset_t sig_block_mask;
sigset_t sig_empty_mask;
struct sigaction sighup_action = { .sa_flags = SA_RESTART };
struct sigaction sigterm_action = { .sa_flags = SA_RESTART, .sa_handler = sigterm };

sigfillset(&sig_block_mask);
sighup_action.sa_mask = sig_block_mask;
sigemptyset(&sig_empty_mask);
sigterm_action.sa_mask = sig_empty_mask;

sigaction (SIGHUP, &sigterm_action, NULL);
sigaction (SIGINT, &sigterm_action, NULL);

last_tv.tv_sec = 0;
last_tv.tv_usec = 0;

while ((opt = getopt(argc, argv, "t:HciaSs:lDdxLn:r:he8T:?")) != -1) {
int getoptargc = argc;

/* Since interface is a required argument, we don't need to parse opt for final arg
* This enabled the -l option to take an optional filename */
if (getoptargc > 0) {
getoptargc = argc - 1;
}

while ((opt = getopt(getoptargc, argv, ":t:HciaSs:l:DdxLn:r:he8T:?")) != -1) {
switch (opt) {
case 't':
timestamp = optarg[0];
Expand Down Expand Up @@ -314,9 +429,32 @@ int main(int argc, char **argv)
}
break;

case ':': //handle flags with optional values

switch (optopt)
{
case 'l':
log = 1;

if (process_logname_arg(optarg) != 0) {
print_usage(basename(argv[0]));
exit(1);
}
break;
default:
fprintf(stderr, "option -%c is missing a required argument\n", optopt);
return EXIT_FAILURE;
}
break;

case 'l':
log = 1;

if (process_logname_arg(optarg) != 0) {
is_auto_log_name = 0;
print_usage(basename(argv[0]));
exit(1);
}
break;

case 'D':
Expand Down Expand Up @@ -371,6 +509,10 @@ int main(int argc, char **argv)
exit(0);
}

/* Configure SIGHUP handler to reopen file if logging */
sighup_action.sa_handler = log ? sighup : sigterm;
sigaction(SIGHUP, &sighup_action, NULL);

if (logfrmt && view) {
fprintf(stderr, "Log file format selected: Please disable ASCII/BINARY/SWAP/RAWDLC options!\n");
exit(0);
Expand Down Expand Up @@ -592,35 +734,10 @@ int main(int argc, char **argv)
}

if (log) {
time_t currtime;
struct tm now;
char fname[83]; /* suggested by -Wformat-overflow= */
const int result = open_log_file(log_filename);

if (time(&currtime) == (time_t)-1) {
perror("time");
if (result != 0)
return 1;
}

localtime_r(&currtime, &now);

sprintf(fname, "candump-%04d-%02d-%02d_%02d%02d%02d.log",
now.tm_year + 1900,
now.tm_mon + 1,
now.tm_mday,
now.tm_hour,
now.tm_min,
now.tm_sec);

if (silent != SILENT_ON)
fprintf(stderr, "Warning: Console output active while logging!\n");

fprintf(stderr, "Enabling Logfile '%s'\n", fname);

logfile = fopen(fname, "w");
if (!logfile) {
perror("logfile");
return 1;
}
}

/* these settings are static and can be held out of the hot path */
Expand All @@ -631,10 +748,17 @@ int main(int argc, char **argv)
msg.msg_control = &ctrlmsg;

while (running) {

if ((num_events = epoll_wait(fd_epoll, events_pending, currmax, timeout_ms)) <= 0) {
//perror("epoll_wait");
running = 0;
if(num_events == -1) {
const int serr = errno;
if (log && flag_reopen_file && serr == EINTR) {
if (reopen_file(logfile) != 0)
return -1;
} else {
running = 0;
}
}
continue;
}

Expand Down Expand Up @@ -817,6 +941,12 @@ int main(int argc, char **argv)
out_fflush:
fflush(stdout);
}

if (log && flag_reopen_file == 1) {
if (reopen_file(logfile) != 0) {
return -1;
}
}
}

for (i = 0; i < currmax; i++)
Expand Down

0 comments on commit 3b5cbc0

Please sign in to comment.