Annotation of fam/fam/Interest.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 "Interest.h"
24:
25: #include <string.h>
26: #include <errno.h>
27: #include <sys/param.h>
28: #if !defined(__FreeBSD__)
29: # include <sys/sysmacros.h>
30: #endif
31:
32: #ifdef HAVE_IRIX_XTAB_VERIFICATION
33: #include <stdio.h>
34: #include <exportent.h>
35: #include <netdb.h>
36: #include <sys/types.h>
37: #include <sys/socket.h>
38: #include <netinet/in.h>
39: #include <arpa/inet.h>
40: #endif // HAVE_IRIX_XTAB_VERIFICATION
41:
42: #include "Boolean.h"
43: #include "Event.h"
44: #include "FileSystem.h"
45: #include "IMon.h"
46: #include "Log.h"
47: #include "Pollster.h"
48: #include "timeval.h"
49:
50: Interest *Interest::hashtable[];
51: IMon Interest::imon(imon_handler);
52: bool Interest::xtab_verification = true;
53:
54: Interest::Interest(const char *name, FileSystem *fs, in_addr host, ExportVerification ev)
55: : hashlink(NULL),
56: myname(strcpy(new char[strlen(name) + 1], name)),
57: scan_state(OK),
58: cur_exec_state(NOT_EXECUTING),
59: old_exec_state(NOT_EXECUTING),
60: myhost(host),
61: mypath_exported_to_host(ev == NO_VERIFY_EXPORTED)
62: {
63: memset(&old_stat, 0, sizeof(old_stat));
64: IMon::Status s = IMon::BAD;
65:
66: s = imon.express(name, &old_stat);
67: if (s != IMon::OK)
68: { int rc = lstat(name, &old_stat);
69: if (rc < 0)
70: { Log::info("can't lstat %s", name);
71: memset(&old_stat, 0, sizeof old_stat);
72: }
73: }
74:
75: dev = old_stat.st_dev;
76: ino = old_stat.st_ino;
77:
78: if (ev == VERIFY_EXPORTED) verify_exported_to_host();
79:
80: if (s == IMon::OK) {
81:
82: if ((exported_to_host()) && (dev || ino))
83: {
84: // Insert on new chain.
85:
86: Interest **ipp = hashchain();
87: hashlink = *ipp;
88: *ipp = this;
89: }
90: else revoke();
91: }
92:
93:
94: #if HAVE_STAT_ST_FSTYPE_STRING
95: // Enable low-level monitoring.
96: // The NetWare filesystem is too slow to monitor, so
97: // don't even try.
98:
99: if ( !strcmp( (char *) &old_stat.st_fstype, "nwfs")) {
100: return;
101: }
102: #endif
103:
104: if (exported_to_host()) fs->ll_monitor(this, s == IMon::OK);
105: }
106:
107: Interest::~Interest()
108: {
109: Pollster::forget(this);
110: revoke();
111: delete[] myname;
112: }
113:
114: void
115: Interest::revoke()
116: {
117: // Traverse our hash chain. Delete this entry when we find it.
118: // Also check for other entries with same dev/ino.
119:
120: if (dev || ino)
121: {
122: bool found_same = false;
123: for (Interest *p, **pp = hashchain(); ((p = *pp) != NULL); )
124: if (p == this)
125: *pp = p->hashlink; // remove this from list
126: else
127: { if (p->ino == ino && p->dev == dev)
128: found_same = true;
129: pp = &p->hashlink; // move to next element
130: }
131: if (!found_same)
132: (void) imon.revoke(name(), dev, ino);
133: }
134: }
135:
136: bool
137: Interest::dev_ino(dev_t newdev, ino_t newino)
138: {
139: // Remove from hash chain and revoke imon's interest.
140:
141: revoke();
142:
143: dev = newdev;
144: ino = newino;
145:
146: if (newdev || newino)
147: {
148:
149: // Express interest.
150: IMon::Status s = IMon::BAD;
151: s = imon.express(name(), NULL);
152: if (s != IMon::OK) {
153: return true;
154: }
155:
156: // Insert on new chain.
157:
158: Interest **ipp = hashchain();
159: hashlink = *ipp;
160: *ipp = this;
161: }
162: else {
163: hashlink = NULL;
164: }
165: return false;
166: }
167:
168: /* Returns true if file changed since last stat */
169: bool
170: Interest::do_stat()
171: {
172: // Consider the case of a Directory changing into a file to be a
173: // simple change, and send only a Changed event.
174:
175: struct stat status;
176:
177: int rc = lstat(name(), &status);
178: if (rc < 0) {
179: if (errno == ETIMEDOUT) {
180: return false;
181: }
182: memset(&status, 0, sizeof status);
183: }
184:
185: bool exists = status.st_mode != 0;
186: bool did_exist = old_stat.st_mode != 0;
187: #ifdef HAVE_STAT_ST_CTIM_TV_NSEC
188: bool stat_changed = (old_stat.st_ctim.tv_sec != status.st_ctim.tv_sec) ||
189: (old_stat.st_ctim.tv_nsec != status.st_ctim.tv_nsec) ||
190: (old_stat.st_mtim.tv_sec != status.st_mtim.tv_sec) ||
191: (old_stat.st_mtim.tv_nsec != status.st_mtim.tv_nsec) ||
192: #else
193: bool stat_changed = (old_stat.st_ctime != status.st_ctime) ||
194: (old_stat.st_mtime != status.st_mtime) ||
195: #endif
196: (old_stat.st_mode != status.st_mode) ||
197: (old_stat.st_uid != status.st_uid) ||
198: (old_stat.st_gid != status.st_gid) ||
199: (old_stat.st_size != status.st_size) ||
200: (old_stat.st_ino != status.st_ino);
201: old_stat = status;
202:
203: // If dev/ino changed, move this interest to the right hash chain.
204:
205: bool keep_polling = false;
206: if (status.st_dev != dev || status.st_ino != ino) {
207: keep_polling = dev_ino(status.st_dev, status.st_ino);
208: }
209:
210: if (exists && !did_exist)
211: {
212: post_event(Event::Created);
213: if (!keep_polling) {
214: notify_created(this);
215: }
216: }
217: else if (did_exist && !exists)
218: {
219: post_event(Event::Deleted);
220: notify_deleted(this);
221: }
222:
223: return stat_changed;
224: }
225:
226: bool
227: Interest::do_scan()
228: {
229: bool stat_changed = false;
230: if (needs_scan() && active())
231: { needs_scan(false);
232: bool did_exist = exists();
233: stat_changed = do_stat();
234: if (stat_changed && did_exist && exists())
235: post_event(Event::Changed);
236: report_exec_state();
237: }
238: return stat_changed;
239: }
240:
241: void
242: Interest::report_exec_state()
243: {
244: if (old_exec_state != cur_exec_state && active())
245: { post_event(cur_exec_state == EXECUTING ?
246: Event::Executing : Event::Exited);
247: old_exec_state = cur_exec_state;
248: }
249: }
250:
251: void
252: Interest::imon_handler(dev_t device, ino_t inumber, int event)
253: {
254: assert(device || inumber);
255:
256: for (Interest *p = *hashchain(device, inumber), *next = p; p; p = next)
257: { next = p->hashlink;
258: if (p->ino == inumber && p->dev == device)
259: { if (event == IMon::EXEC)
260: { p->cur_exec_state = EXECUTING;
261: (void) p->report_exec_state();
262: }
263: else if (event == IMon::EXIT)
264: { p->cur_exec_state = NOT_EXECUTING;
265: (void) p->report_exec_state();
266: }
267: else
268: { assert(event == IMon::CHANGE);
269: p->scan();
270: }
271: }
272: }
273: }
274:
275: void
276: Interest::enable_xtab_verification(bool enable)
277: {
278: #ifdef HAVE_IRIX_XTAB_VERIFICATION
279: xtab_verification = enable;
280: Log::info("%s xtab verification of remote requests",
281: enable ? "Enabling" : "Disabling");
282: #endif
283: }
284:
285: // This determines whether this Interest falls on a filesystem which is
286: // exported to the host from which the request originated, and sets
287: // mypath_exported_to_host accordingly.
288: void
289: Interest::verify_exported_to_host()
290: {
291: #ifdef HAVE_IRIX_XTAB_VERIFICATION
292:
293: // This sets mypath_exported_to_host by checking /etc/xtab and seeing
294: // whether the export entry has an access list; if it does, we see
295: // whether each entry in the list is a netgroup-containing-the-
296: // requesting-host or the-requesting-host-itself.
297:
298: if ((!xtab_verification) ||
299: (myhost.s_addr == htonl(INADDR_LOOPBACK)))
300: {
301: mypath_exported_to_host = true;
302: return;
303: }
304: mypath_exported_to_host = false;
305:
306: // Check the xtab for the list of exported filesystems. If this
307: // Interest is located on a filesystem which has been exported to the
308: // Interest's host, set mypath_exported_to_host to true.
309:
310: Log::debug("XTAB: checking requested interest %s, dev/ino %d/%d, "
311: "from host %s", name(), dev, ino, inet_ntoa(myhost));
312:
313: // This is a little bogus... if we don't have a dev or ino, we're not
314: // going to find a matching exported filesystem, so let's bail.
315: if (!dev && !ino) {
316: Log::debug("XTAB: returning false for dev/ino %d/%d", dev, ino);
317: return;
318: }
319:
320: Cred::SuperUser.become_user(); // setexportent fails if you're not root??
321: exportent *xent;
322: FILE *xtab = setexportent();
323: if (xtab == NULL) {
324: Log::perror("setexportent");
325: return;
326: }
327:
328: while ((xent = getexportent(xtab)) != NULL) {
329: // See if the Interest falls under this export entry.
330: char *xent_path = xent->xent_dirname;
331: int xent_pathlen = strlen(xent_path);
332: if (xent_path[xent_pathlen - 1] == '/') --xent_pathlen;
333: if ((xent_pathlen == 0) || // xent_path was "/"
334: ((strncmp(xent_path, name(), xent_pathlen) == 0) &&
335: ((name()[xent_pathlen] == '/') || // so /usr doesn't
336: (name()[xent_pathlen] == '\0')))) // match /usrbooboo
337: {
338: // This export entry is somewhere above the requested directory.
339: // If it has the same device number, this is the entry we want.
340: struct stat sb;
341: //need to set cred here?
342: if (stat(xent_path, &sb) == -1) {
343: //only log if errno != EACCES or ENOENT?
344: Log::perror("stat(%s)", name());
345: continue;
346: }
347: if (sb.st_dev == dev) break; // yippee
348:
349: // Are there other cases we need to handle? Child filesystems
350: // exported -nohide still need to be exported, and we'll keep
351: // going until we find them.
352: }
353: }
354: endexportent(xtab); // Note that we're still using memory returned by
355: // getexportent().
356:
357: if (xent == NULL) {
358: // no matching xtab entry.
359: Log::info("Found no matching xtab entry for remote request from %s "
360: "for %s", inet_ntoa(myhost), name());
361: return;
362: }
363:
364: Log::debug("XTAB: %s is on xent %s", name(), xent->xent_dirname);
365:
366: char *xopt;
367: if ((xopt = getexportopt(xent, ACCESS_OPT)) == NULL) {
368: // This is exported to all clients, so we're OK.
369: Log::debug("XTAB: no access list for %s, so remote request was "
370: "granted", xent->xent_dirname);
371: mypath_exported_to_host = true;
372: return;
373: }
374:
375: hostent *hent = gethostbyaddr(&myhost, sizeof(in_addr), AF_INET);
376: if (hent == NULL) {
377: Log::perror("gethostbyaddr(%s)", inet_ntoa(myhost));
378: return;
379: }
380:
381: // This export entry has a list of netgroups and/or hosts which are
382: // allowed access. Run through the list & see if our host is there.
383: char *cs = xopt, *ce;
384: while ((!mypath_exported_to_host) && (cs != NULL) && (*cs != '\0')) {
385: ce = strchr(cs, ':');
386: if (ce != NULL) *ce = '\0';
387:
388: // See if this client is a netgroup containing myhost.
389: Log::debug("XTAB: seeing if %s is a netgroup containing host %s",
390: cs, hent->h_name);
391: if (innetgr(cs, hent->h_name, NULL, NULL)) {
392: mypath_exported_to_host = true;
393: break;
394: }
395: // See if this client is a netgroup containing one of myhost's aliases.
396: for (int ai = 0; hent->h_aliases[ai] != NULL; ++ai) {
397: Log::debug("XTAB: seeing if %s is a netgroup containing host "
398: "alias %s", cs, hent->h_aliases[ai]);
399: if (innetgr(cs, hent->h_aliases[ai], NULL, NULL)) {
400: mypath_exported_to_host = true;
401: break;
402: }
403: }
404: if (mypath_exported_to_host) break;
405:
406: // See if this client is a host.
407: Log::debug("XTAB: seeing if %s is a host with the address %s", cs,
408: inet_ntoa(myhost));
409: hostent *chent = gethostbyname(cs);
410: if ((chent != NULL) &&
411: (chent->h_addrtype == AF_INET) &&
412: chent->h_length == sizeof(in_addr)) {
413: for (int i = 0; chent->h_addr_list[i] != NULL; ++i) {
414: if (((in_addr *)(chent->h_addr_list[i]))->s_addr ==
415: myhost.s_addr) {
416: // whew. what a pain.
417: mypath_exported_to_host = true;
418: break;
419: }
420: }
421: }
422:
423: if(ce != NULL) cs = ce + 1;
424: else break;
425: }
426:
427: Log::info("XTAB: %s request from %s to monitor %s",
428: mypath_exported_to_host ? "Granted" : "Denied",
429: inet_ntoa(myhost), name());
430: return;
431:
432: #else
433:
434: // We don't have xtab verification, so this just says we're OK.
435: mypath_exported_to_host = true;
436:
437: #endif // HAVE_IRIX_XTAB_VERIFICATION
438: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>