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

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

Revision 1.1, Thu Apr 24 19:08:26 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 "Directory.h"

#include <assert.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <sys/dir.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>

#include "Client.h"
#include "DirEntry.h"
#include "DirectoryScanner.h"
#include "Event.h"
#include "FileSystem.h"
#include "Log.h"
#include "Scheduler.h"

Directory *Directory::current_dir;

Directory::Directory(const char *name, Client *c, Request r, const Cred& cr)
    : ClientInterest(name, c, r, cr, DIRECTORY), entries(NULL), unhangPid(-1)
{
    dir_bits() = 0;
    if (exported_to_host())
    {
        dir_bits() = SCANNING;
        DirectoryScanner *scanner = new DirectoryScanner(*this,
						         Event::Exists, false,
						         new_done_handler, this);
        if (scanner->done()) {
            delete scanner;
        } else {
	    client()->enqueue_scanner(scanner);
        }
    }
}

void
Directory::new_done_handler(void *closure)
{
    Directory *dir = (Directory *) closure;
    dir->dir_bits() &= ~SCANNING;
    dir->post_event(Event::EndExist);
}

Directory::~Directory()
{
    assert(!(dir_bits() & SCANNING));
    if (dir_bits() & RESCAN_SCHEDULED)
	Scheduler::remove_onetime_task(scan_task, this);
    DirEntry *q, *p = entries;
    if (p)
    {   (void) chdir();
	while (p)
	{   q = p->next;
	    delete p;
	    p = q;
	}
    }
    if (current_dir == this)
	chdir_root();
}

ClientInterest::Type
Directory::type() const
{
    assert(!(dir_bits() & SCANNING));
    return DIRECTORY;
}

void
Directory::resume()
{
    assert(!(dir_bits() & SCANNING));
    ClientInterest::resume();
    for (DirEntry *ep = entries; ep; ep = ep->next)
	if (ep->needs_scan())
	    ep->scan();
}

Interest *
Directory::find_name(const char *name)
{
    assert(!(dir_bits() & SCANNING));
    if (name[0] == '/')
        // I don't know why this happens, but just in case ...
	return this;
    else
	for (DirEntry *ep = entries; ep; ep = ep->next)
	    if (!strcmp(name, ep->name()))
		return ep;
    return NULL;
}

//  Directory::do_scan() scans a Directory.  There are several cases.
//
//  If monitoring is suspended, do nothing.
//
//  If the Directory is actually not a directory (e.g., a file, a
//  device or nonexistent), then it is lstat'd once, and a Changed,
//  Deleted or Created event is written if appropriate.
//
//  If a Directory changes from a directory to something else, then
//  all its entries are deleted and Deleted events are sent.
//
//  If it is a real directory, then it is lstat'd repeatedly and read
//  repeatedly until it stops changing.  This prevents fam from
//  missing files in race conditions (I hope).

bool
Directory::do_scan()
{
    if (!active() || !needs_scan() || (dir_bits() & SCANNING))
	return false; 
    become_user();
    bool stat_changed = do_stat();
    if (stat_changed && !isdir())   // Seems like a bug to send Changed after
	post_event(Event::Changed); // Deleted, but what would fixing it break?
    bool scan_entries = filesystem()->dir_entries_scanned();
    dir_bits() |= SCANNING;
    DirectoryScanner *scanner = new DirectoryScanner(*this, Event::Created,
						     scan_entries,
						     scan_done_handler,
						     this);
    if (scanner->done()) {
        delete scanner;
    } else {
	client()->enqueue_scanner(scanner);
    }
    return stat_changed;
}

void
Directory::scan_task(void *closure)
{
    Directory *dir = (Directory *) closure;
    dir->dir_bits() &= ~RESCAN_SCHEDULED;
    dir->scan();
}

void
Directory::scan_done_handler(void *closure)
{
    Directory *dir = (Directory *) closure;
    dir->dir_bits() &= ~SCANNING;
}

bool
Directory::chdir()
{
    if (current_dir == this)
	return true;

    int rc = ::chdir(name());
    Log::debug("+chdir to \"%s\"", name());
    if (rc < 0)
    {   Log::info("can't chdir(\"%s\"): %m", name());
	if ((errno == EACCES) && (client() != NULL))
        {
            client()->suggest_insecure_compat(name());
        }
	return false;
    }

    current_dir = this;
    return true;
}

bool
Directory::chdir_root()
{
    int rc = 0;
    if (current_dir)
    {
        rc = ::chdir("/");
        Log::debug("-chdir to \"/\"");
	current_dir = NULL;
    }
    return rc == 0;
}

//
//  void
//  Directory::unhang()
//
//  Description:
//      This gets called after a system call has failed with oserror()
//      set to ETIMEDOUT.  This means that we can't contact the nfs
//      server.  In order to reconnect when the server becomes visible
//      again, a process has to acually hang on the mount.  We fork and
//      exec nfsunhang to do this for us.  We don't care about
//      nfsunhang's exit status because we poll nfs mounts anyway.
//
#if HAVE_SGI_NOHANG
void
Directory::unhang()
{
    int status;
    if (unhangPid == -1
	|| waitpid(unhangPid, &status, WNOHANG) != 0) {

	if (access("/usr/lib/nfsunhang", X_OK) == -1) {
	    return;
	}

	unhangPid = fork();
	if (unhangPid == 0) {
	    for (int fd = getdtablesize() - 1; fd > 2; fd--) {
		close(fd);
	    }
	    execl("/usr/lib/nfsunhang", "nfsunhang", name(), NULL);
	    exit(1);
	}
	Log::debug("unhangPid: %d", unhangPid);
    }
}
#endif // HAVE_SGI_NOHANG