forest-net
an overlay networks for large-scale virtual worlds
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
ClientMgr.cpp
Go to the documentation of this file.
1 
11 #include "ClientMgr.h"
12 #include <map>
13 #include <iostream>
14 #include <fstream>
15 #include <sstream>
16 
17 using namespace forest;
18 
27 int main(int argc, char *argv[]) {
28  ipa_t nmIp, myIp; uint32_t finTime = 0;
29 
30  if(argc != 4 ||
31  (nmIp = Np4d::ipAddress(argv[1])) == 0 ||
32  (myIp = Np4d::ipAddress(argv[2])) == 0 ||
33  (sscanf(argv[3],"%d", &finTime)) != 1)
34  fatal("usage: ClientMgr nmIp myIp fintime");
35 
36  if (!init(nmIp, myIp))
37  fatal("init: Failed to initialize ClientMgr");
38 
39  sub->run(finTime);
40 
41  return 0;
42 }
43 
44 namespace forest {
45 
51 bool init(ipa_t nmIp1, ipa_t myIp1) {
52  nmIp = nmIp1; myIp = myIp1;
53 
54  ps = new PacketStoreTs(10000);
55  cliTbl = new ClientTable(10000,30000);
56  logger = new Logger();
57 
58  if (!cliTbl->init()) {
59  logger->log("ClientMgr::init: could not initialize "
60  "client data",2);
61  return false;
62  }
63 
64  uint64_t nonce;
65  if (!bootMe(nmIp, myIp, nmAdr, myAdr, rtrAdr, rtrIp, rtrPort, nonce)) {
66  return false;
67  }
68 
69  sub = new Substrate(myAdr, myIp, rtrAdr, rtrIp, rtrPort, nonce,
71  if (!sub->init()) return false;
72  sub->setRtrReady(true);
73 
74  dummyRecord = 0; maxRecord = 0;
75 
76  // read clientData file
77  clientFile.open("clientData");
78  if (!clientFile.good() || !cliTbl->read(clientFile)) {
79  logger->log("ClientMgr::init: could not read clientData "
80  "file",2);
81  return false;
82  }
83  clientFile.clear();
84 
85  // if size of file is not equal to count*RECORD_SIZE
86  // re-write the file using padded records
87  int n = cliTbl->getMaxClx();
88  clientFile.seekp(0,ios_base::end);
89  long len = clientFile.tellp();
90  if (len != (n+1)*RECORD_SIZE) {
91  for (int clx = 0; clx <= n; clx++) writeRecord(clx);
92  }
93  if (pthread_mutex_init(&clientFileLock,NULL) != 0) {
94  logger->log("ClientMgr::init: could not initialize lock "
95  "on client data file",2);
96  return false;
97  }
98 
99  acctFile.open("acctRecords",fstream::app);
100  if (!acctFile.good()) {
101  logger->log("ClientMgr::init: could not open acctRecords "
102  "file",2);
103  return false;
104  }
105  return true;
106 }
107 
124 bool bootMe(ipa_t nmIp, ipa_t myIp, fAdr_t& nmAdr, fAdr_t& myAdr,
125  fAdr_t& rtrAdr, ipa_t& rtrIp, ipp_t& rtrPort, uint64_t& nonce) {
126 
127  // open boot socket
128  int bootSock = Np4d::datagramSocket();
129  if (bootSock < 0) return false;
130  if (!Np4d::bind4d(bootSock,myIp,0) || !Np4d::nonblock(bootSock)) {
131  close(bootSock);
132  return false;
133  }
134 
135  // setup boot leaf packet to net manager
136  Packet p; buffer_t buf1; p.buffer = &buf1;
137  CtlPkt cp(CtlPkt::BOOT_LEAF,CtlPkt::REQUEST,1,p.payload());
138  int plen = cp.pack();
139  if (plen == 0) { close(bootSock); return false; }
141  p.flags = 0; p.srcAdr = p.dstAdr = 0; p.comtree = Forest::NET_SIG_COMT;
142  p.pack();
143 
144  // setup reply packet
145  Packet reply; buffer_t buf2; reply.buffer = &buf2;
146  CtlPkt repCp;
147 
148  int resendTime = Misc::getTime();
149  ipa_t srcIp; ipp_t srcPort;
150  while (true) {
151  int now = Misc::getTime();
152  if (now > resendTime) {
153  if (Np4d::sendto4d(bootSock,(void *) p.buffer, p.length,
154  nmIp,Forest::NM_PORT) == -1) {
155  close(bootSock); return false;
156  }
157  resendTime += 1000000; // retry after 1 second
158  }
159  usleep(10000);
160  int nbytes = Np4d::recvfrom4d(bootSock,reply.buffer,1500,
161  srcIp, srcPort);
162  if (nbytes < 0) continue;
163  reply.unpack();
164 
165  // do some checking
166  if (srcIp != nmIp || reply.type != Forest::NET_SIG) {
167  logger->log("unexpected response to boot request",
168  2,reply);
169  close(bootSock); return false;
170  }
171  repCp.reset(reply);
172  if (repCp.type != CtlPkt::CONFIG_LEAF ||
173  repCp.mode != CtlPkt::REQUEST) {
174  logger->log("unexpected response from NetMgr",2,reply);
175  close(bootSock); return false;
176  }
177 
178  // unpack data from packet
179  myAdr = repCp.adr1; rtrAdr = repCp.adr2;
180  rtrIp = repCp.ip1; rtrPort = repCp.port1;
181  nonce = repCp.nonce;
182  nmAdr = reply.srcAdr;
183 
184  // send positive reply
185  repCp.reset(CtlPkt::CONFIG_LEAF,CtlPkt::POS_REPLY,repCp.seqNum,
186  repCp.payload);
187  plen = repCp.pack();
188  if (plen == 0) { close(bootSock); return false; }
189  reply.length = Forest::OVERHEAD + plen;
190  reply.srcAdr = myAdr; reply.dstAdr = nmAdr;
191  reply.pack();
192  if (Np4d::sendto4d(bootSock,(void *) reply.buffer, reply.length,
193  nmIp,Forest::NM_PORT) == -1) {
194  close(bootSock); return false;
195  }
196  break; // proceed to next step
197  }
198  // we have the configuration information, now just wait for
199  // final ack
200  while (true) {
201  int now = Misc::getTime();
202  if (now > resendTime) {
203  if (Np4d::sendto4d(bootSock,(void *) p.buffer, p.length,
204  nmIp,Forest::NM_PORT) == -1) {
205  close(bootSock); return false;
206  }
207  resendTime += 1000000; // retry after 1 second
208  }
209  int nbytes = Np4d::recvfrom4d(bootSock,reply.buffer,1500,
210  srcIp, srcPort);
211  if (nbytes < 0) { usleep(100000); continue; }
212  reply.unpack();
213  if (srcIp != nmIp || reply.type != Forest::NET_SIG) {
214  logger->log("unexpected response to boot request",
215  2,reply);
216  close(bootSock); return false;
217  }
218  // do some checking
219  repCp.reset(reply);
220  if (repCp.type == CtlPkt::CONFIG_LEAF &&
221  repCp.mode == CtlPkt::REQUEST) {
222  // our reply must have been lost, send it again
223  repCp.reset(CtlPkt::CONFIG_LEAF,CtlPkt::POS_REPLY,
224  repCp.seqNum, repCp.payload);
225  plen = repCp.pack();
226  if (plen == 0) { close(bootSock); return false; }
227  reply.length = Forest::OVERHEAD + plen;
228  reply.srcAdr = myAdr; reply.dstAdr = nmAdr;
229  reply.pack();
230  if (Np4d::sendto4d(bootSock,(void *) reply.buffer,
231  reply.length,nmIp,Forest::NM_PORT)
232  == -1) {
233  close(bootSock); return false;
234  }
235  } else if (repCp.type != CtlPkt::BOOT_LEAF ||
236  repCp.mode != CtlPkt::POS_REPLY) {
237  logger->log("unexpected response from NetMgr",
238  2,reply);
239  close(bootSock); return false;
240  }
241  break; // success
242  }
243  close(bootSock); return true;
244 }
245 
255 void* handler(void *qp) {
256  Queue& inq = ((Substrate::QueuePair *) qp)->in;
257  Queue& outq = ((Substrate::QueuePair *) qp)->out;
258  CpHandler cph(&inq, &outq, myAdr, logger, ps);
259 
260  while (true) {
261  pktx px = inq.deq();
262  bool success = false;
263  if (px < 0) {
264  // in this case, p is really a negated socket number
265  // for a remote client
266  int sock = -px;
267  success = handleClient(sock, cph);
268  close(sock);
269  } else {
270  Packet& p = ps->getPacket(px);
271  string s;
272  cerr << "handler, got packet " << p.toString(s) << endl;
273  CtlPkt cp(p);
274  switch (cp.type) {
275  case CtlPkt::CLIENT_CONNECT:
276  case CtlPkt::CLIENT_DISCONNECT:
277  success = handleConnDisc(px, cp, cph);
278  break;
279  default:
280  cph.errReply(px,cp,"invalid control packet "
281  "type for ClientMgr");
282  break;
283  }
284  if (!success) {
285  string s;
286  cerr << "handler: operation failed\n"
287  << p.toString(s);
288  }
289  }
290  ps->free(px); // release px now that we're done
291  outq.enq(0); // signal completion to main thread
292  }
293 }
294 
303 bool handleClient(int sock,CpHandler& cph) {
304  NetBuffer buf(sock,1024);
305  string cmd, reply, clientName, type;
306  bool loggedIn;
307 
308  // verify initial "greeting"
309  if (!buf.readLine(cmd) || cmd != "Forest-login-v1") {
310  Np4d::sendString(sock,"misformatted user dialog\n"
311  "overAndOut\n");
312  return false;
313  }
314  // main processing loop for requests from client
315  loggedIn = false;
316  while (buf.readAlphas(cmd)) {
317  cerr << "cmd=" << cmd << endl;
318  reply = "success";
319  if (cmd == "over") {
320  // shouldn't happen, but ignore it, if it does
321  buf.nextLine(); continue;
322  } else if (cmd == "overAndOut") {
323  buf.nextLine(); return true;
324  } else if (cmd == "login") {
325  loggedIn = login(buf,clientName,reply);
326  } else if (cmd == "newAccount") {
327  loggedIn = newAccount(buf,clientName,reply);
328  } else if (!loggedIn) {
329  continue; // must login before anything else
330  } else if (cmd == "newSession") {
331  newSession(sock,cph,buf,clientName,reply);
332  } else if (cmd == "getProfile") {
333  getProfile(buf,clientName,reply);
334  } else if (cmd == "updateProfile") {
335  updateProfile(buf,clientName,reply);
336  } else if (cmd == "changePassword") {
337  changePassword(buf,clientName,reply);
338  } else if (cmd == "uploadPhoto") {
339  type = "photo";
340  uploadFile(type, sock,buf,clientName,reply);
341  } else if (cmd == "uploadAvatar") {
342  type = "avatar";
343  uploadFile(type, sock, buf,clientName,reply);
344  } else if (cmd == "uploadTexH"){
345  type = "hi_res";
346  uploadFile(type, sock, buf, clientName, reply);
347  } else if (cmd == "uploadTexM"){
348  type = "medium_res";
349  uploadFile(type, sock, buf, clientName, reply);
350  } else if (cmd == "getSessions") {
351  getSessions(buf,clientName,reply);
352  } else if (cmd == "cancelSession") {
353  cancelSession(buf,clientName,cph,reply);
354  } else if (cmd == "cancelAllSessions") {
355  cancelAllSessions(buf,clientName,cph,reply);
356  } else if (cmd == "addComtree" && buf.nextLine()) {
357  addComtree(buf,clientName,reply);
358  } else {
359  reply = "unrecognized input";
360  }
361 cerr << "sending reply: " << reply << endl;
362  reply += "\nover\n";
363  Np4d::sendString(sock,reply);
364  }
365  return true;
366 cerr << "terminating" << endl;
367 }
368 
378 bool login(NetBuffer& buf, string& clientName, string& reply) {
379  string pwd, s1, s2;
380  if (buf.verify(':') && buf.readName(clientName) && buf.nextLine() &&
381  buf.readAlphas(s1) && s1 == "password" &&
382  buf.verify(':') && buf.readWord(pwd) && buf.nextLine() &&
383  buf.readLine(s2) && s2 == "over") {
384  int clx =cliTbl->getClient(clientName);
385  // locks clx
386  if (clx == 0) {
387  reply = "login failed: try again";
388  return false;
389  } else if (cliTbl->checkPassword(clx,pwd)) {
390 cerr << "login succeeded " << clientName << endl;
391  cliTbl->releaseClient(clx);
392  return true;
393  } else {
394  cliTbl->releaseClient(clx);
395  reply = "login failed: try again";
396  return false;
397  }
398  } else {
399  reply = "misformatted login request";
400  return false;
401  }
402 }
403 
413 bool newAccount(NetBuffer& buf, string& clientName, string& reply) {
414  string pwd, s1, s2;
415  if (buf.verify(':') && buf.readName(clientName) && buf.nextLine() &&
416  buf.readAlphas(s1) && s1 == "password" &&
417  buf.verify(':') && buf.readWord(pwd) && buf.nextLine() &&
418  buf.readLine(s2) && s2 == "over") {
419  int clx =cliTbl->getClient(clientName);
420  // locks clx
421  if (clx != 0) {
422  cliTbl->releaseClient(clx);
423  reply = "name not available, select another";
424  return false;
425  }
426  clx = cliTbl->addClient(clientName,pwd,ClientTable::STANDARD);
427  if (clx != 0) {
428  writeRecord(clx);
429  cliTbl->releaseClient(clx);
430  return true;
431  } else {
432  reply = "unable to add client";
433  return false;
434  }
435  } else {
436  reply = "misformatted new account request";
437  return false;
438  }
439 }
440 
452 void newSession(int sock, CpHandler& cph, NetBuffer& buf,
453  string& clientName, string& reply) {
454  string s1;
455  RateSpec rs;
456  if (buf.verify(':')) {
457  if (!buf.readRspec(rs) || !buf.nextLine() ||
458  !buf.readLine(s1) || s1 != "over") {
459  reply = "misformatted new session request";
460  return;
461  }
462  } else {
463  if (!buf.nextLine() || !buf.readLine(s1) || s1 != "over") {
464  reply = "misformatted new session request";
465  return;
466  }
467  }
468  int clx = cliTbl->getClient(clientName);
469  if (clx == 0) {
470  reply = "cannot access client data(" + clientName + ")";
471  return;
472  }
473  if (!rs.isSet()) rs = cliTbl->getDefRates(clx);
474  // make sure we have sufficient capacity
475  if (!rs.leq(cliTbl->getAvailRates(clx))) {
476  cliTbl->releaseClient(clx);
477  reply = "session rate exceeds available capacity";
478  return;
479  }
480  cliTbl->getAvailRates(clx).subtract(rs);
481  cliTbl->releaseClient(clx);
482  // proceed with new session setup
483  CtlPkt repCp;
484  ipa_t clientIp = Np4d::getPeerIp(sock);
485  pktx rpx = cph.newSession(nmAdr, clientIp, rs, repCp);
486  clx = cliTbl->getClient(clientName);
487  if (clx == 0) {
488  reply = "cannot update client data(" + clientName + ")";
489  logger->log("ClientMgr::newSession: cannot update session data "
490  "session state may be inconsistent",2);
491  return;
492  }
493  if (rpx == 0) {
494  cliTbl->getAvailRates(clx).add(rs);
495  cliTbl->releaseClient(clx);
496  reply = "cannot setup session: NetMgr never responded";
497  return;
498  }
499  ps->free(rpx);
500  if (repCp.mode != CtlPkt::POS_REPLY) {
501  cliTbl->getAvailRates(clx).add(rs);
502  cliTbl->releaseClient(clx);
503  reply = "cannot setup new session: NetMgr failed (" +
504  repCp.errMsg + ")";
505  return;
506  }
507  int sess = cliTbl->addSession(repCp.adr1, repCp.adr2, clx);
508  if (sess == 0) {
509  cliTbl->getAvailRates(clx).add(rs);
510  cliTbl->releaseClient(clx);
511  reply = "cannot setup new session: could not create "
512  "session record";
513  return;
514  }
515 
516  // set session information
517  cliTbl->setClientIp(sess,clientIp);
518  cliTbl->setRouterAdr(sess,repCp.adr2);
519  cliTbl->setState(sess,ClientTable::PENDING);
521  cliTbl->getSessRates(sess) = rs;
522  cliTbl->releaseClient(clx);
523 
524  // output accounting record
525  writeAcctRecord(clientName,repCp.adr1,clientIp,repCp.adr2,NEWSESSION);
526 
527  // send information back to client
528  stringstream ss; string s;
529  ss << "yourAddress: " << Forest::fAdr2string(repCp.adr1,s) << endl;
530  ss << "yourRouter: (" << Np4d::ip2string(repCp.ip1,s)
531  << "," << repCp.port1 << ",";
532  ss << Forest::fAdr2string(repCp.adr2,s) << ")\n";
533  ss << "comtCtlAddress: " << Forest::fAdr2string(repCp.adr3,s) << endl;
534  ss << "connectNonce: " << repCp.nonce << "\noverAndOut\n";
535  reply = ss.str();
536 
537  ps->free(rpx); return;
538 }
539 
550 int checkPrivileges(string clientName, string targetName) {
551  // if names match, get and return client index (or 0 if no match)
552  if (targetName == clientName) return cliTbl->getClient(clientName);
553 
554  // get client's privileges and check if privileged
555  int clx = cliTbl->getClient(clientName);
556  if (clx == 0) return 0;
557  ClientTable::privileges priv = cliTbl->getPrivileges(clx);
558  cliTbl->releaseClient(clx);
559  if (priv != ClientTable::ADMIN && priv != ClientTable::ROOT) return 0;
560 
561  // get target's privileges
562  int tclx = cliTbl->getClient(targetName);
563  if (tclx == 0) return 0;
564  ClientTable::privileges tpriv = cliTbl->getPrivileges(tclx);
565  if (priv == ClientTable::ADMIN &&
566  (tpriv == ClientTable::ADMIN || tpriv == ClientTable::ROOT)) {
567  cliTbl->releaseClient(tclx); return 0;
568  }
569  return tclx;
570 }
571 
581 void getProfile(NetBuffer& buf, string& clientName, string& reply) {
582  string s, targetName;
583  if (!buf.verify(':') || !buf.readName(targetName) ||
584  !buf.nextLine() || !buf.readLine(s) || s != "over") {
585  reply = "misformatted get profile request"; return;
586  }
587  int tclx = checkPrivileges(clientName, targetName);
588  if (tclx == 0) {
589  reply = "insufficient privileges for requested operation";
590  return;
591  }
592  reply = "realName: \"" + cliTbl->getRealName(tclx) + "\"\n";
593  reply += "email: " + cliTbl->getEmail(tclx) + "\n";
594  reply += "defRates: " + cliTbl->getDefRates(tclx).toString(s) + "\n";
595  reply += "totalRates: " + cliTbl->getTotalRates(tclx).toString(s) +"\n";
596  cliTbl->releaseClient(tclx);
597 }
598 
608 void updateProfile(NetBuffer& buf, string& clientName, string& reply) {
609  string s, targetName;
610  if (!buf.verify(':') || !buf.readName(targetName) ||
611  !buf.nextLine()) {
612  reply = "misformatted updateProfile request"; return;
613  }
614  // read profile information from client
615  string item; RateSpec rates;
616  string realName, email; RateSpec defRates, totRates;
617  while (buf.readAlphas(item)) {
618  if (item == "realName" && buf.verify(':') &&
619  buf.readString(realName) && buf.nextLine()) {
620  // that's all for now
621  } else if (item == "email" && buf.verify(':') &&
622  buf.readWord(email) && buf.nextLine()) {
623  // that's all for now
624  } else if (item == "defRates" && buf.verify(':') &&
625  buf.readRspec(defRates) && buf.nextLine()) {
626  // that's all for now
627  } else if (item == "totalRates" && buf.verify(':') &&
628  buf.readRspec(totRates) && buf.nextLine()) {
629  // that's all for now
630  } else if (item == "over" && buf.nextLine()) {
631  break;
632  } else {
633  reply = "misformatted update profile request (" +
634  item + ")";
635  return;
636  }
637  }
638  int tclx = checkPrivileges(clientName, targetName);
639  if (tclx == 0) {
640  reply = "insufficient privileges for requested operation";
641  return;
642  }
643  if (realName.length() > 0) cliTbl->setRealName(tclx,realName);
644  if (email.length() > 0) cliTbl->setEmail(tclx,email);
645  ClientTable::privileges tpriv = cliTbl->getPrivileges(tclx);
646  if (tpriv != ClientTable::LIMITED) {
647  if (defRates.isSet() &&
648  defRates.leq(cliTbl->getTotalRates(tclx)))
649  cliTbl->getDefRates(tclx) = defRates;
650  if (totRates.isSet() &&
651  defRates.leq(cliTbl->getTotalRates(tclx)))
652  cliTbl->getTotalRates(tclx) = totRates;
653  }
654  writeRecord(tclx);
655  cliTbl->releaseClient(tclx);
656  return;
657 }
658 
668 void changePassword(NetBuffer& buf, string& clientName, string& reply) {
669  string targetName, pwd;
670  if (!buf.verify(':') || !buf.readName(targetName) ||
671  !buf.readWord(pwd) && buf.nextLine()) {
672  reply = "misformatted change password request"; return;
673  }
674  int tclx = checkPrivileges(clientName, targetName);
675  if (tclx == 0) {
676  reply = "insufficient privileges for requested operation";
677  return;
678  }
679  cliTbl->setPassword(tclx,pwd);
680  writeRecord(tclx);
681  cliTbl->releaseClient(tclx);
682  return;
683 }
684 
695 void uploadFile(string& type, int sock, NetBuffer& buf, string& clientName, string& reply) {
696  int length;
697  int limit;
698  string fileName;
699  if (!buf.verify(':') || !buf.readInt(length) || !buf.nextLine()) {
700  reply = "misformatted upload photo request"; return;
701  }
702  if (type == "photo"){
703  limit = 50000;
704  fileName = "clientPhotos/" + clientName + ".jpg";
705  }
706  else{
707  limit = 200000;
708  if(type == "avatar")
709  fileName = "clientAvatars/" + clientName + ".zip";
710  else if (type == "hi_res")
711  fileName = "clientTextures/" + clientName + ".png";
712  else// (type == "medium_res")
713  fileName = "clientTextures/" + clientName + ".jpg";
714  }
715 
716  if (length > limit) {
717  reply = "file exceeds byte limit"; return;
718  }
719 
720  int clx = cliTbl->getClient(clientName);
721  ofstream inFile;
722  inFile.open(fileName.c_str(), ofstream::binary);
723  if (!inFile.good()) {
724  reply = "cannot open file to write"; return;
725  }
726  cliTbl->releaseClient(clx);
727  Np4d::sendString(sock,"proceed\n");
728  char xbuf[1001];
729  int numRcvd = 0;
730  while (true) {
731  int n = buf.readBlock(xbuf,min(1000,length-numRcvd));
732  if (n <= 0) break;
733  inFile.write(xbuf,n);
734  numRcvd += n;
735  if (numRcvd == length) break;
736  }
737  if (numRcvd != length) {
738  reply = "could not transfer complete file"; return;
739  }
740  string s1, s2;
741  if (!buf.readLine(s1) || s1 != "upload finished" ||
742  !buf.readLine(s2) || s2 != "over") {
743  reply = "misformatted upload request"; return;
744  }
745  inFile.close();
746  return;
747 }
748 
755 void getSessions(NetBuffer& buf, string& clientName, string& reply) {
756  string s, targetName;
757  if (!buf.verify(':') || !buf.readName(targetName) ||
758  !buf.nextLine() || !buf.readLine(s) || s != "over") {
759  reply = "misformatted get sessions request"; return;
760  }
761  int tclx = checkPrivileges(clientName, targetName);
762  if (tclx == 0) {
763  reply = "insufficient privileges for requested operation";
764  return;
765  }
766  int sess = cliTbl->firstSession(tclx);
767  reply += "\n";
768  while (sess != 0) {
769  string sessString;
770  cliTbl->session2string(sess, sessString);
771  reply += sessString;
772  sess = cliTbl->nextSession(sess,tclx);
773  }
774  reply.erase(reply.end()-1);
775  cliTbl->releaseClient(tclx);
776  return;
777 }
778 
786 void cancelSession(NetBuffer& buf, string& clientName, CpHandler& cph,
787  string& reply) {
788  string s, targetName, cancelAdrStr;
789  if (!buf.verify(':') || !buf.readName(targetName) ||
790  !buf.readForestAddress(cancelAdrStr) || !buf.nextLine() ||
791  !buf.readLine(s) || s != "over") {
792  reply = "misformatted cancel session request"; return;
793  }
794  fAdr_t cancelAdr = Forest::forestAdr(cancelAdrStr.c_str());
795  if (cancelAdr == 0) {
796  reply = "misformatted address"; return;
797  }
798  int tclx = checkPrivileges(clientName, targetName);
799  if (tclx == 0) {
800  reply = "insufficient privileges for requested operation";
801  return;
802  }
803  cliTbl->releaseClient(tclx); // release before calling getSession()
804  int sess = cliTbl->getSession(cancelAdr); // locked again
805  if (sess == 0) {
806  reply = "invalid session address"; return;
807  }
808  if (cliTbl->getClientIndex(sess) != tclx) {
809  reply = "session address belongs to another client"; return;
810  }
811  ipa_t cliIp = cliTbl->getClientIp(sess);
812  fAdr_t rtrAdr = cliTbl->getRouterAdr(sess);
813 
814  // release lock on entry while waiting for reply from NetMgr
815  cliTbl->releaseClient(tclx);
816 
817  CtlPkt repCp;
818  pktx rpx = cph.cancelSession(nmAdr, cancelAdr, rtrAdr, repCp);
819  if (rpx == 0) {
820  reply = "cannot cancel session: NetMgr never responded";
821  return;
822  }
823  ps->free(rpx);
824  if (repCp.mode != CtlPkt::POS_REPLY) {
825  reply = "cannot complete cancelSession: NetMgr failed (" +
826  repCp.errMsg + ")";
827  return;
828  }
829  tclx = cliTbl->getClient(targetName); // re-acquire lock on entry
830  if (tclx == 0) {
831  reply = "cannot update session data for " + targetName;
832  logger->log("ClientMgr::cancelSession: cannot update session "
833  "data session state may be inconsistent",2);
834  return;
835  }
836  cliTbl->getAvailRates(tclx).add(cliTbl->getSessRates(sess));
837  writeAcctRecord(clientName,cancelAdr,cliIp,rtrAdr,CANCELSESSION);
838  cliTbl->removeSession(sess);
839  cliTbl->releaseClient(tclx);
840  return;
841 }
842 
849 void cancelAllSessions(NetBuffer& buf, string& clientName, CpHandler& cph,
850  string& reply) {
851  string s, targetName;
852  if (!buf.verify(':') || !buf.readName(targetName) || !buf.nextLine() ||
853  !buf.readLine(s) || s != "over") {
854  reply = "misformatted cancel all sessions request"; return;
855  }
856  int tclx = checkPrivileges(clientName, targetName);
857  if (tclx == 0) {
858  reply = "insufficient privileges for requested operation";
859  return;
860  }
861  int sess = cliTbl->firstSession(tclx);
862  while (sess != 0) {
863  fAdr_t cliAdr = cliTbl->getClientAdr(sess);
864  ipa_t cliIp = cliTbl->getClientIp(sess);
865  fAdr_t rtrAdr = cliTbl->getRouterAdr(sess);
866 
867  cliTbl->releaseClient(tclx); // release while waiting for reply
868  CtlPkt repCp;
869  pktx rpx = cph.cancelSession(nmAdr, cliAdr, rtrAdr, repCp);
870  if (rpx == 0) {
871  reply = "cannot cancel session: NetMgr never responded";
872  return;
873  }
874  ps->free(rpx);
875  if (repCp.mode != CtlPkt::POS_REPLY) {
876  reply = "cannot complete cancelSession: NetMgr failed "
877  "(" + repCp.errMsg + ")";
878  return;
879  }
880  tclx = cliTbl->getClient(targetName); // re-acquire lock
881 
882  cliTbl->getAvailRates(tclx).add(cliTbl->getSessRates(sess));
883  writeAcctRecord(clientName,cliAdr,cliIp,rtrAdr,CANCELSESSION);
884  cliTbl->removeSession(sess);
885  sess = cliTbl->firstSession(tclx);
886  }
887  cliTbl->releaseClient(tclx);
888  return;
889 }
890 
891 void addComtree(NetBuffer& buf, string& clientName, string& reply) {
892 }
893 
900 bool handleConnDisc(pktx px, CtlPkt& cp, CpHandler& cph) {
901  Packet& p = ps->getPacket(px);
902  if (p.srcAdr != nmAdr || cp.mode != CtlPkt::REQUEST) {
903  return false;
904  }
905  fAdr_t cliAdr = cp.adr1;
906  int sess = cliTbl->getSession(cliAdr); // locks client
907  if (sess == 0) {
908  cph.errReply(px,cp,"no record of session for "
909  "specified client address");
910  return false;
911  }
912  int clx = cliTbl->getClientIndex(sess);
913  const string& cliName = cliTbl->getClientName(clx);
914  ipa_t cliIp = cliTbl->getClientIp(sess);
915  fAdr_t rtrAdr = cliTbl->getRouterAdr(sess);
916 
917  if (cp.type == CtlPkt::CLIENT_CONNECT) {
918  cliTbl->setState(sess,ClientTable::CONNECTED);
919  } else {
920  cliTbl->removeSession(sess);
921  }
922 
923  cliTbl->releaseClient(clx);
924  acctRecType typ = (cp.type == CtlPkt::CLIENT_CONNECT ?
925  CONNECT_REC : DISCONNECT_REC);
926  writeAcctRecord(cliName, cliAdr, cliIp, rtrAdr, typ);
927 
928  // send reply to original request
929  CtlPkt repCp(cp.type,CtlPkt::POS_REPLY,cp.seqNum);
930  cph.sendReply(repCp, p.srcAdr);
931  return true;
932 }
933 
941 void writeAcctRecord(const string& cname, fAdr_t cliAdr, ipa_t cliIp,
942  fAdr_t rtrAdr, acctRecType recType) {
943  // should really lock while writing
944  if (!acctFile.good()) {
945  logger->log("ClientMgr::writeAcctRecord: cannot write "
946  "to accouting file",2);
947  return;
948  }
949  string typeStr = (recType == NEWSESSION ? "new session" :
950  (recType == CANCELSESSION ? "cancel session" :
951  (recType == CONNECT_REC ? "connect" :
952  (recType == DISCONNECT_REC ?
953  "disconnect" : "undefined record"))));
954  time_t t = Misc::currentTime();
955  string now = string(ctime(&t)); now.erase(now.end()-1);
956  string s;
957  acctFile << typeStr << ", " << now << ", " << cname << ", "
958  << Np4d::ip2string(cliIp,s) << ", ";
959  acctFile << Forest::fAdr2string(cliAdr,s) << ", ";
960  acctFile << Forest::fAdr2string(rtrAdr,s) << "\n";
961  acctFile.flush();
962 }
963 
968 void writeRecord(int clx) {
969  if (clx < 0 || clx >= cliTbl->getMaxClients()) return;
970 
971  pthread_mutex_lock(&clientFileLock);
972  if (dummyRecord == 0) {
973  // create dummy record, for padding clientFile
974  dummyRecord = new char[RECORD_SIZE];
975  for (char *p = dummyRecord; p < dummyRecord+RECORD_SIZE; p++)
976  *p = ' ';
977  dummyRecord[0] = '-'; dummyRecord[RECORD_SIZE-1] = '\n';
978  }
979  if (maxRecord == 0) {
980  clientFile.seekp(0,ios_base::end);
981  maxRecord = clientFile.tellp()/RECORD_SIZE;
982  }
983 
984 
985  // position file pointer, adding dummy records if needed
986  if (clx > maxRecord) {
987  clientFile.seekp((maxRecord+1)*RECORD_SIZE);
988  while (clx > maxRecord) {
990  maxRecord++;
991  }
992  }
993  clientFile.seekp(clx*RECORD_SIZE);
994 
995  if (cliTbl->validClient(clx)) {
996  string s;
997  cliTbl->client2string(clx,s);
998  s = "+ " + s;
999  if (s.length() > RECORD_SIZE) {
1000  s.erase(RECORD_SIZE-1); s += "\n";
1001  } else {
1002  s.erase(s.length()-1);
1003  int len = RECORD_SIZE - s.length();
1004  char *p = dummyRecord + s.length();
1005  s.append(p,len);
1006  }
1007  clientFile.write(s.c_str(),RECORD_SIZE);
1008  } else {
1009  //s.assign(dummyRecord,RECORD_SIZE);
1010  clientFile.write(dummyRecord,RECORD_SIZE);
1011  }
1012  clientFile.flush();
1013  maxRecord = max(clx,maxRecord);
1014  pthread_mutex_unlock(&clientFileLock);
1015  return;
1016 }
1017 
1018 } // ends namespace