forest-net
an overlay networks for large-scale virtual worlds
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
ClientTable.cpp
Go to the documentation of this file.
1 
9 #include "ClientTable.h"
10 
11 namespace forest {
12 
13 
15 ClientTable::ClientTable(int maxClients, int maxSessions)
16  : maxCli(maxClients), maxSess(maxSessions) {
17  svec = new Session[maxSess+1];
18  sessLists = new UiClist(maxSess);
19  sessMap = new IdMap(maxSess);
20  cvec = new Client[maxCli+1];
21  clients = new UiSetPair(maxCli);
22  nameMap = new map<string, int>();
23  maxClx = 0;
24  for (int i = 0; i <= maxSess; i++) svec[i].clx = 0;
25  defRates.set(50,500,25,250);
26  totalRates.set(100,1000,50,500);
27 }
28 
31  delete [] svec; delete sessLists; delete sessMap;
32  delete [] cvec; delete clients; delete nameMap;
33  pthread_mutex_destroy(&mapLock);
34 }
35 
37 bool ClientTable::init() {
38  if (pthread_mutex_init(&mapLock,NULL) != 0) return false;
39  for (int clx = 1; clx <= maxCli; clx++) {
40  cvec[clx].busyBit = false;
41  if (pthread_cond_init(&cvec[clx].busyCond,NULL) != 0)
42  return false;
43  }
44  return true;
45 }
46 
76 int ClientTable::getClient(const string& cname) {
77  lockMap();
78  map<string, int>::iterator p = nameMap->find(cname);
79  if (p == nameMap->end()) { unlockMap(); return 0; }
80  int clx = p->second;
81  while (cvec[clx].busyBit) { // wait until client's entry is not busy
82  pthread_cond_wait(&cvec[clx].busyCond,&mapLock);
83  p = nameMap->find(cname);
84  if (p == nameMap->end()) {
85  pthread_cond_signal(&cvec[clx].busyCond);
86  unlockMap(); return 0;
87  }
88  }
89  cvec[clx].busyBit = true; // set busyBit to lock client table entry
90  unlockMap();
91  return clx;
92 }
93 
98 void ClientTable::releaseClient(int clx) {
99  lockMap();
100  cvec[clx].busyBit = false;
101  pthread_cond_signal(&cvec[clx].busyCond);
102  unlockMap();
103 }
104 
112 int ClientTable::getSession(fAdr_t cliAdr) {
113  lockMap();
114  int sess = sessMap->getId(key(cliAdr));
115  if (sess == 0) { unlockMap(); return 0; }
116  int clx = svec[sess].clx;
117  while (cvec[clx].busyBit) { // wait until client's entry is not busy
118  pthread_cond_wait(&cvec[clx].busyCond,&mapLock);
119  sess = sessMap->getId(key(cliAdr));
120  if (sess == 0) {
121  pthread_cond_signal(&cvec[clx].busyCond);
122  unlockMap(); return 0;
123  }
124  clx = svec[sess].clx;
125  }
126  cvec[clx].busyBit = true; // set busyBit to lock client table entry
127  unlockMap();
128  return sess;
129 }
130 
136  lockMap();
137  int clx = clients->firstIn();
138  if (clx == 0) { unlockMap(); return 0; }
139  while (cvec[clx].busyBit) {
140  pthread_cond_wait(&cvec[clx].busyCond,&mapLock);
141  clx = clients->firstIn(); // first client may have changed
142  if (clx == 0) {
143  pthread_cond_signal(&cvec[clx].busyCond);
144  unlockMap(); return 0;
145  }
146  }
147  cvec[clx].busyBit = true;
148  unlockMap();
149  return clx;
150 }
151 
159 int ClientTable::nextClient(int clx) {
160  lockMap();
161  int nuClx = clients->nextIn(clx);
162  if (nuClx == 0) {
163  cvec[clx].busyBit = false;
164  pthread_cond_signal(&cvec[clx].busyCond);
165  unlockMap();
166  return 0;
167  }
168  while (cvec[nuClx].busyBit) {
169  pthread_cond_wait(&cvec[nuClx].busyCond,&mapLock);
170  nuClx = clients->nextIn(clx);
171  if (nuClx == 0) {
172  cvec[clx].busyBit = false;
173  pthread_cond_signal(&cvec[clx].busyCond);
174  pthread_cond_signal(&cvec[nuClx].busyCond);
175  unlockMap();
176  return 0;
177  }
178  }
179  cvec[nuClx].busyBit = true;
180  cvec[clx].busyBit = false;
181  pthread_cond_signal(&cvec[clx].busyCond);
182  unlockMap();
183  return nuClx;
184 }
185 
200 int ClientTable::addClient(string& cname, string& pwd,
201  privileges priv, int clx) {
202  lockMap();
203  map<string,int>::iterator p = nameMap->find(cname);
204  if (p != nameMap->end()) { unlockMap(); return 0; }
205  if (clx != 0) {
206  if (clients->isIn(clx)) clx = 0;
207  } else {
208  clx = clients->firstOut();
209  }
210  if (clx == 0) { unlockMap(); return 0;}
211  nameMap->insert(pair<string,int>(cname,clx));
212  clients->swap(clx);
213  cvec[clx].busyBit = true;
214  unlockMap();
215 
216  setClientName(clx,cname); setPassword(clx,pwd); setPrivileges(clx,priv);
217  setRealName(clx,"noname"); setEmail(clx,"nomail");
219  cvec[clx].firstSess = cvec[clx].numSess = 0;
220 
221  maxClx = max(clx,maxClx);
222  return clx;
223 }
224 
230 void ClientTable::removeClient(int clx) {
231  lockMap();
232  nameMap->erase(cvec[clx].cname);
233  while (cvec[clx].firstSess != 0)
234  removeSession(cvec[clx].firstSess);
235  clients->swap(clx);
236  cvec[clx].busyBit = false;
237  pthread_cond_signal(&cvec[clx].busyCond);
238  unlockMap();
239 }
240 
250 int ClientTable::addSession(fAdr_t cliAdr, fAdr_t rtrAdr, int clx) {
251  lockMap();
252  int sess = sessMap->addPair(key(cliAdr));
253  if (sess == 0) { unlockMap(); return 0; }
254  svec[sess].cliAdr = cliAdr;
255  svec[sess].rtrAdr = rtrAdr;
256  svec[sess].clx = clx;
257  if (cvec[clx].firstSess == 0) {
258  cvec[clx].firstSess = sess;
259  } else {
260  sessLists->join(sess,cvec[clx].firstSess);
261  }
262  cvec[clx].numSess++;
263  unlockMap();
264 
265  return sess;
266 }
267 
272 void ClientTable::removeSession(int sess) {
273  lockMap();
274  int clx = svec[sess].clx;
275  if (clx == 0) { unlockMap(); return; }
276  if (cvec[clx].firstSess == sess) {
277  if (sessLists->suc(sess) == sess) {
278  cvec[clx].firstSess = 0;
279  } else {
280  cvec[clx].firstSess = sessLists->suc(sess);
281  sessLists->remove(sess);
282  }
283  } else {
284  sessLists->remove(sess);
285  }
286  sessMap->dropPair(key(svec[sess].cliAdr));
287  svec[sess].clx = 0; // used to detect unused entries
288  cvec[clx].numSess--;
289  unlockMap();
290 }
291 
300 bool ClientTable::readEntry(istream& in, int clx) {
301  string cname, pwd, privString, realName, email;
303 
304  if (!in.good()) return false;
305  if (Misc::verify(in,'+')) {
306  if (!Misc::readName(in, cname) || !Misc::verify(in,',') ||
307  !Misc::readWord(in, pwd) || !Misc::verify(in,',') ||
308  !Misc::readWord(in, privString) || !Misc::verify(in,',') ||
309  !Misc::readString(in, realName) || !Misc::verify(in,',') ||
310  !Misc::readWord(in, email) || !Misc::verify(in,',') ||
311  !defRates.read(in) || !Misc::verify(in,',') ||
312  !totalRates.read(in)) {
313  return false;
314  }
315  Misc::cflush(in,'\n');
316  } else if (Misc::verify(in,'-')) {
317  maxClx = max(clx, maxClx);
318  Misc::cflush(in,'\n'); return true;
319  } else {
320  Misc::cflush(in,'\n'); return false;
321  }
322 
323  privileges priv;
324  if (privString == "limited") priv = LIMITED;
325  else if (privString == "standard") priv = STANDARD;
326  else if (privString == "admin") priv = ADMIN;
327  else if (privString == "root") priv = ROOT;
328  else priv = NUL_PRIV;
329 
330  if (addClient(cname, pwd, priv,clx) == 0) return false;
331  setRealName(clx,realName); setEmail(clx,email);
332  getDefRates(clx) = defRates;
333  getTotalRates(clx) = totalRates;
334  getAvailRates(clx) = totalRates;
335  releaseClient(clx);
336  return true;
337 }
338 
392 bool ClientTable::read(istream& in) {
393  int i = 0;
394  while (readEntry(in,i)) i++;
395  cout << "read " << i << " client records, producing "
396  << clients->getNumIn() << "table entries\n";
397  return true;
398 }
399 
407 string& ClientTable::client2string(int clx, string& s, bool includeSess) const {
408  string s1;
409  s = getClientName(clx) + ", " + getPassword(clx) + ", ";
410 
411  privileges priv = getPrivileges(clx);
412  switch (priv) {
413  case LIMITED: s += "limited"; break;
414  case STANDARD: s += "standard"; break;
415  case ADMIN: s += "admin"; break;
416  case ROOT: s += "root"; break;
417  default: s += "-";
418  }
419 
420  s += ", \"" + getRealName(clx) + "\", " + getEmail(clx) + ", " +
421  getDefRates(clx).toString(s1) + ", ";
422  s += getTotalRates(clx).toString(s1) + "\n";
423  if (includeSess) {
424  for (int sess = firstSession(clx); sess != 0;
425  sess = nextSession(sess,clx)) {
426  s += session2string(sess, s1);
427  }
428  }
429  return s;
430 }
431 
438 string& ClientTable::session2string(int sess, string& s) const {
439  string s1;
440  s = Forest::fAdr2string(getClientAdr(sess),s1) + ", ";
441  s += Forest::fAdr2string(getRouterAdr(sess),s1) + ", ";
442  s += getSessRates(sess).toString(s1) + ", ";
443  time_t t = getStartTime(sess);
444  s += ctime(&t);
445  return s;
446 }
447 
454 string& ClientTable::toString(string& s, bool includeSess) {
455  string s1; s = "";
456  for (int clx = firstClient(); clx != 0; clx = nextClient(clx))
457  s += client2string(clx,s1,includeSess);
458  return s;
459 }
460 
466 void ClientTable::write(ostream& out, bool includeSess) {
467  string s;
468  for (int clx = firstClient(); clx != 0; clx = nextClient(clx))
469  out << client2string(clx,s,includeSess);
470 }
471 
472 } // ends namespace
473