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>