Annotation of fam/fam/Directory.c++, Revision 1.1
1.1 ! trev 1: // Copyright (C) 1999 Silicon Graphics, Inc. All Rights Reserved.
! 2: //
! 3: // This program is free software; you can redistribute it and/or modify it
! 4: // under the terms of version 2 of the GNU General Public License as
! 5: // published by the Free Software Foundation.
! 6: //
! 7: // This program is distributed in the hope that it would be useful, but
! 8: // WITHOUT ANY WARRANTY; without even the implied warranty of
! 9: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Further, any
! 10: // license provided herein, whether implied or otherwise, is limited to
! 11: // this program in accordance with the express provisions of the GNU
! 12: // General Public License. Patent licenses, if any, provided herein do not
! 13: // apply to combinations of this program with other product or programs, or
! 14: // any other product whatsoever. This program is distributed without any
! 15: // warranty that the program is delivered free of the rightful claim of any
! 16: // third person by way of infringement or the like. See the GNU General
! 17: // Public License for more details.
! 18: //
! 19: // You should have received a copy of the GNU General Public License along
! 20: // with this program; if not, write the Free Software Foundation, Inc., 59
! 21: // Temple Place - Suite 330, Boston MA 02111-1307, USA.
! 22:
! 23: #include "Directory.h"
! 24:
! 25: #include <assert.h>
! 26: #include <errno.h>
! 27: #include <string.h>
! 28: #include <stdio.h>
! 29: #include <sys/dir.h>
! 30: #include <sys/stat.h>
! 31: #include <sys/wait.h>
! 32: #include <unistd.h>
! 33:
! 34: #include "Client.h"
! 35: #include "DirEntry.h"
! 36: #include "DirectoryScanner.h"
! 37: #include "Event.h"
! 38: #include "FileSystem.h"
! 39: #include "Log.h"
! 40: #include "Scheduler.h"
! 41:
! 42: Directory *Directory::current_dir;
! 43:
! 44: Directory::Directory(const char *name, Client *c, Request r, const Cred& cr)
! 45: : ClientInterest(name, c, r, cr, DIRECTORY), entries(NULL), unhangPid(-1)
! 46: {
! 47: dir_bits() = 0;
! 48: if (exported_to_host())
! 49: {
! 50: dir_bits() = SCANNING;
! 51: DirectoryScanner *scanner = new DirectoryScanner(*this,
! 52: Event::Exists, false,
! 53: new_done_handler, this);
! 54: if (scanner->done()) {
! 55: delete scanner;
! 56: } else {
! 57: client()->enqueue_scanner(scanner);
! 58: }
! 59: }
! 60: }
! 61:
! 62: void
! 63: Directory::new_done_handler(void *closure)
! 64: {
! 65: Directory *dir = (Directory *) closure;
! 66: dir->dir_bits() &= ~SCANNING;
! 67: dir->post_event(Event::EndExist);
! 68: }
! 69:
! 70: Directory::~Directory()
! 71: {
! 72: assert(!(dir_bits() & SCANNING));
! 73: if (dir_bits() & RESCAN_SCHEDULED)
! 74: Scheduler::remove_onetime_task(scan_task, this);
! 75: DirEntry *q, *p = entries;
! 76: if (p)
! 77: { (void) chdir();
! 78: while (p)
! 79: { q = p->next;
! 80: delete p;
! 81: p = q;
! 82: }
! 83: }
! 84: if (current_dir == this)
! 85: chdir_root();
! 86: }
! 87:
! 88: ClientInterest::Type
! 89: Directory::type() const
! 90: {
! 91: assert(!(dir_bits() & SCANNING));
! 92: return DIRECTORY;
! 93: }
! 94:
! 95: void
! 96: Directory::resume()
! 97: {
! 98: assert(!(dir_bits() & SCANNING));
! 99: ClientInterest::resume();
! 100: for (DirEntry *ep = entries; ep; ep = ep->next)
! 101: if (ep->needs_scan())
! 102: ep->scan();
! 103: }
! 104:
! 105: Interest *
! 106: Directory::find_name(const char *name)
! 107: {
! 108: assert(!(dir_bits() & SCANNING));
! 109: if (name[0] == '/')
! 110: // I don't know why this happens, but just in case ...
! 111: return this;
! 112: else
! 113: for (DirEntry *ep = entries; ep; ep = ep->next)
! 114: if (!strcmp(name, ep->name()))
! 115: return ep;
! 116: return NULL;
! 117: }
! 118:
! 119: // Directory::do_scan() scans a Directory. There are several cases.
! 120: //
! 121: // If monitoring is suspended, do nothing.
! 122: //
! 123: // If the Directory is actually not a directory (e.g., a file, a
! 124: // device or nonexistent), then it is lstat'd once, and a Changed,
! 125: // Deleted or Created event is written if appropriate.
! 126: //
! 127: // If a Directory changes from a directory to something else, then
! 128: // all its entries are deleted and Deleted events are sent.
! 129: //
! 130: // If it is a real directory, then it is lstat'd repeatedly and read
! 131: // repeatedly until it stops changing. This prevents fam from
! 132: // missing files in race conditions (I hope).
! 133:
! 134: bool
! 135: Directory::do_scan()
! 136: {
! 137: if (!active() || !needs_scan() || (dir_bits() & SCANNING))
! 138: return false;
! 139: become_user();
! 140: bool stat_changed = do_stat();
! 141: if (stat_changed && !isdir()) // Seems like a bug to send Changed after
! 142: post_event(Event::Changed); // Deleted, but what would fixing it break?
! 143: bool scan_entries = filesystem()->dir_entries_scanned();
! 144: dir_bits() |= SCANNING;
! 145: DirectoryScanner *scanner = new DirectoryScanner(*this, Event::Created,
! 146: scan_entries,
! 147: scan_done_handler,
! 148: this);
! 149: if (scanner->done()) {
! 150: delete scanner;
! 151: } else {
! 152: client()->enqueue_scanner(scanner);
! 153: }
! 154: return stat_changed;
! 155: }
! 156:
! 157: void
! 158: Directory::scan_task(void *closure)
! 159: {
! 160: Directory *dir = (Directory *) closure;
! 161: dir->dir_bits() &= ~RESCAN_SCHEDULED;
! 162: dir->scan();
! 163: }
! 164:
! 165: void
! 166: Directory::scan_done_handler(void *closure)
! 167: {
! 168: Directory *dir = (Directory *) closure;
! 169: dir->dir_bits() &= ~SCANNING;
! 170: }
! 171:
! 172: bool
! 173: Directory::chdir()
! 174: {
! 175: if (current_dir == this)
! 176: return true;
! 177:
! 178: int rc = ::chdir(name());
! 179: Log::debug("+chdir to \"%s\"", name());
! 180: if (rc < 0)
! 181: { Log::info("can't chdir(\"%s\"): %m", name());
! 182: if ((errno == EACCES) && (client() != NULL))
! 183: {
! 184: client()->suggest_insecure_compat(name());
! 185: }
! 186: return false;
! 187: }
! 188:
! 189: current_dir = this;
! 190: return true;
! 191: }
! 192:
! 193: bool
! 194: Directory::chdir_root()
! 195: {
! 196: int rc = 0;
! 197: if (current_dir)
! 198: {
! 199: rc = ::chdir("/");
! 200: Log::debug("-chdir to \"/\"");
! 201: current_dir = NULL;
! 202: }
! 203: return rc == 0;
! 204: }
! 205:
! 206: //
! 207: // void
! 208: // Directory::unhang()
! 209: //
! 210: // Description:
! 211: // This gets called after a system call has failed with oserror()
! 212: // set to ETIMEDOUT. This means that we can't contact the nfs
! 213: // server. In order to reconnect when the server becomes visible
! 214: // again, a process has to acually hang on the mount. We fork and
! 215: // exec nfsunhang to do this for us. We don't care about
! 216: // nfsunhang's exit status because we poll nfs mounts anyway.
! 217: //
! 218: #if HAVE_SGI_NOHANG
! 219: void
! 220: Directory::unhang()
! 221: {
! 222: int status;
! 223: if (unhangPid == -1
! 224: || waitpid(unhangPid, &status, WNOHANG) != 0) {
! 225:
! 226: if (access("/usr/lib/nfsunhang", X_OK) == -1) {
! 227: return;
! 228: }
! 229:
! 230: unhangPid = fork();
! 231: if (unhangPid == 0) {
! 232: for (int fd = getdtablesize() - 1; fd > 2; fd--) {
! 233: close(fd);
! 234: }
! 235: execl("/usr/lib/nfsunhang", "nfsunhang", name(), NULL);
! 236: exit(1);
! 237: }
! 238: Log::debug("unhangPid: %d", unhangPid);
! 239: }
! 240: }
! 241: #endif // HAVE_SGI_NOHANG
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>