[BACK]Return to Client.c++ CVS log [TXT][DIR] Up to [Development] / fam / libfam

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>