Annotation of fam/fam/ServerHost.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 "ServerHost.h"
! 24:
! 25: #include <netdb.h>
! 26: #include <rpc/rpc.h>
! 27: #include <rpc/pmap_clnt.h>
! 28: #include <string.h>
! 29: #include <sys/socket.h>
! 30: #include <unistd.h>
! 31:
! 32: #include "FileSystem.h"
! 33: #include "Event.h"
! 34: #include "Listener.h"
! 35: #include "Log.h"
! 36: #include "Pollster.h"
! 37: #include "Scheduler.h"
! 38: #include "ServerConnection.h"
! 39:
! 40: ///////////////////////////////////////////////////////////////////////////////
! 41: // Construction/Destruction
! 42:
! 43: ServerHost::ServerHost(const hostent& hent)
! 44: : refcount(0), connector(Listener::FAMPROG, Listener::FAMVERS,
! 45: ((in_addr *) hent.h_addr)->s_addr, connect_handler, this),
! 46: connection(NULL), unique_request(1), deferred_scans(NULL), last(NULL),
! 47: min_time(0)
! 48: {
! 49: // Save first component of full hostname.
! 50:
! 51: char *p = strchr(hent.h_name, '.');
! 52: unsigned int nchar;
! 53: if (p)
! 54: nchar = p - hent.h_name;
! 55: else
! 56: nchar = strlen(hent.h_name);
! 57:
! 58: myname = new char[nchar + 1];
! 59: (void) strncpy(myname, hent.h_name, nchar);
! 60: myname[nchar] = '\0';
! 61: }
! 62:
! 63: ServerHost::~ServerHost()
! 64: {
! 65: assert(!active());
! 66: Scheduler::remove_onetime_task(deferred_scan_task, this);
! 67: if (is_connected())
! 68: { delete connection;
! 69: Scheduler::remove_onetime_task(timeout_task, this);
! 70: }
! 71: else
! 72: Pollster::forget(this);
! 73: delete [] myname;
! 74:
! 75: DeferredScan *ds = deferred_scans, *dsp;
! 76: while (ds != NULL)
! 77: {
! 78: dsp = ds;
! 79: ds = ds->next;
! 80: delete dsp;
! 81: }
! 82: }
! 83:
! 84: //////////////////////////////////////////////////////////////////////////////
! 85: // Activation. "Active" means that something on this host needs monitoring.
! 86:
! 87: void
! 88: ServerHost::activate()
! 89: {
! 90: if (is_connected())
! 91: Scheduler::remove_onetime_task(timeout_task, this);
! 92: else
! 93: { connector.activate();
! 94: Pollster::watch(this);
! 95: }
! 96: }
! 97:
! 98: void
! 99: ServerHost::deactivate()
! 100: {
! 101: assert(!active());
! 102: if (is_connected())
! 103: { timeval t;
! 104: (void) gettimeofday(&t, NULL);
! 105: t.tv_sec += Pollster::interval();
! 106: Scheduler::install_onetime_task(t, timeout_task, this);
! 107: }
! 108: else
! 109: { connector.deactivate();
! 110: Pollster::forget(this);
! 111: }
! 112: }
! 113:
! 114: void
! 115: ServerHost::timeout_task(void *closure)
! 116: {
! 117: ServerHost *host = (ServerHost *) closure;
! 118: assert(host->is_connected());
! 119: Log::debug("disconnecting from server fam@%s "
! 120: "after %d seconds of inactivity",
! 121: host->name(), Pollster::interval());
! 122: delete host->connection;
! 123: host->connection = NULL;
! 124: }
! 125:
! 126: //////////////////////////////////////////////////////////////////////////////
! 127: // Connection. Both connection and disconnection are async. events.
! 128:
! 129: void
! 130: ServerHost::connect_handler(int fd, void *closure)
! 131: {
! 132: ServerHost *host = (ServerHost *) closure;
! 133: assert(host->active());
! 134: assert(!host->is_connected());
! 135: host->connection = new ServerConnection(fd, event_handler,
! 136: disconnect_handler, host);
! 137: Pollster::forget(host);
! 138:
! 139: // Tell the server's fam who we are.
! 140:
! 141: char myname[MAXHOSTNAMELEN + 11];
! 142: (void) strcpy(myname, "client fam@");
! 143: int rc = gethostname(myname + 11, sizeof myname - 11);
! 144: assert(rc == 0);
! 145: host->connection->send_name(myname);
! 146: Log::debug("connected to server fam@%s", host->name());
! 147:
! 148: // Tell the server's fam about existing requests.
! 149:
! 150: for (Request r = host->requests.first(); r; r = host->requests.next(r))
! 151: { ClientInterest *ci = host->requests.find(r);
! 152: char remote_path[PATH_MAX];
! 153: ci->filesystem()->hl_map_path(remote_path, ci->name(), ci->cred());
! 154: host->connection->send_monitor(ci->type(), r, remote_path, ci->cred());
! 155: Log::debug("told server fam@%s: request %d monitor file \"%s\"",
! 156: host->name(), r, remote_path);
! 157: if (!ci->active())
! 158: host->send_suspend(r);
! 159: }
! 160: }
! 161:
! 162: void
! 163: ServerHost::disconnect_handler(void *closure)
! 164: {
! 165: ServerHost *host = (ServerHost *) closure;
! 166: Log::debug("lost connection to server fam@%s", host->name());
! 167: if (host->active()) {
! 168: assert(host->is_connected());
! 169: delete host->connection;
! 170: host->connection = NULL;
! 171: Pollster::watch(host);
! 172: host->connector.activate(); // Try to reconnect.
! 173: } else {
! 174: // We're in the timeout period waiting to close the
! 175: // connection. Remove the timeout callback and don't poll
! 176: // this host
! 177: Scheduler::remove_onetime_task(timeout_task, host);
! 178: delete host->connection;
! 179: host->connection = NULL;
! 180: }
! 181: }
! 182:
! 183: ///////////////////////////////////////////////////////////////////////////////
! 184: // Input. For every event of interest, we perform an immediate scan
! 185: // and then we queue a second scan to be done later on. This is
! 186: // necessary because NFS caches attribute information and we have to
! 187: // wait for old cached info to be out of date.
! 188: //
! 189: // For "Changed" events, we save the path because we need it to look
! 190: // up a DirEntry. We don't save the result of the first lookup
! 191: // because result may become invalid while we're sleeping.
! 192: //
! 193: // There is no way to avoid the deferred scan short of adding a
! 194: // hook in the kernel to invalidate an entry in the attribute cache.
! 195:
! 196: void
! 197: ServerHost::event_handler(const Event* event, Request r,
! 198: const char *path, void *closure)
! 199: {
! 200: ServerHost *host = (ServerHost *) closure;
! 201: Log::debug("server fam@%s said request %d \"%s\" %s",
! 202: host->name(), r, path, event->name());
! 203:
! 204: // If we care about this event, tell the ClientInterest to scan itself.
! 205: // Also enqueue a deferred task to rescan later.
! 206:
! 207: if (*event == Event::Changed || *event == Event::Deleted ||
! 208: *event == Event::Created || *event == Event::Exists)
! 209: {
! 210: ClientInterest *cip = host->requests.find(r);
! 211: if (!cip)
! 212: return;
! 213: Interest *ip;
! 214:
! 215: if (*event == Event::Changed || *event == Event::Deleted)
! 216: { ip = cip->find_name(path);
! 217: if (!ip)
! 218: return;
! 219: }
! 220: else
! 221: { ip = cip;
! 222: path = NULL;
! 223: }
! 224: ip->scan();
! 225: int wait = cip->filesystem()->get_attr_cache_timeout();
! 226: if (wait > 0) {
! 227: host->defer_scan(wait < RETRY_INTERVAL ?
! 228: wait :
! 229: RETRY_INTERVAL,(int)((wait-1)/RETRY_INTERVAL),
! 230: r, path);
! 231: }
! 232: }
! 233: }
! 234:
! 235: inline
! 236: ServerHost::DeferredScan::DeferredScan(int then, int rtrys, Request r, const char *s)
! 237: : when(then), retries(rtrys), next(NULL), myrequest(r)
! 238: {
! 239: assert(!s || strlen(s) < sizeof mypath);
! 240: if (s)
! 241: {
! 242: strncpy(mypath, s, (sizeof mypath) - 1);
! 243: mypath[(sizeof mypath) - 1] = '\0';
! 244: }
! 245: else
! 246: mypath[0] = '\0';
! 247: }
! 248:
! 249: void
! 250: ServerHost::defer_scan(int when, int retries, Request r, const char *path)
! 251: {
! 252: timeval t;
! 253: (void) gettimeofday(&t, NULL);
! 254: int then = t.tv_sec + when + 1;
! 255:
! 256: DeferredScan *ds = new DeferredScan(then, retries, r, path);
! 257: // In most cases, our new element will go either
! 258: // at the beginning or end of the list.
! 259: if ((deferred_scans == NULL) || (deferred_scans->when >= then))
! 260: {
! 261: ds->next = deferred_scans;
! 262: deferred_scans = ds;
! 263: if (last == NULL) last = ds;
! 264: }
! 265: else if (last->when <= then)
! 266: {
! 267: last = last->next = ds;
! 268: }
! 269: else
! 270: {
! 271: // It's in the middle after all. Put it in the right spot.
! 272: DeferredScan *prev = deferred_scans;
! 273: while ((prev->next != NULL) && (prev->next->when < then))
! 274: {
! 275: prev = prev->next;
! 276: }
! 277: ds->next = prev->next;
! 278: prev->next = ds;
! 279: // last still points to an element after ds.
! 280: }
! 281:
! 282: // If this new request needs to happen before our previously-first task,
! 283: // or we didn't have any task scheduled at all, tell the scheduler.
! 284: if (!min_time || then < min_time)
! 285: { if (min_time)
! 286: Scheduler::remove_onetime_task(deferred_scan_task, this);
! 287: min_time = then;
! 288: timeval t = { then, 0 };
! 289: Scheduler::install_onetime_task(t, deferred_scan_task, this);
! 290: }
! 291: }
! 292:
! 293: void
! 294: ServerHost::deferred_scan_task(void *closure)
! 295: {
! 296: ServerHost *host = (ServerHost *) closure;
! 297: int& min_time = host->min_time;
! 298: DeferredScan *ds = host->deferred_scans, *dsp;
! 299: assert(ds);
! 300: if(ds == NULL) return;
! 301:
! 302: bool changed;
! 303:
! 304: timeval t;
! 305: (void) gettimeofday(&t, NULL);
! 306: while ((ds != NULL) && (ds->when <= t.tv_sec))
! 307: {
! 308: ClientInterest *cip = host->requests.find(ds->request());
! 309: if (cip)
! 310: {
! 311: Interest *ip = ds->path() ? cip->find_name(ds->path()) : cip;
! 312: if (ip)
! 313: {
! 314: Log::debug("Handing a defered scan on: %s (Request %i)",
! 315: ip->name(), ds->request());
! 316: changed = ip->scan();
! 317: if (!changed) {
! 318: if (ds->retries > 0) {
! 319: Log::debug(
! 320: "Nothing changed, so rescheduling for %i seconds "
! 321: "(%i retries left)", RETRY_INTERVAL, ds->retries-1);
! 322: host->defer_scan(RETRY_INTERVAL, ds->retries-1, ds->request(), ds->path());
! 323: }
! 324: }
! 325: }
! 326: }
! 327: dsp = host->deferred_scans;
! 328: ds = host->deferred_scans = ds->next;
! 329: if (ds == NULL) host->last = NULL;
! 330: delete dsp;
! 331: }
! 332: if (ds != NULL)
! 333: {
! 334: // We still have some deferred scans which need to happen later.
! 335: min_time = ds->when;
! 336: timeval t = { ds->when, 0 };
! 337: Scheduler::install_onetime_task(t, deferred_scan_task, host);
! 338: }
! 339: else min_time = 0;
! 340: }
! 341:
! 342: ///////////////////////////////////////////////////////////////////////////////
! 343: // Output
! 344:
! 345: Request
! 346: ServerHost::send_monitor(ClientInterest *ci,
! 347: ClientInterest::Type type,
! 348: const char *remote_path)
! 349: {
! 350: if (!active())
! 351: activate();
! 352:
! 353: // Find a unique request number.
! 354:
! 355: Request r = unique_request++;
! 356: assert(!requests.find(r));
! 357:
! 358: // Send the monitor message to the remote fam.
! 359:
! 360: if (is_connected())
! 361: { connection->send_monitor(type, r, remote_path, ci->cred());
! 362: Log::debug("told server fam@%s: request %d monitor file \"%s\"",
! 363: name(), r, remote_path);
! 364: }
! 365:
! 366: // Store the request number in the request table.
! 367:
! 368: requests.insert(r, ci);
! 369: return r;
! 370: }
! 371:
! 372: void
! 373: ServerHost::send_cancel(Request r)
! 374: {
! 375: assert(requests.find(r));
! 376: if (connection)
! 377: { connection->send_cancel(r);
! 378: Log::debug("told server fam@%s: cancel request %d", name(), r);
! 379: }
! 380: requests.remove(r);
! 381: if (requests.size() == 0)
! 382: deactivate();
! 383: }
! 384:
! 385: void
! 386: ServerHost::send_suspend(Request r)
! 387: {
! 388: if (connection)
! 389: { connection->send_suspend(r);
! 390: Log::debug("told server fam@%s: suspend request %d", name(), r);
! 391: }
! 392: }
! 393:
! 394: void
! 395: ServerHost::send_resume(Request r)
! 396: {
! 397: if (connection)
! 398: { connection->send_resume(r);
! 399: Log::debug("told server fam@%s: resume request %d", name(), r);
! 400: }
! 401: }
! 402:
! 403: //////////////////////////////////////////////////////////////////////////////
! 404: // Polling
! 405:
! 406: void
! 407: ServerHost::poll()
! 408: {
! 409: for (Request r = requests.first(); r; r = requests.next(r))
! 410: requests.find(r)->poll();
! 411: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>