Skip to content

Instantly share code, notes, and snippets.

@stefanalt
Last active December 31, 2022 06:00
Show Gist options
  • Save stefanalt/dd4e68490d1a4e2a343b0beaa1b0d230 to your computer and use it in GitHub Desktop.
Save stefanalt/dd4e68490d1a4e2a343b0beaa1b0d230 to your computer and use it in GitHub Desktop.
log STDIN to in-memory ring-buffer, print buffer content on pipe close or signal
/*
* rbuflog.c - log STDIN to in-memory ring-buffer, print on pipe close or signal
*
* See usage() for details;
*/
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <time.h>
//#include "svnglbrev.h"
volatile int sigusr1_flag;
int opt_lines;
int opt_dots;
#define LOG_MAX 2048
#define LOG_LINE_LENGTH 512
int log_pos; // next entry to fill
struct log_buff {
/* todo timestamp */
char line[LOG_LINE_LENGTH];
} log_buff[LOG_MAX];
void log_print(
FILE *fp,
int linecnt,
const char *reason
)
{
int i;
int pos;
time_t daytime;
char daytime_frmt[80];
if( linecnt >= LOG_MAX-1 ){
linecnt = LOG_MAX-1;
}
/* print in output order */
pos = log_pos - linecnt;
if( pos < 0 ){
pos = pos + LOG_MAX;
}
time(&daytime);
strftime(daytime_frmt, 80, "%Y-%m-%dT%H:%M:%S", localtime(&daytime));
fprintf(fp, "## rbuflog %s (%s) #########\n", daytime_frmt, reason);
for(i=0; i<linecnt; i++){
if( log_buff[pos].line[0] != '\0' ){
fprintf(fp, "%s", log_buff[pos].line);
}
pos++;
if( pos >= LOG_MAX ){
pos = 0;
}
}
fflush(fp);
}
void sighandler(
int signo
)
{
sigusr1_flag = 1;
}
/*
* Show help
*
* RETURNS: N/A
*/
void usage(void)
{
fprintf(
stdout,
/* 1 2 3 4 5 6 7 */
/* 345678901234567890123456789012345678901234567890123456789012345678901234567*/
"rbuflog - log STDIN to in-memory ring-buffer, print on pipe close or signal\n"
"USAGE: rbuflog [options] [<outputfile>]\n"
"VERSION: $Id: rbuflog.c 4010 2022-12-30 17:02:57Z as $ "
#ifdef __SVNGLBREV__
"$GlbRev: " __SVNGLBREV__ " $"
#endif
"\n"
"BUILT: " __DATE__ " " __TIME__ "\n"
"\n"
"The program consumes lines from STDIN and stores them to a ring buffer.\n"
"When STDIN gets closed (or on receiption of a signal) the requested number\n"
"of lines are printed to STDOUT or to <outputfile>.\n"
"The ring buffer is currently limited to 2048 lines.\n"
"\n"
"SIGNALS:\n"
"Upon receiving SIGUSR1 the current ring-buffer content is printed as\n"
"requested on the command line.\n"
"\n"
"\n"
"OPTIONS: (defaults in square brackets)\n"
" -n <lines> Lines to print [10]. Less lines might be printed if the\n"
" line length execeeds 512 characters\n"
" -d <n> Print a dot to STDERR for every <n> input lines. [0=off].\n"
"\n"
"EXAMPLE:\n"
"some-program | rbuflog -n 20\n"
);
}
int main(
int argc,
char **argv
)
{
int opt;
long long linecnt;
char *filename;
FILE *fpout;
char *p;
struct sigaction act;
linecnt = 0;
opt_lines = 10;
opt_dots = 0;
sigusr1_flag = 0;
while( (opt=getopt(argc, argv, "hn:d:")) != EOF ){
switch(opt){
case 'n':
opt_lines = strtol(optarg, 0, 0);
break;
case 'd':
opt_dots = strtol(optarg, 0, 0);
break;
case '?': /* unknown option */
case 'h': /* help */
case ':': /* missing parameter */
usage();
exit(1);
}
}
argv += optind;
argc -= optind;
if( argc == 1 ){
filename = argv[0];
fpout = fopen(filename, "w");
if( fpout == NULL ){
fprintf(stderr, "rbuflog: can not open %s for writing\n", filename);
exit(1);
}
} else {
fpout = stdout;
}
signal(SIGINT, SIG_IGN);
/* gnu tee ignores SIGPIPE in case one of the output files is a pipe
* that doesn't consume all its input. Good idea... */
signal(SIGPIPE, SIG_IGN);
/* We want the USR1 interupt the fgets() so we can print the
log buffer even if there are no input lines at that time */
act.sa_handler = sighandler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGUSR1, &act, 0);
log_pos = 0;
memset(log_buff, 0x00, sizeof(log_buff));
while( 1 ){
/* read one line or less. If the line is too long for our
buffer, it will be continued in the next log buffer */
errno = 0;
p = fgets(log_buff[log_pos].line, LOG_LINE_LENGTH, stdin);
if( (p == NULL) ){
if( (errno == EINTR) && sigusr1_flag ){
log_print(fpout, opt_lines, "interrupt");
sigusr1_flag = 0;
continue;
} else {
log_print(fpout, opt_lines, "EOF");
break;
}
}
if( sigusr1_flag ){
log_print(fpout, opt_lines, "interrupt");
sigusr1_flag = 0;
}
linecnt++;
if( opt_dots ){
if( (linecnt % opt_dots) == 0 ){
fprintf(stderr, ".");
if( ((linecnt / opt_dots) % 80) == 0 ){
fprintf(stderr, "\n");
}
}
}
log_pos++;
if( log_pos >= LOG_MAX ){
log_pos = 0;
}
}
exit(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment