2008年10月28日 星期二

How to use inotify

1. gcc -o watcher watcher.c
2. mkdir /home/qustion/tmp/ionotify
3. sudo ./watcher --daemon
4. touch /home/qustion/tmp/ionotify/test.txt
5. sudo cat /var/log/syslog | tail

Reference:

1. "Monitor file system activity with inotify" http://www.ibm.com/developerworks/linux/library/l-ubuntu-inotify/index.html?ca=drs-

2. udev source code

#include <&sys/stat.h&>
#include <&sys/wait.h&>
#include <&stdlib.h&>
#include <&unistd.h&>
#include <&errno.h&>
#include <&stdio.h&>
#include <&time.h&>
#include <&fcntl.h&>
#include <&string.h&>
#include <&signal.h&>
#include <&getopt.h&>
#include <&sys/syslog.h&>
#include <&sys/inotify.h&>
#include <&sys/ioctl.h&>


#define WATCHING_DIR "/home/qustion/tmp/ionotify"
#define FD_MAX(a,b) ((a) > (b) ? (a) : (b))

#define READ_END 0
#define WRITE_END 1

static int inotify_fd = -1;
static volatile int sigchilds_waiting;
static volatile int udev_exit=0;
static void reap_sigchilds(void);
static void sig_handler(int signum);

static int signal_pipe[2] = {-1, -1};

