Annotation of fam/fam/Interest.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 "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>