Annotation of fam/libfam/Client.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.1 of the GNU Lesser General Public License
! 5: // as 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 Lesser
! 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 Lesser
! 17: // General 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 <stdio.h>
! 24: #include <stdlib.h>
! 25: #include <unistd.h>
! 26: #include <netinet/in.h>
! 27: #include <sys/un.h>
! 28: #include <sys/socket.h>
! 29: #include <rpc/rpc.h>
! 30: #include <rpc/pmap_clnt.h>
! 31: #include <rpc/pmap_prot.h>
! 32: #include <string.h> // for memset
! 33: #include <ctype.h>
! 34: #include <syslog.h>
! 35: #include <errno.h>
! 36:
! 37: #include <iostream.h>
! 38:
! 39: #include "fam.h"
! 40: #include "Client.h"
! 41:
! 42: static void getword(const char *p, u_int32_t *l);
! 43:
! 44: Client::Client(long host, unsigned int prog, int vers)
! 45: : sock(0), haveCompleteEvent(false), userData(NULL), endExist(NULL),
! 46: inend(inbuf)
! 47: {
! 48: struct sockaddr_in sin;
! 49:
! 50: memset(&sin, 0, sizeof sin);
! 51: sin.sin_family = AF_INET;
! 52: sin.sin_addr.s_addr = htonl(host);
! 53: // This is set below instead.
! 54: //sin.sin_port = htons(pmap_getport(&sin, prog, vers, IPPROTO_TCP));
! 55:
! 56: //
! 57: // We'll run through the list of pmaps ourselves instead of calling
! 58: // pmap_getport, because pmap_getport will give you the port for a
! 59: // version 1 service even when you ask for version 2, and we really
! 60: // need to know which version of fam we're talking to. (Isn't there
! 61: // an easier way to do that?)
! 62: //
! 63: pmaplist *pl = pmap_getmaps(&sin); // this is leaked; see note below loop
! 64: unsigned long bestvers = 0;
! 65: for (pmaplist *plp = pl; plp != NULL; plp = plp->pml_next)
! 66: {
! 67: if ((plp->pml_map.pm_prog == prog) &&
! 68: (plp->pml_map.pm_prot == IPPROTO_TCP))
! 69: {
! 70: if (plp->pml_map.pm_vers > bestvers)
! 71: {
! 72: bestvers = plp->pml_map.pm_vers;
! 73: sin.sin_port = htons((unsigned short)(plp->pml_map.pm_port));
! 74: if (bestvers == vers)
! 75: {
! 76: break;
! 77: }
! 78: }
! 79: }
! 80: }
! 81: // We can't call clnt_freeres because we don't have the client, and
! 82: // Purify says this xdr_free gives a bunch of UMR & UMW's, so we'll
! 83: // just leak it. This sucks! (call CLNT_CALL(client, PMAPPROC_DUMP, ...
! 84: // ourselves?)
! 85: //xdr_free((xdrproc_t)xdr_pmaplist, &pl);
! 86:
! 87: if(sin.sin_port == 0)
! 88: {
! 89: // Couldn't get port for rpc call.
! 90: sock = -1;
! 91: return;
! 92: }
! 93: int insock = socket(PF_INET, SOCK_STREAM, 0);
! 94: if (insock < 0)
! 95: {
! 96: sock = -2;
! 97: return;
! 98: }
! 99: if (connect(insock, (const struct sockaddr *)&sin, sizeof(sin)) < 0)
! 100: {
! 101: close(insock);
! 102: sock = -3;
! 103: return;
! 104: }
! 105:
! 106: // If we're version 1, we're going to use the inet socket for
! 107: // communicating with fam, so we're done.
! 108: if (bestvers == 1)
! 109: {
! 110: sock = insock;
! 111: return;
! 112: }
! 113:
! 114: // If we're still here, we want to request a unix domain socket from
! 115: // fam, and use that for communicating. The "N" message with a group
! 116: // list (sort of) is how we tell fam we're version 2.
! 117: char msg[200];
! 118: snprintf(msg + sizeof(u_int32_t), sizeof(msg) - sizeof(u_int32_t),
! 119: "N0 %d %d sockmeister%c0\n", geteuid(), getegid(), '\0');
! 120: int len = strlen(msg + sizeof(u_int32_t)) + 1;
! 121: len += (strlen(msg + sizeof(u_int32_t) + len) + 1);
! 122: u_int32_t nmsglen = htonl(len);
! 123: memcpy(msg, &nmsglen, sizeof(u_int32_t));
! 124: len += sizeof(u_int32_t);
! 125: if (write(insock, msg, len) != len)
! 126: {
! 127: close(insock);
! 128: sock = -6;
! 129: return;
! 130: }
! 131:
! 132: struct sockaddr_un sun;
! 133: memset(&sun, 0, sizeof sun);
! 134: sun.sun_family = AF_UNIX;
! 135:
! 136: // We will block here, waiting for response from fam.
! 137: unsigned int nread = 0;
! 138: char inbuf[sizeof(sun.sun_path)];
! 139: while(nread < sizeof(u_int32_t))
! 140: {
! 141: int rv = read(insock, inbuf + nread, sizeof(u_int32_t) - nread);
! 142: if (rv <= 0)
! 143: {
! 144: close(insock);
! 145: sock = -7;
! 146: return;
! 147: }
! 148: nread += rv;
! 149: }
! 150: u_int32_t mlen;
! 151: memcpy(&mlen, inbuf, sizeof(mlen));
! 152: mlen = ntohl(mlen);
! 153: if (mlen >= sizeof(sun.sun_path))
! 154: {
! 155: close(insock);
! 156: sock = -8;
! 157: return;
! 158: }
! 159:
! 160: nread = 0;
! 161: while (nread < mlen)
! 162: {
! 163: int rv = read(insock, inbuf + nread, mlen - nread);
! 164: if (rv <= 0)
! 165: {
! 166: close(insock);
! 167: sock = -9;
! 168: return;
! 169: }
! 170: nread += rv;
! 171: }
! 172: strncpy(sun.sun_path, inbuf, mlen);
! 173: sun.sun_path[mlen] = '\0';
! 174:
! 175: // When we connected to the inet socket and told fam our UID, fam
! 176: // created a new UNIX domain socket and sent its name to us. Now
! 177: // let's connect on that socket and return.
! 178: sock = socket(PF_UNIX, SOCK_STREAM, 0);
! 179: if (sock < 0)
! 180: {
! 181: close(insock);
! 182: sock = -10;
! 183: return;
! 184: }
! 185: if (connect(sock, (const struct sockaddr *)&sun, sizeof(sun)) < 0)
! 186: {
! 187: close(sock);
! 188: close(insock);
! 189: sock = -11;
! 190: return;
! 191: }
! 192: // all done on the inet sock
! 193: close(insock);
! 194: }
! 195:
! 196: Client::~Client()
! 197: {
! 198: if(sock >= 0) close(sock);
! 199: if(userData != NULL) delete userData;
! 200: if(endExist != NULL) delete endExist;
! 201: }
! 202:
! 203: int
! 204: Client::writeToServer(char *buf, int nbytes)
! 205: {
! 206: if (!connected()) return -1;
! 207: char msgHeader[sizeof(u_int32_t)];
! 208: nbytes = htonl(nbytes);
! 209: memcpy(msgHeader, &nbytes, sizeof(u_int32_t));
! 210: nbytes = ntohl(nbytes);
! 211: if(write(sock, msgHeader, sizeof(u_int32_t)) != sizeof(u_int32_t))
! 212: {
! 213: return -1;
! 214: }
! 215: return write(sock, buf, nbytes);
! 216: }
! 217:
! 218: int
! 219: Client::eventPending()
! 220: {
! 221: if (readEvent(false) < 0) return 1; // EOF or error
! 222: return haveCompleteEvent ? 1 : 0;
! 223: }
! 224:
! 225: int
! 226: Client::nextEvent(FAMEvent *fe)
! 227: {
! 228: if (!connected()) return -1;
! 229: if ((!haveCompleteEvent) && (readEvent(true) < 0))
! 230: {
! 231: // EOF now
! 232: return -1;
! 233: }
! 234: // readEvent(true) blocks until we have a complete event or EOF.
! 235:
! 236: u_int32_t msglen;
! 237: getword(inbuf, &msglen);
! 238:
! 239: char *p = inbuf + sizeof (u_int32_t), *q;
! 240: int limit;
! 241: //
! 242: char code, changeInfo[100];
! 243: int reqnum;
! 244:
! 245: code = *p++;
! 246: reqnum = strtol(p, &q, 10);
! 247: if (p == q)
! 248: {
! 249: croakConnection("Couldn't find reqnum in message!");
! 250: return -1;
! 251: }
! 252: // XXX it would be nice to make sure reqnum is valid, but we only store
! 253: // it if they gave us user data or if it needs an endexists message
! 254: fe->fr.reqnum = reqnum;
! 255: fe->userdata = getUserData(reqnum);
! 256: p = q;
! 257: p++;
! 258: if (code == 'c')
! 259: {
! 260: q = changeInfo;
! 261: limit = sizeof(changeInfo);
! 262: while ((*p != '\0') && (!isspace(*p)) && (--limit)) *q++ = *p++;
! 263: if (!limit)
! 264: {
! 265: char msg[100];
! 266: snprintf(msg, sizeof(msg),
! 267: "change info too long! (%d max)", sizeof(changeInfo));
! 268: croakConnection(msg);
! 269: return -1;
! 270: }
! 271: *q = '\0';
! 272: while (isspace(*p)) ++p;
! 273: }
! 274:
! 275: q = fe->filename;
! 276: limit = PATH_MAX;
! 277: while ((*p != '\0') && (*p != '\n') && (--limit)) *q++ = *p++;
! 278: if (!limit)
! 279: {
! 280: char msg[100];
! 281: snprintf(msg, sizeof(msg), "path too long! (%d max)", PATH_MAX);
! 282: croakConnection(msg);
! 283: return -1;
! 284: }
! 285: *q = '\0';
! 286:
! 287: switch (code) {
! 288: case 'c': // change
! 289: fe->code = FAMChanged;
! 290: break;
! 291: case 'A': // delete
! 292: fe->code = FAMDeleted;
! 293: break;
! 294: case 'X': // start execute
! 295: fe->code = FAMStartExecuting;
! 296: break;
! 297: case 'Q': // quit execute
! 298: fe->code = FAMStopExecuting;
! 299: break;
! 300: case 'F':
! 301: fe->code = getEndExist(reqnum) ? FAMCreated : FAMExists;
! 302: break;
! 303: case 'G':
! 304: // XXX we should be able to free the user data here
! 305: freeRequest(reqnum);
! 306: fe->code = FAMAcknowledge;
! 307: break;
! 308: case 'e': // new - XXX what about exists ?
! 309: fe->code = getEndExist(reqnum) ? FAMCreated : FAMExists;
! 310: break;
! 311: case 'P':
! 312: fe->code = FAMEndExist;
! 313: storeEndExist(reqnum);
! 314: break;
! 315: default:
! 316: snprintf(changeInfo, sizeof(changeInfo),
! 317: "unrecognized code '%c'!", code);
! 318: croakConnection(changeInfo);
! 319: return -1;
! 320: }
! 321: #ifdef DEBUG
! 322: printf("\nFAM received %s ", msg);
! 323: printf("translated to event code:%d, reqnum:%d, ud:%d, filename:<%s>\n",
! 324: fe->code, reqnum, fe->userdata, fe->filename);
! 325: #endif
! 326:
! 327: // Now that we've copied the contents out of this message, slide the
! 328: // contents of the buffer over. Crude, but easier than letting the
! 329: // buffer wrap.
! 330: msglen += sizeof(u_int32_t); // include the size now; less math
! 331: memmove(inbuf, inbuf + msglen, inend - inbuf - msglen);
! 332: inend -= msglen;
! 333: checkBufferForEvent();
! 334:
! 335: return 1;
! 336: }
! 337:
! 338: void
! 339: Client::storeUserData(int reqnum, void *p)
! 340: {
! 341: if(p == NULL) return;
! 342: if(userData == NULL) userData = new BTree<int, void *>();
! 343: userData->insert(reqnum, p);
! 344: }
! 345:
! 346: void *
! 347: Client::getUserData(int reqnum)
! 348: {
! 349: if(userData == NULL) return NULL;
! 350: return userData->find(reqnum);
! 351: }
! 352:
! 353: void
! 354: Client::storeEndExist(int reqnum)
! 355: {
! 356: // It's actually kind of dumb to use a BTree for this, since the only
! 357: // value we ever store will be "true", but access should be fast.
! 358: // I'm not sure whether or not we'll tend to have a lot of requests
! 359: // stored at a time, though.
! 360: if(endExist == NULL) endExist = new BTree<int, bool>();
! 361: endExist->insert(reqnum, true);
! 362: }
! 363:
! 364: bool
! 365: Client::getEndExist(int reqnum)
! 366: {
! 367: if(endExist == NULL) return false;
! 368: return endExist->find(reqnum);
! 369: }
! 370:
! 371: void
! 372: Client::freeRequest(int reqnum)
! 373: {
! 374: if(userData != NULL) userData->remove(reqnum);
! 375: if(endExist != NULL) endExist->remove(reqnum);
! 376: }
! 377:
! 378: int
! 379: Client::readEvent(bool block)
! 380: {
! 381: if (!connected()) return -1;
! 382: if (haveCompleteEvent) return 0;
! 383:
! 384: if (!block)
! 385: {
! 386: fd_set tfds;
! 387: struct timeval tv = { 0, 0 };
! 388: FD_ZERO(&tfds);
! 389: FD_SET(sock, &tfds);
! 390: if (select(sock + 1, &tfds, NULL, NULL, &tv) < 1) return 0;
! 391: }
! 392:
! 393: do
! 394: {
! 395: int rc = read(sock, inend, MSGBUFSIZ - (inend - inbuf));
! 396: if (rc <= 0) return -1; // EOF now
! 397: inend += rc;
! 398: checkBufferForEvent();
! 399: } while (block && !haveCompleteEvent);
! 400:
! 401: return 0;
! 402: }
! 403:
! 404: void
! 405: Client::checkBufferForEvent()
! 406: {
! 407: if (!connected()) return;
! 408: haveCompleteEvent = false;
! 409: u_int32_t msglen = 0;
! 410: if ((inend - inbuf) <= (int)sizeof(u_int32_t)) return;
! 411: getword(inbuf, &msglen);
! 412: if((msglen == 0) || (msglen > MAXMSGSIZ))
! 413: {
! 414: char msg[100];
! 415: snprintf(msg, sizeof(msg), "bad message size! (%d max)", MAXMSGSIZ);
! 416: croakConnection(msg);
! 417: return;
! 418: }
! 419:
! 420: if (inend - inbuf >= (int)msglen + (int)sizeof(u_int32_t))
! 421: {
! 422: haveCompleteEvent = true;
! 423: }
! 424: }
! 425:
! 426: void
! 427: Client::croakConnection(const char *reason)
! 428: {
! 429: if (!connected()) return;
! 430: syslog(LOG_ERR, "libfam killing connection: %s", reason);
! 431: close(sock);
! 432: sock = -1;
! 433: haveCompleteEvent = false;
! 434: }
! 435:
! 436:
! 437: static void
! 438: getword(const char *p, u_int32_t *l)
! 439: {
! 440: memcpy(l, p, sizeof(u_int32_t));
! 441: *l = ntohl(*l);
! 442: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>