/* This file is part of Bolixo. Bolixo is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Bolixo is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Bolixo. If not, see . */ /* Manage web session ID */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define INSTRUMENT_DONOTOPEN #include "instrument.h" static DEBUG_KEY D_PROTO ("proto","Protocol information"); enum CONNECT_TYPE { TYPE_NONE, TYPE_CONTROL, TYPE_CLIENT, TYPE_ADMIN }; struct HANDLE_INFO: public ARRAY_OBJ{ CONNECT_TYPE type = TYPE_NONE; int no = -1; std::string host; REQUEST_INFO req; int wait_userid = -1; }; #include "proto/bo-sessiond_control.protoh" #define bo_sessiond_client_rep_waitevent_NEEDED #include "proto/bo-sessiond_client.protoh" #include "proto/bo-sessiond_admin.protoh" #define session_log_session_NOTNEED #define session_log_sessionv1_NOTNEED #define session_log_notify_NOTNEED #define session_log_notify_v1_NOTNEED #define session_log_notify_v2_NOTNEED #define session_log_notify_v3_NOTNEED #include "proto/session_log.protoch" #include "proto/session_log.protoh" using namespace std; struct VARIABLE{ string name; string val; VARIABLE (PARAM_STRING _name, PARAM_STRING _val){ name = _name.ptr; val = _val.ptr; } }; // Information associated with a session struct SESSION_INFO{ unsigned userid; string userid_str; string name; string email; string lang; unsigned char dateformat; string timezone; bool admin; vector vars; struct { time_t lastvisit = time(nullptr); time_t lastping = (time_t)0; unsigned nbacc = 0; } stats; SESSION_INFO(){ userid = (unsigned)-1; admin = false; dateformat = 0; } SESSION_INFO (unsigned _userid, const string &_userid_str, const char *_name, const char *_email, const char *_lang, bool _admin, unsigned _dateformat, const char *_timezone){ userid = _userid; userid_str = _userid_str; name = _name; email = _email; lang = _lang; admin = _admin; dateformat = _dateformat; timezone = _timezone; stats.nbacc=1; } }; static const char *VAR_NOTIFIES = "notifies"; // Information associated with a user // Notification are globals to all sessions of a user. // Some variables are globals as well. struct USER_INFO{ unsigned sequence = 0; vector notes; vector vars; }; static unsigned max_key_notes=1; // Number of notifications kept with the same key static void bo_sessiond_setnotify( map &userinfos, map> &waiting, const vector &userids, const char *name, const char *script) { time_t now = time(NULL); for (auto userid:userids){ auto &v = userinfos[userid]; unsigned sequence = ++v.sequence; // Notifications contains script. We can't just override a notification with // a new one. The previous script would be lost. A user would miss one script // and the document/game would be broken // Notifications like these are never deleted because the end user may // have several UIs running. // So we keep around the last max_key_notes. // Further, in waitevent, if there are several consecutive event for the same // key, we return a concatenation of the scripts. unsigned found = 1; // We count the one we will add next for (auto &n:v.notes){ if (n.key == name){ found++; } } { NOTIFY n; n.key = name; n.script = script; n.when = now; n.sequence = sequence; v.notes.push_back(n); } for (auto it=v.notes.begin(); found > max_key_notes && it != v.notes.end(); ){ auto next_it = it+1; if (it->key == name){ next_it = v.notes.erase(it); found--; } it = next_it; } auto w = waiting.find(userid); if (w != waiting.end()){ for (auto fd:w->second){ bo_sessiond_client_rep_waitevent (fd,true,"",name,script,sequence); } waiting.erase(w); } } } static void bo_sessiond_dumpvars(const char *prefix, const vector &vars, vector &tb) { for (auto &v:vars){ string tmp = string_f("\t%s %s:[",prefix,v.name.c_str()); bool geometry = v.name == "geometry"; for (auto &vv:v.vals){ if (geometry && vv.sname == "id") tmp += "\n\t"; tmp += string_f (" %s=%s",vv.sname.c_str(),vv.sval.c_str()); } tmp += "]"; tb.push_back(move(tmp)); } } int main (int argc, char *argv[]) { glocal int ret = -1; glocal int noproc = 1; glocal const char *client_secretfile = "/etc/bolixo/secrets.client"; glocal const char *admin_secretfile = "/etc/bolixo/secrets.admin"; glocal const char *bind = "0.0.0.0"; glocal const char *port = "9200"; glocal const char *control = "/var/run/bo-sessiond.sock"; glocal const char *user = "bolixo"; glocal bool daemon = false; glocal const char *pidfile = "/var/run/bo-sessiond.pid"; glocal vector variables; glocal vector user_variables; glocal unsigned maxhandles = 10000; glocal.ret = (argc,argv,"tlmpsql"); setproginfo ("bo-sessiond",VERSION,"Manage web sessions"); setgrouparg ("Networking"); setarg ('b',"bindaddr","Bind to this address (TCP)",glocal.bind,false); setarg ('p',"tcpport","Listen for command on this TCP port",glocal.port,false); setarg ('c',"control","Unix socket for bo-sessiond",glocal.control,false); setarg (' ',"variable","Supported web session variables",glocal.variables,false); setarg (' ',"user_variable","Supported web user variables",glocal.user_variables,false); setgrouparg ("Misc."); setarg (' ',"admin-secrets","File holding admin secrets for communication",glocal.admin_secretfile,false); setarg (' ',"client-secrets","File holding client secrets for communication",glocal.client_secretfile,false); setarg (' ',"user","Run the program as this user",glocal.user,false); setarg (' ',"daemon","Run in background",glocal.daemon,false); setarg (' ',"pidfile","File holding the PID of the process",glocal.pidfile,false); setarg (' ',"maxhandles","Maximum number of file handle supported",glocal.maxhandles,false); setarg (' ',"max_eq_notes","Maximum number of notifications kept with the same key",max_key_notes,false); glocal const char *msg = msg; ("/tmp/err.log",true); fprintf (fout,"%s\n",glocal.msg); return 0; if (glocal.daemon){ syslog (LOG_ERR,"%s",msg); }else{ fprintf (stderr,"%s",msg); } if (glocal.daemon){ syslog (LOG_WARNING,"%s",msg); }else{ fprintf (stderr,"%s",msg); } glocal set vars; // Acceptable variable names glocal set user_vars; // glocal variable names glocal map sessions; glocal map userinfos; glocal map> waiting; // Connections waiting for events glocal map admin_secrets; glocal map client_secrets; glocal unsigned long nbrequest_admin = 0; glocal unsigned long nbrequest_client = 0; glocal unsigned long nbsessions_created = 0; glocal CONNECT_INFO con; glocal string controlport = string_f("unix:%s",glocal.control); string clientport = string_f ("unix:/tmp/sessiond-client-%s.sock",glocal.port); string adminport = string_f ("unix:/tmp/sessiond-admin-%s.sock",glocal.port); fdpass_readsecrets (glocal.admin_secretfile,glocal.admin_secrets); fdpass_readsecrets (glocal.client_secretfile,glocal.client_secrets); int ret = -1; for (auto s:glocal.variables) glocal.vars.insert(s); for (auto s:glocal.user_variables){ glocal.vars.insert(s); glocal.user_vars.insert(s); } { FILE *fin = fopen ("/tmp/sessions.log","r"); if (fin != NULL){ glocal function &vars)> freadvars; glocal.freadvars = [&](unsigned userid, SESSION_INFO &sess, const vector &vars){ for (auto &v:vars){ if (glocal.user_vars.count(v.name)!=0){ // Move per session variables to per user variables auto &uvars = glocal.userinfos[userid].vars; bool found = false; for (auto &uv:uvars){ if (uv.name == v.name){ found = true; break; } } if (!found){ uvars.push_back(v); } }else{ sess.vars.push_back(v); } } }; (fin); SESSION_INFO sess(userid, userid_str, name, email, lang, admin, dateformat,""); sess.stats.lastvisit = lastvisit; sess.stats.nbacc = nbacc; for (auto &v:vars) sess.vars.push_back(v); glocal.sessions[session] = move(sess); SESSION_INFO sess(userid, userid_str, name, email, lang, admin, dateformat,timezone); sess.stats.lastvisit = lastvisit; sess.stats.nbacc = nbacc; for (auto &v:vars){ if (glocal.user_vars.count(v.name)!=0){ // Move per session variables to per user variables auto &uvars = glocal.userinfos[userid].vars; bool found = false; for (auto &uv:uvars){ if (uv.name == v.name){ found = true; break; } } if (!found){ uvars.push_back(v); } }else{ sess.vars.push_back(v); } } glocal.sessions[session] = move(sess); SESSION_INFO sess(userid, userid_str, name, email, lang, admin, dateformat,timezone); sess.stats.lastvisit = lastvisit; sess.stats.lastping = lastping; sess.stats.nbacc = nbacc; glocal.freadvars(userid,sess,vars); glocal.sessions[session] = move(sess); auto &v = glocal.userinfos[userid]; // Old format, when must assign a sequence number for (auto &n:notifies){ NOTIFY nn; nn.key = n.key; nn.when = n.when; nn.sequence = ++v.sequence; v.notes.push_back(nn); } auto &v = glocal.userinfos[userid]; v.sequence = 0; for (auto &n:notifies){ if (v.sequence < n.sequence) v.sequence = n.sequence; v.notes.push_back(n); } auto &v = glocal.userinfos[userid]; v.sequence = sequence; for (auto &n:notifies){ NOTIFY note; note.key = n.key; note.when = n.when; note.sequence = n.sequence; v.notes.push_back(note); } auto &v = glocal.userinfos[userid]; v.sequence = sequence; for (auto &n:notifies) v.notes.push_back(n); auto &user = glocal.userinfos[userid]; user.sequence = sequence; for (auto &n:notifies) user.notes.push_back(n); // A bug created dups in user variables. We keep the first here for (auto &var:vars){ bool found = false; for (auto &uv:user.vars){ if (uv.name == var.name){ found = true; break; } } if (!found){ user.vars.push_back(var); } } tlmp_error ("Invalid entry, reading sessions.log\n"); fclose (fin); } } (glocal.bind,clientport,5); HANDLE_INFO *n = new HANDLE_INFO; info.data = n; if (string_cmp(info.port,glocal.controlport)==0){ n->type = TYPE_CONTROL; }else{ settcpnodelay(true); char addr[20]; const char *fromstr = addr; if (strncmp(info.port,"unix:",5)==0){ fromstr = info.port; }else{ ipnum_ip2a (from,addr); } n->host = fromstr; n->req.secret = fdpass_findsecret (glocal.client_secrets,fromstr); if (n->req.secret.size() > 0){ n->type = TYPE_CLIENT; }else{ n->req.secret = fdpass_findsecret (glocal.admin_secrets,fromstr); if (n->req.secret.size() > 0){ n->type = TYPE_ADMIN; }else{ tlmp_error ("Rejected connexion from IP %s\n",fromstr); endclient = true; } } } HANDLE_INFO *n = (HANDLE_INFO*)info.data; if (n->wait_userid != -1){ auto &w = glocal.waiting[n->wait_userid]; for (auto it=w.begin(); it != w.end(); it++){ if (*it == no){ w.erase(it); break; } } } debug_printf (D_PROTO,"receive line: %s\n",line); HANDLE_INFO *c = (HANDLE_INFO*)info.data; static const char *tbtype[]={"none","control request","client request","admin request"}; ERROR_PREFIX prefix ("%s: host %s secret %s:",tbtype[c->type],c->host.c_str(),c->req.secret.c_str()); if (c->type == TYPE_CONTROL){ (this,c->req,line, info.linelen,endserver, endclient, no,c,c->host.c_str()); vector tb; tb.push_back(string_f ("Version %s",VERSION)); tb.push_back(string_f ("nbsessions=%zu",glocal.sessions.size())); unsigned long nbanon = 0; unsigned long nbadmin = 0; for (auto &x:glocal.sessions){ if (x.second.userid ==(unsigned)-1) nbanon++; if (x.second.admin) nbadmin++; } tb.push_back(string_f ("nbanon=%lu",nbanon)); tb.push_back(string_f ("nbadmin=%lu",nbadmin)); tb.push_back(string_f ("nbuserinfos=%zu",glocal.userinfos.size())); tb.push_back(string_f ("nbsesssions_created=%lu",glocal.nbsessions_created)); tb.push_back(string_f ("nbrequest_admin=%lu",glocal.nbrequest_admin)); tb.push_back(string_f ("nbrequest_client=%lu",glocal.nbrequest_client)); unsigned long nbwaiting=0; for (auto &ww:glocal.waiting){ nbwaiting += ww.second.size(); } tb.push_back(string_f("nbwaiting=%lu",nbwaiting)); tb.push_back(string_f ("max_eq_notes=%u",max_key_notes)); instrument_status (tb); rep_status(tb); toggle_instrument_file(on); ("/tmp/sessions.log",false); for (auto const &s:glocal.sessions){ session_log_sessionv2(fout,s.first,s.second.userid,s.second.userid_str ,s.second.name,s.second.email,s.second.lang,s.second.timezone,s.second.admin ,s.second.dateformat ,s.second.stats.lastvisit,s.second.stats.lastping,s.second.stats.nbacc,s.second.vars); } for (auto const &s:glocal.userinfos){ session_log_notify_v4(fout,s.first,s.second.sequence,s.second.notes,s.second.vars); } return 0; endserver = true; unsigned pos = 0; unsigned end = offset+nb-1; vector tb; tb.push_back(string_f("listsessions %u -> %u",offset,end)); for (auto &it:glocal.sessions){ if (pos >end){ break; }else if (pos >= offset){ DATEASC datevisit,dateping; fdpass_asctime (it.second.stats.lastvisit,datevisit); fdpass_asctime (it.second.stats.lastping,dateping); string tmp; if (strncasecmp(it.second.userid_str.c_str(),"http://",7)==0 || strncasecmp(it.second.userid_str.c_str(),"https://",8)==0){ const char *status = "unknown"; if (it.second.userid == (unsigned)-1){ status = "logged"; }else if (it.second.userid == (unsigned)-2){ status = "login"; } tmp = string_f ("%07u: %s %s-%s %s %s %s %u %u %d %s",pos,it.first.c_str() ,status,it.second.userid_str.c_str(),it.second.name.c_str(),it.second.email.c_str() ,datevisit.buf,it.second.stats.nbacc ,it.second.userid,it.second.admin,it.second.lang.c_str()); }else if (it.second.userid==(unsigned)-1){ tmp = string_f ("%07u: %s anonymous %s %u",pos,it.first.c_str() ,datevisit.buf,it.second.stats.nbacc); }else{ tmp = string_f ("%07u: %s %d-%s %s %s %s %s %u %u %d %s %u %s",pos,it.first.c_str() ,it.second.userid,it.second.userid_str.c_str(),it.second.name.c_str(),it.second.email.c_str() ,datevisit.buf,dateping.buf,it.second.stats.nbacc ,it.second.userid,it.second.admin,it.second.lang.c_str() ,it.second.dateformat,it.second.timezone.c_str()); } tb.push_back(move(tmp)); bo_sessiond_dumpvars("S",it.second.vars,tb); auto note = glocal.userinfos.find(it.second.userid); if (note != glocal.userinfos.end()){ tmp = string_f("\t%s:",VAR_NOTIFIES); tmp += string_f(" %u",note->second.sequence); for (auto &n:note->second.notes){ tmp += string_f(" %s,%u,%u",n.key.c_str(),n.when,n.sequence); } tb.push_back(move(tmp)); bo_sessiond_dumpvars("U",note->second.vars,tb); } } pos++; } rep_listsessions(tb); glocal.userinfos.clear(); time_t t = time(NULL); for (unsigned i=0; i unsigned deletedanon = 0; unsigned deleteduser = 0; unsigned deletedadmin = 0; time_t oldtime = time(NULL)-nbseconds; for (auto it=glocal.sessions.begin(); it!= glocal.sessions.end(); ){ auto next = it; next++; if (it->second.stats.lastvisit < oldtime && it->second.stats.lastping < oldtime){ if (it->second.userid == (unsigned)-1){ if (anonymous){ next = glocal.sessions.erase(it); deletedanon++; } }else if (it->second.admin){ if (adminuser){ next = glocal.sessions.erase(it); deletedadmin++; } }else{ if (normaluser){ next = glocal.sessions.erase(it); deleteduser++; } } } it = next; } rep_eraseold(deletedanon,deleteduser,deletedadmin); time_t old = time(nullptr)-nbseconds; unsigned deleted = 0; for (auto &p:glocal.userinfos){ auto &v = p.second.notes; for (auto it=v.begin(); it != v.end(); ){ auto next_it = it+1; if ((time_t)it->when < old){ deleted++; next_it = v.erase(it); } it = next_it; } } rep_eraseoldnotes(deleted); for (auto &w:glocal.waiting){ for (auto &c:w.second){ glocal.TCPSERVER.closeclient(c); } } glocal.waiting.clear(); if (on){ debug_seton(); }else{ debug_setoff(); } debug_setfdebug (filename); max_key_notes = max_notes; tlmp_error ("Control: Invalid command: %s\n",line); endclient = true; }else if (c->type == TYPE_CLIENT){ glocal.nbrequest_client++; (this,c->req,line, info.linelen,endserver, endclient, no,c,c->host.c_str()); auto it = glocal.sessions.find(sessionid); if (it != glocal.sessions.end()){ it->second.stats.lastping = time(nullptr); it->second.stats.nbacc++; rep_getsessioninfo(true,it->second.name,it->second.lang,it->second.dateformat ,it->second.admin,it->second.userid,it->second.timezone); }else{ rep_getsessioninfo(false,"","",0,false,0,""); } auto it = glocal.sessions.find(sessionid); if (it != glocal.sessions.end()){ it->second.stats.lastvisit = time(NULL); it->second.stats.nbacc++; vector vars = it->second.vars; auto note = glocal.userinfos.find(it->second.userid); if (note != glocal.userinfos.end()){ VAR var; var.name = VAR_NOTIFIES; // We send the sequence number as a variable { SNAMEVAL val; val.sname = "notify_sequence"; val.sval = string_f("%u",note->second.sequence); var.vals.push_back(val); } for (auto &n:note->second.notes){ SNAMEVAL val; val.sname = n.key; var.vals.push_back(val); } vars.push_back(var); for (auto &v:note->second.vars){ vars.push_back(v); } } rep_getsessioninfovars(true,it->second.name,it->second.lang,it->second.dateformat,it->second.admin,it->second.userid,vars); }else{ vector vars; rep_getsessioninfovars(false,"","",0,false,0,vars); } auto it = glocal.sessions.find(sessionid); if (it != glocal.sessions.end()){ { time_t now = time(nullptr); if (activity){ it->second.stats.lastvisit = now; it->second.stats.nbacc++; }else{ // bo-websocket is calling this (wih activity == false) whenever it starts a new // session but this is not really a user activity it->second.stats.lastping = now; } } vector vars = it->second.vars; auto note = glocal.userinfos.find(it->second.userid); if (note != glocal.userinfos.end()){ VAR var; var.name = VAR_NOTIFIES; // We send the sequence number as a variable { SNAMEVAL val; val.sname = "notify_sequence"; val.sval = string_f("%u",note->second.sequence); var.vals.push_back(val); } for (auto &n:note->second.notes){ SNAMEVAL val; val.sname = n.key; var.vals.push_back(val); } vars.push_back(var); for (auto &v:note->second.vars){ vars.push_back(v); } } rep_getsessioninfovars_v2(true,it->second.name,it->second.lang,it->second.dateformat,it->second.admin,it->second.userid,vars); }else{ vector vars; rep_getsessioninfovars_v2(false,"","",0,false,0,vars); } string msg; bool success = false; if (glocal.vars.count(var.name)==0){ msg = "unknown variable"; }else{ auto it = glocal.sessions.find(sessionid); if (it != glocal.sessions.end()){ auto *vars = &it->second.vars; if (glocal.user_vars.count(var.name)!=0){ vars = &glocal.userinfos[it->second.userid].vars; } bool found = false; for (auto &v:*vars){ if (v.name == var.name){ v.vals.clear(); for (auto &vv:var.vals){ SNAMEVAL vals; vals.sname = vv.sname; vals.sval = vv.sval; v.vals.push_back(vals); } found = true; success = true; msg = "Variable updated"; break; } } if (!found){ vars->push_back(var); msg = "Variable added"; success = true; } }else{ msg = "invalid session"; } } rep_setvar (success,msg); bool success = false; string msg; auto it = glocal.sessions.find(sessionid); if (it == glocal.sessions.end()){ msg = "Invalid sessionid"; }else{ auto note = glocal.userinfos.find(it->second.userid); if (note != glocal.userinfos.end()){ for (auto vit=note->second.notes.begin(); vit != note->second.notes.end(); ){ auto next_vit = vit+1; if (vit->key == name){ next_vit = note->second.notes.erase(vit); } vit = next_vit; } // We must keep the entry around to keep the sequence number. //if (note->second.notes.size()==0) glocal.userinfos.erase(note); } success = true; } rep_delnotify(success,msg); // sessionid sequence = success:b msg content sequence auto it = glocal.sessions.find(sessionid); if (it == glocal.sessions.end()){ rep_waitevent (false,"Invalid sessionid","","",0); }else if (it->second.userid == (unsigned)-1){ rep_waitevent (false,"Anonymous session","","",0); }else{ // We do not respond immediatly, only if there is an event auto n = glocal.userinfos.find(it->second.userid); unsigned next_sequence = (unsigned)-1; string name; string script; if (n != glocal.userinfos.end()){ // We have to find the next event with the closest smallest // sequence number, yet larger than the received sequence parameter. for (auto &nn:n->second.notes){ if (nn.sequence > sequence && nn.sequence < next_sequence){ next_sequence = nn.sequence; name = nn.key; script = nn.script; }else if (nn.sequence == next_sequence+1 && nn.key == name){ script += nn.script; next_sequence = nn.sequence; //tlmp_warning ("merge name=%s next_sequence=%u sequence=%u",name.c_str(),next_sequence,nn.sequence); } } } if (next_sequence != (unsigned)-1){ rep_waitevent (true,"",name,script,next_sequence); }else{ // We record the request and set_notify will reply c->wait_userid = it->second.userid; auto &w = glocal.waiting[it->second.userid]; bool found = false; for (auto u:w){ if (u==no){ found = true; break; } } if (!found) w.push_back(no); } } bo_sessiond_setnotify(glocal.userinfos,glocal.waiting,userids,name,script); rep_test (true); bool ret = false; auto it = glocal.sessions.find(sessionid); if (it == glocal.sessions.end()){ tlmp_error ("ping invalid session %s",sessionid); }else{ ret = true; time_t now = time(nullptr); if (activity){ it->second.stats.lastvisit = now; it->second.stats.nbacc++; }else{ it->second.stats.lastping = now; } } rep_ping (ret); tlmp_error ("Client: Invalid command: %s\n",line); endclient = true; }else if (c->type == TYPE_ADMIN){ glocal.nbrequest_admin++; (this,c->req,line, info.linelen,endserver, endclient, no,c,c->host.c_str()); auto it = glocal.sessions.find(sessionid); if (it != glocal.sessions.end()){ it->second.stats.lastping = time(nullptr); it->second.stats.nbacc++; rep_getsessioninfo(true,it->second.name.c_str(),it->second.email.c_str(),it->second.lang,it->second.admin,it->second.userid); }else{ rep_getsessioninfo(false,"","",0,false,0); } auto it = glocal.sessions.find(sessionid); if (it != glocal.sessions.end()){ rep_getsession(true,it->second.userid,it->second.userid_str.c_str(),it->second.admin,it->second.lang); }else{ rep_getsession(false,0,"",false,""); } auto it = glocal.sessions.find(sessionid); if (it == glocal.sessions.end()){ tlmp_error ("setsession: %s does not exist\n",sessionid); }else{ it->second = SESSION_INFO(userid,userid_str,name,email,lang,admin,dateformat,timezone); } auto it = glocal.sessions.find(sessionid); if (it == glocal.sessions.end()){ tlmp_error ("setsession: %s does not exist\n",sessionid); }else{ it->second.lang = lang; it->second.dateformat = dateformat; // Update all sessions for this user for (auto &s:glocal.sessions){ if (s.second.userid==it->second.userid){ s.second.lang = lang; s.second.dateformat = dateformat; s.second.timezone = timezone; } } } // Assigned a notification to a list of userids bo_sessiond_setnotify(glocal.userinfos,glocal.waiting,userids,name,script); auto it = glocal.sessions.find(sessionid); if (it != glocal.sessions.end()){ glocal.sessions.erase(it); }else{ tlmp_error ("deletesession: unknown sessionid %s\n",sessionid); } vector sessionids; for (auto &it:glocal.sessions){ if (it.second.userid==userid) sessionids.push_back(it.first); } for (auto &it:sessionids){ auto s = glocal.sessions.find(it); if (s != glocal.sessions.end()){ glocal.sessions.erase(s); } } auto it = glocal.sessions.find(sessionid); if (it == glocal.sessions.end()){ glocal.nbsessions_created++; glocal.sessions[sessionid]=SESSION_INFO(); }else{ tlmp_error ("createsession: %s already exists\n",sessionid); } rep_test (true); tlmp_error ("Admin: Invalid command: %s\n",line); endclient = true; } bool some_errors = false; if (fdpass_setcontrol(s,glocal.control,glocal.user)==-1){ some_errors = true; } if (s.listen (NULL,adminport)==-1){ some_errors = true; } if (!some_errors && s.is_ok()){ chmod (clientport.c_str()+5,0666); chmod (adminport.c_str()+5,0666); s.setmaxclients (glocal.maxhandles); s.setrawmode(true); if (glocal.daemon){ daemon_init(glocal.pidfile,glocal.user); } s.loop(); ret = 0; } return ret; return glocal.ret; }