int main(int argc, char *argv[], char *envp[]){

int retval;
int fd;
struct sigaction act;
fd_set readfds;
const char *value;
int daemonize=1;
int option;
int wd,i=0,length;
struct timeval tv;
int rc = 1;
int maxfd;
int op;
static const struct option options[] = {
{ "daemon", 0, NULL, 'd' },
{ "help", 0, NULL, 'h' },
{}
};
if (argc ==1 ){
printf("Usage: udevd [--help] [--daemon]\n");
goto exit;
}

while (1) {
op = getopt_long(argc, argv, "dh", options, NULL);
if (op == -1)
break;

switch (op) {
case 'd':
daemonize = 1;
break;
case 'h':
default:
printf("Usage: udevd [--help] [--daemon]\n");
goto exit;
}
}

openlog("watcher", LOG_PID, LOG_USER);
syslog(LOG_INFO, "program started.");

if (getuid() != 0) {
syslog(LOG_INFO, "root privileges required");

goto exit;
}

/* make sure std{in,out,err} fd's are in a sane state */
fd = open("/dev/null", O_RDWR);
if (fd < 0) {
fprintf(stderr, "cannot open /dev/null\n");
err("cannot open /dev/null\n");
}
if (fd > STDIN_FILENO)
dup2(fd, STDIN_FILENO);
if (write(STDOUT_FILENO, 0, 0) < 0)
dup2(fd, STDOUT_FILENO);
if (write(STDERR_FILENO, 0, 0) < 0)
dup2(fd, STDERR_FILENO);

retval = pipe(signal_pipe);
if (retval < 0) {
syslog(LOG_INFO,"error getting pipes: %s\n", strerror(errno));
goto exit;
}

retval = fcntl(signal_pipe[READ_END], F_GETFL, 0);
if (retval < 0) {
syslog(LOG_INFO,"error fcntl on read pipe: %s\n", strerror(errno));
goto exit;
}
retval = fcntl(signal_pipe[READ_END], F_SETFL, retval | O_NONBLOCK);
if (retval < 0) {
syslog(LOG_INFO,"error fcntl on read pipe: %s\n", strerror(errno));
goto exit;
}

retval = fcntl(signal_pipe[WRITE_END], F_GETFL, 0);
if (retval < 0) {
syslog(LOG_INFO,"error fcntl on write pipe: %s\n", strerror(errno));
goto exit;
}
retval = fcntl(signal_pipe[WRITE_END], F_SETFL, retval | O_NONBLOCK);
if (retval < 0) {
syslog(LOG_INFO,"error fcntl on write pipe: %s\n", strerror(errno));
goto exit;
}
if (daemonize) {
pid_t pid;

pid = fork();
switch (pid) {
case 0:
syslog(LOG_INFO,"daemonized fork running");
break;
case -1:
syslog(LOG_INFO,"fork of daemon failed: %s", strerror(errno));
rc = 4;
goto exit;
default:
syslog(LOG_INFO,"child [%u] running, parent exits", pid);
rc = 0;
goto exit;
}
}

dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
if (fd > STDERR_FILENO)
close(fd);

chdir("/");
umask(022);
setsid();

/* set signal handlers */
memset(&act, 0x00, sizeof(struct sigaction));
act.sa_handler = (void (*)(int)) sig_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
sigaction(SIGINT, &act, NULL);
sigaction(SIGTERM, &act, NULL);
sigaction(SIGCHLD, &act, NULL);
sigaction(SIGHUP, &act, NULL);



inotify_fd = inotify_init();
if (inotify_fd >= 0) {
wd = inotify_add_watch( fd, WATCHING_DIR ,
IN_MODIFY | IN_CREATE | IN_DELETE );

} else if (errno == ENOSYS){
syslog(LOG_INFO,"the kernel does not support inotify, udevd can't monitor rules file changes");
goto exit;
}else{
syslog(LOG_INFO,"inotify_init failed: %s", strerror(errno));
goto exit;
}

maxfd =FD_MAX(inotify_fd,signal_pipe[READ_END]) ;

while (!udev_exit) {
//syslog(LOG_INFO, "begin Recieve the event.");
int fdcount;
FD_ZERO(&readfds);
FD_SET(signal_pipe[READ_END], &readfds);
if (inotify_fd >= 0)
FD_SET(inotify_fd, &readfds);
tv.tv_sec = 5;
tv.tv_usec = 0;
fdcount = select(maxfd+1, &readfds, NULL, NULL, NULL);
if (fdcount < 0) {
if (errno != EINTR)
syslog(LOG_INFO,"error in select: %s\n", strerror(errno));
continue;
}


/* received a signal, clear our notification pipe */
if (FD_ISSET(signal_pipe[READ_END], &readfds)) {
char buf[256];
read(signal_pipe[READ_END], &buf, sizeof(buf));
}
/* rules directory inotify watch */
if ((inotify_fd >= 0) && FD_ISSET(inotify_fd, &readfds)) {
int nbytes;

/* discard all possible events, we can just reload the config */
if ((ioctl(inotify_fd, FIONREAD, &nbytes) == 0) && nbytes > 0) {
char *buf;


buf = malloc(nbytes);
if (buf == NULL) {
err("error getting buffer for inotify, disable watching\n");
close(inotify_fd);
inotify_fd = -1;
}
length =read(inotify_fd, buf, nbytes);
i=0;
if ( length < 0 ) {
syslog(LOG_INFO, "inotify_fd error read.");
}

while ( i < length ) {
struct inotify_event *event = ( struct inotify_event * ) &buf[ i ];
if ( event->len ) {
if ( event->mask & IN_CREATE ) {
if ( event->mask & IN_ISDIR ) {
syslog(LOG_INFO,"The directory %s was created.\n", event->name );
}
else {
syslog(LOG_INFO,"The file %s was created.\n", event->name );
}
}
else if ( event->mask & IN_DELETE ) {
if ( event->mask & IN_ISDIR ) {
syslog(LOG_INFO, "The directory %s was deleted.\n", event->name );
}
else {
syslog(LOG_INFO,"The file %s was deleted.\n", event->name );
}
}
else if ( event->mask & IN_MODIFY ) {
if ( event->mask & IN_ISDIR ) {
syslog(LOG_INFO, "The directory %s was modified.\n", event->name );
}
else {
syslog(LOG_INFO, "The file %s was modified.\n", event->name );
}
}
}
i += sizeof (struct inotify_event)+ event->len;
}
free(buf);
}
}
/* forked child has returned */
if (sigchilds_waiting) {
sigchilds_waiting = 0;
reap_sigchilds();
}
}//End of while loop


rc=0;
exit:
if (inotify_fd >= 0){
inotify_rm_watch( fd, wd );
close(inotify_fd);
}
if(fd >=0)
close(fd);
if (signal_pipe[READ_END] >= 0)
close(signal_pipe[READ_END]);
if (signal_pipe[WRITE_END] >= 0)
close(signal_pipe[WRITE_END]);
closelog();
return rc;
}


static void sig_handler(int signum)
{
switch (signum) {
case SIGINT:
case SIGTERM:
udev_exit=1;
syslog(LOG_INFO,"get SIGTERM.");
break;
case SIGCHLD:
/* set flag, then write to pipe if needed */
sigchilds_waiting = 1;
syslog(LOG_INFO,"get SIGCHLD.");
break;
case SIGHUP:
syslog(LOG_INFO,"get SIGHUP.");
break;
}
/* write to pipe, which will wakeup select() in our mainloop */
write(signal_pipe[WRITE_END], "", 1);

}

static void reap_sigchilds(void)
{
pid_t pid;
int status;

while (1) {
pid = waitpid(-1, &status, WNOHANG);
if (pid <= 0)
break;
if (WIFEXITED(status))
status = WEXITSTATUS(status);
else if (WIFSIGNALED(status))
status = WTERMSIG(status) + 128;
else
status = 0;

}
}

沒有留言: