[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

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>