[BACK]Return to IMonLinux.c++ CVS log [TXT][DIR] Up to [Development] / fam / fam

File: [Development] / fam / fam / IMonLinux.c++ (download)

Revision 1.1, Thu Apr 24 19:08:27 2003 UTC (14 years, 6 months ago) by trev
Branch point for: MAIN

Initial revision

//  Copyright (C) 1999 Silicon Graphics, Inc.  All Rights Reserved.
//  
//  This program is free software; you can redistribute it and/or modify it
//  under the terms of version 2 of the GNU General Public License as
//  published by the Free Software Foundation.
//
//  This program is distributed in the hope that it would be useful, but
//  WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  Further, any
//  license provided herein, whether implied or otherwise, is limited to
//  this program in accordance with the express provisions of the GNU
//  General Public License.  Patent licenses, if any, provided herein do not
//  apply to combinations of this program with other product or programs, or
//  any other product whatsoever.  This program is distributed without any
//  warranty that the program is delivered free of the rightful claim of any
//  third person by way of infringement or the like.  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 the Free Software Foundation, Inc., 59
//  Temple Place - Suite 330, Boston MA 02111-1307, USA.

#include "IMon.h"
#include "Log.h"

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <sys/wait.h>

#include <sys/ioctl.h>
#include <sys/sysmacros.h>
#include <linux/imon.h>

#define DEV_IMON "/dev/imon"
const intmask_t INTEREST_MASK = (IMON_CONTENT | IMON_ATTRIBUTE | IMON_DELETE |
				 IMON_EXEC | IMON_EXIT);

enum OpenStatus { OPEN_OK, OPEN_RETRY, OPEN_FAILED };

static OpenStatus open_device(int& imonfd)
{
    int major;
    bool foundMajor = false;
    char line[100], name[100];

    FILE* dev = fopen("/proc/devices", "r");
    if (!dev) {
	Log::critical("can't open /proc/devices: %m");
	return OPEN_FAILED;
    }
    while (fgets(line, sizeof line, dev) != NULL) {
	if (sscanf(line, "%d %s\n", &major, name) == 2
	    && strcmp(name, "imon") == 0) {
	    foundMajor = true;
	    break;
	}
    }
    fclose(dev);

    if (!foundMajor) {
	return OPEN_RETRY;
    }

    (void)unlink(DEV_IMON);
    if (mknod(DEV_IMON, S_IFCHR | 0600, makedev(major, 0)) == -1) {
	Log::critical("can't create %s: %m", DEV_IMON);
	return OPEN_FAILED;
    }

    int imon = open(DEV_IMON, O_RDONLY | O_NONBLOCK);
    (void)unlink(DEV_IMON);
    if (imon == -1) {
	Log::critical("can't open %s: %m", DEV_IMON);
	return OPEN_FAILED;
    }
    imonfd = imon;
    return OPEN_OK;
}

static void insmod()
{
    pid_t pid = fork();
    if (pid == 0) {
	execl("/sbin/insmod", "insmod", "imon", NULL);
	exit(1);
    }
    if (pid > 0) {
	waitpid(pid, NULL, 0);
    }
}

int IMon::imon_open()
{
    int imon;
    OpenStatus status = open_device(imon);
    if (status == OPEN_RETRY) {
	insmod();
	status = open_device(imon);
    }
    if (status == OPEN_RETRY) {
	Log::critical("can't open %s: imon major number not found in /proc/devices", DEV_IMON);
	return -1;
    }
    if (status != OPEN_OK) {
	Log::critical("can't open %s: %m", DEV_IMON);
	return -1;
    }
    return imon;
}

IMon::Status IMon::imon_express(const char *name, struct stat *status)
{
    famstat_t famstat;
    struct interest interest = { name, &famstat, INTEREST_MASK };
    int rc = ioctl(imonfd, IMONIOC_EXPRESS, &interest);
    if (rc < 0)
    {
        if (name[0] == '/') {
            Log::info("IMONIOC_EXPRESS on \"%s\" failed (euid: %i): %m",
                      name, geteuid());
        } else {
            char * cwd = getcwd(0, 256);
            Log::info("IMONIOC_EXPRESS on \"%s\" with cwd \"%s\" failed (euid: %i): %m",
                      name,cwd,geteuid());
            free(cwd);
        }
	return BAD;
    }

    //
    // Check for a race condition; if someone removed or changed the
    // file at the same time that we are expressing interest in it,
    // revoke the interest so we don't get notifications about changes
    // to a recycled inode that we don't otherwise care about.
    //
    struct stat st;
    if (status == NULL) {
	status = &st;
    }
    if (stat(name, status) == -1) {
	Log::perror("stat on \"%s\" failed", name);
	revoke(name, famstat.st_dev, famstat.st_ino);
	return BAD;
    }
    if (status->st_dev != famstat.st_dev
	|| status->st_ino != famstat.st_ino) {
	Log::error("File \"%s\" changed between express and stat",
		   name);
	revoke(name, famstat.st_dev, famstat.st_ino);
	return BAD;
    }	

    Log::debug("told imon to monitor \"%s\" = dev %d/%d, ino %d", name,
	       major(status->st_dev), minor(status->st_dev),
	       status->st_ino);

    return OK;
}

IMon::Status IMon::imon_revoke(const char *name, dev_t dev, ino_t ino)
{
    revoke_t rv = { dev, ino, INTEREST_MASK };
    int rc = ioctl(imonfd, IMONIOC_REVOKE, &rv);
    if (rc < 0) {
	Log::perror("IMONIOC_REVOKE on \"%s\" failed", name);
	return BAD;
    }
    Log::debug("told imon to forget \"%s\"", name);
    return OK;
}