Multi File Monitor
12. November 2012 04:30

Ein C Programm mit ncurses, welches mehrere Logfiles gleichzeitig anzeigt.
Mit Aktivitätsüberwachung.

                    /*
 * 	Multi File Monitor
 * 	Sebastian Kricner
 * 	tuxwave.net 
 * 	February 2011
 *
 * 	Description:
 * 	This is a monitor for usual textfiles like logfiles.
 * 	It monitors multiple files using ncurses and inotify.
 * 	Use down+up arrows for selecting files and q for quitting.
 *
 * 	Compiling:
 * 	gcc multifilemon.c -o multifilemon -lncurses -lpthread
 *
 * 	Why this?
 * 	It was for coding practice.
 * 	This program can still be optimized and extended :)
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fcntl.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <curses.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>

#include <sys/inotify.h>
#include <sys/ioctl.h>

static void usage(char *name);
static void init_windows(void);
static void interaction(void);

static void *filewatch(void* arg);
static void *logfilewatch(void *arg);

void sigint_handler(int sig);
void sigwinch_handler(int sig);

char **arguments;
int *argcount, curfile,  threads_created;
WINDOW *filewindow, *sub_filewindow, *logwindow, *sub_logwindow, *infowindow, *sub_infowindow;
pthread_t logfilewatch_thread, filewatch_thread; 

sem_t locker;

int main(int argc, char **argv, char **envp)
{
	int count;
	struct stat statbuf;

	argcount = &argc;	
	arguments = argv;

	curfile = 1;
	threads_created = 0;

	if(argc == 1)
	{
		usage(argv[0]);
	}
	for(count = 1; count < argc; count++)
	{
		if(stat(argv[count], &statbuf) == 0)
		{
			if(!S_ISREG(statbuf.st_mode))
			{
				fprintf(stderr, "Error: No valid file: %s\n", argv[count]);
				usage(argv[0]);
			}
		}
		else
		{
			fprintf(stderr, "Error: No valid file: %s\n", argv[count]);
			usage(argv[0]);
		}
	}
	initscr();
	if(!has_colors() || !isatty(fileno(stdout)))
	{   
		endwin();
		fprintf(stderr, "Error - no color support available\n");
		exit(EXIT_FAILURE);
	} 
	if(start_color() != OK) 
	{   
		endwin();    
		fprintf(stderr, "Could not initialize colors\n");
		exit(EXIT_FAILURE);
	} 
	clear();
	cbreak();
	noecho();
	nonl();
	keypad(stdscr, 1);
	curs_set(0);
	use_default_colors();
	init_pair(1, COLOR_RED, -1);
	init_windows();
	exit(EXIT_SUCCESS);
}

void sigwinch_handler(int sig)
{
	endwin();
	fprintf(stderr, "This application does not support sigwinch-resizing, sorry\n");
	exit(EXIT_SUCCESS);
	return;
}

void sigint_handler(int sig)
{
	pthread_cancel(filewatch_thread);
 	pthread_cancel(logfilewatch_thread);
	endwin();
	exit(EXIT_SUCCESS);
	return;
}

static void usage(char *name)
{
	printf("Usage:\n"
	"%s [files...]\n", name);
	exit(EXIT_FAILURE);
}

static void init_windows(void)
{
	int count, max_y, max_x;
	getmaxyx(stdscr, max_y, max_x);

	filewindow = newwin(max_y-3, max_x/3, 0, 0);
	sub_filewindow = subwin(filewindow, max_y-5, (max_x/3)-2, 1, 1);
	scrollok(sub_filewindow, 1);

	logwindow = newwin(max_y-3, (max_x-(max_x/3)-1), 0, (max_x/3)+1);
	sub_logwindow = subwin(logwindow, max_y-5, max_x-(max_x/3)-3, 1, (max_x/3)+2);
	scrollok(sub_logwindow, 1);

	infowindow = newwin(3, max_x, max_y-3, 0);
	sub_infowindow = subwin(infowindow, 1, max_x-2, max_y-2, 1);

	box(filewindow, 0, 0);
	box(logwindow, 0, 0);
	box(infowindow, 0, 0);

	for(count = 1;count < *argcount;count++)
	{
		wprintw(sub_filewindow, "%s\n", *(arguments+count));
	}

	wprintw(sub_infowindow, "%s", *(arguments+1));

	refresh();
	wrefresh(filewindow);
	wrefresh(logwindow);
	wrefresh(infowindow);
	if(threads_created == 0)
	{
		interaction();
	}
	return;
}

static void interaction(void)
{
	int key;
	sem_init(&locker, 0, 0);

	pthread_create(&filewatch_thread,NULL,filewatch,NULL); 
	pthread_create(&logfilewatch_thread,NULL,logfilewatch,*(arguments+1)); 
	threads_created = 1;
	
	struct sigaction act[2];
	sigfillset(&act[0].sa_mask);
	sigdelset(&act[0].sa_mask, SIGWINCH);
	act[0].sa_handler = sigwinch_handler;
	sigaction(SIGWINCH, &act[0], 0);

	sigfillset(&act[1].sa_mask);
	sigdelset(&act[1].sa_mask, SIGINT);
	act[1].sa_handler = sigint_handler;
	sigaction(SIGINT, &act[1], 0);

	while(1)
	{
		key = getch();
		switch(key)
		{
			case KEY_UP:
					if(curfile > 1)
					{
						if(pthread_cancel(logfilewatch_thread))
						{
							endwin();
							fprintf(stderr, "pthread error\n");
							exit(EXIT_FAILURE);
						}
						curfile--;
						pthread_create(&logfilewatch_thread,NULL,logfilewatch,*(arguments+curfile)); 
					}
			break;
			case KEY_DOWN:
					if(curfile < *argcount-1)
					{
						if(pthread_cancel(logfilewatch_thread))
						{
							endwin();
							fprintf(stderr, "pthread error\n");
							exit(EXIT_FAILURE);
						}
						curfile++;
						pthread_create(&logfilewatch_thread,NULL,logfilewatch,*(arguments+curfile)); 
					}
			break;
			case 'q':
					pthread_cancel(filewatch_thread);
					pthread_cancel(logfilewatch_thread);
					endwin();
					exit(EXIT_SUCCESS);
			break;
		}
	}
	return;
}

static void *filewatch(void* arg)
{
	int inotify_fd, count, icount, len, cur_y, cur_x;
	char inobuf[1024 * (sizeof(struct inotify_event)+16)];

	int res;
	res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
	if(res != 0)
	{
		wclear(sub_filewindow);
		wprintw(sub_filewindow, "pthread error");
		touchwin(filewindow);
		wrefresh(filewindow);
		pthread_exit(0);
	}
	res = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
	if(res != 0)
        {   
		wclear(sub_filewindow);
		wprintw(sub_filewindow, "pthread error");
		touchwin(filewindow);
		wrefresh(filewindow);
                pthread_exit(0);
        }

	if((inotify_fd = inotify_init()) < 0)
	{
		wclear(sub_filewindow);
		wprintw(sub_filewindow, "inotify error");
		touchwin(filewindow);
		wrefresh(filewindow);
		pthread_exit(0);
	}
	for(count = 1;count < *argcount; count++)
	{
		if((inotify_add_watch(inotify_fd, *(arguments+count), IN_MODIFY)) == -1)
		{
			wclear(sub_filewindow);
			wprintw(sub_filewindow, "inotify error");
			touchwin(filewindow);
			wrefresh(filewindow);
			close(inotify_fd);
			pthread_exit(0);
		}
	}
	while((len = read(inotify_fd, inobuf, sizeof(struct inotify_event))))
	{
		while(icount < len)
		{
			struct inotify_event *event = (struct inotify_event *) &inobuf[icount];
			wclear(sub_filewindow);
			for(count = 1;count < *argcount;count++)
			{
				if(event->wd == count)
				{
					wattrset(sub_filewindow, COLOR_PAIR(1));
					wprintw(sub_filewindow, "%s\n", *(arguments+count));
					wattroff(sub_filewindow, COLOR_PAIR(1));
					if(curfile == count)
					{
						sem_post(&locker);
					}
				}
				else
				{
					wprintw(sub_filewindow, "%s\n", *(arguments+count));
				}
			}
			icount += sizeof(struct inotify_event) + event->len;
		}
		touchwin(filewindow);
		wrefresh(filewindow);
		icount=0;
	}
	pthread_exit(0);
}

static void *logfilewatch(void *arg)
{
	char *file = (char *) arg;
	int logfile, state, character;
	int res;
	res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
	if(res != 0)
	{
		wclear(sub_logwindow);
		wprintw(sub_logwindow, "pthread error");
		touchwin(logwindow);
		wrefresh(logwindow);
		pthread_exit(0);
	}
	res = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
	if(res != 0)
        {   
		wclear(sub_logwindow);
		wprintw(sub_logwindow, "pthread error");
		touchwin(logwindow);
		wrefresh(logwindow);
                pthread_exit(0);
        }
	wclear(sub_logwindow);
	touchwin(logwindow);
	wrefresh(logwindow);
	wclear(sub_infowindow);
	wprintw(sub_infowindow, "%s", file);
	touchwin(infowindow);
	wrefresh(infowindow);
	logfile = open(file, O_RDONLY);
	if(logfile == -1)
	{
		wprintw(sub_logwindow, "Could not open %s", file);
		touchwin(logwindow);
		wrefresh(logwindow);
		pthread_exit(0);
	}
	lseek(logfile, -1024, SEEK_END);
	while((state = read(logfile, &character, 1)) != -1)
	{
		if(state == 0)
		{
			sem_wait(&locker);
			continue;
		}
		if((char) character == '\n')
		{
			wprintw(sub_logwindow, "%c", character);
			touchwin(logwindow);
			wrefresh(logwindow);
		}
		else
		{
			wprintw(sub_logwindow, "%c", character);
			touchwin(logwindow);
		}
	}
	close(logfile);
	pthread_exit(0);
}

                    
        
Download