Annotation of fam/fam/Directory.c++, Revision 1.1.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>