/* * Return the number of clients held by this thread */ int ConnectionPool::numClientsBorrowed() { ClientSet *clients = reinterpret_cast<ClientSet*>(pthread_getspecific(m_borrowedClients)); if (clients != NULL) { return clients->size(); } return 0; }
/* * Retrieve a client that is connected and authenticated * to the specified hostname and port. Will reuse an existing connection if one is available. */ voltdb::Client ConnectionPool::acquireClient( std::string hostname, std::string username, std::string password, StatusListener *listener, unsigned short port, ClientAuthHashScheme sha) throw (voltdb::Exception, voltdb::ConnectException, voltdb::LibEventException) { LockGuard guard(m_lock); ClientSet *clients = reinterpret_cast<ClientSet*>(pthread_getspecific(m_borrowedClients)); if (clients == NULL) { clients = new ClientSet(); pthread_setspecific( m_borrowedClients, static_cast<const void *>(clients)); } char portBytes[16]; unsigned int portInt = port; snprintf(portBytes, 16, "%d", portInt); std::string identifier = hostname + "," + std::string(portBytes) + "," + username + "," + password; // if a thread calls acquireClient() multiple times with the same identifier, reuse the same client for (ClientSet::iterator i = clients->begin(); i != clients->end(); i++) { if ((*i)->m_identifier == identifier) { return (*i)->m_client; } } std::vector<boost::shared_ptr<ClientStuff> > *clientStuffs = &m_clients[identifier]; while (clientStuffs->size() > 0) { boost::shared_ptr<ClientStuff> clientStuff = clientStuffs->back(); clientStuffs->pop_back(); // run the event loop once to verify the connection is still available clientStuff->m_client.runOnce(); if (clientStuff->m_listener->m_connectionLost) { // if this connection is lost, try the next continue; } else { // otherwise return this connection clientStuff->m_listener->m_listener = listener; clients->push_back(clientStuff); return clientStuff->m_client; } } // no connection available, make a new one DelegatingStatusListener *delegatingListener = new DelegatingStatusListener(); Client client = voltdb::Client::create(ClientConfig( username, password, delegatingListener, sha)); client.createConnection(hostname, port); boost::shared_ptr<ClientStuff> stuff(new ClientStuff(client, identifier, delegatingListener)); stuff->m_listener->m_listener = listener; clients->push_back(stuff); return client; }
/* * Cleanup function used by thread local ptr to a list of clients * that were borrowed from the pool. It unsets the listener and returns the client * to the pool. */ void cleanupOnScriptEnd(void *ptr) { if (gPool != NULL) { LockGuard guard(gPool->m_lock); ClientSet *clients = reinterpret_cast<ClientSet*>(ptr); if (clients != NULL) { boost::scoped_ptr<ClientSet> guard(clients); for(ClientSet::iterator i = clients->begin(); i != clients->end(); i++) { (*i)->m_listener->m_listener = NULL; gPool->m_clients[(*i)->m_identifier].push_back(*i); } pthread_setspecific(gPool->m_borrowedClients, NULL); } } }
void ConnectionPool::closeClientConnection(Client client) throw (voltdb::Exception) { LockGuard guard(m_lock); ClientSet *clients = reinterpret_cast<ClientSet*>(pthread_getspecific(m_borrowedClients)); if (clients == NULL) { //No clients closing a stale object or not owned by this thread. return; } for (ClientSet::iterator i = clients->begin(); i != clients->end(); i++) { if ((*i)->m_client == client) { client.close(); (*i)->m_listener->m_listener = NULL; clients->erase(i); return; } } }
/** * getProjectList generates a list of projects on this server, each list (vector) item is * actually a pinfo (project info) object, the list does NOT contain all projects, but * only contains projects relevant to the binary that is currently loaded in IDA * @param phash the IDA generated hash that is unique among the analysis files * @return a vector of project info objects for the provided phash */ vector<ProjectInfo*> *BasicConnectionManager::getProjectList(const string &phash) { vector<ProjectInfo*> *plist; //build a basic mode project list Basic_it bi = basicProjects.find(phash); if (bi != basicProjects.end()) { plist = (*bi).second; } else { plist = new vector<ProjectInfo*>; } for (Info_it it = plist->begin(); it != plist->end(); it++) { ClientSet *cs = projects.get((*it)->lpid); if (cs != NULL) { (*it)->connected = cs->size(); } } return plist; }
void ConnectionPool::returnClient(Client client) throw (voltdb::Exception) { LockGuard guard(m_lock); ClientSet *clients = reinterpret_cast<ClientSet*>(pthread_getspecific(m_borrowedClients)); if (clients == NULL) { throw MisplacedClientException(); } for (ClientSet::iterator i = clients->begin(); i != clients->end(); i++) { if ((*i)->m_client == client) { (*i)->m_listener->m_listener = NULL; m_clients[(*i)->m_identifier].push_back(*i); clients->erase(i); return; } } throw MisplacedClientException(); }
/** * joinProject joings a particular client to a project so that it can participate in collabREation * @param c the client attempting to join * @param lpid the local project id of the project on this server * @return 0 on success, negative value on failure */ int DatabaseConnectionManager::joinProject(Client *c, uint32_t lpid) { int rval = -1; bool foundPid = false; uint32_t tpid = htonl(lpid); static const int plens[1] = {sizeof(tpid)}; static const int pformats[1] = {1}; const char * const parms[1] = {(char*)&tpid}; #ifdef DEBUG log(LINFO4, "trying to join project %u\n", lpid); #endif sem_wait(&fpbp_sem); PGresult *rset = PQexecPrepared(dbConn, "findProjectByPid", 1, //int nParams, size of arrays that follow parms, //parms, //const char * const *paramValues, array of string values plens, //const int *paramLengths, pformats, //const int *paramFormats, 1); //int resultFormat); 0 == text, 1 == binary sem_post(&fpbp_sem); ExecStatusType qres = PQresultStatus(rset); //expecting a single row returned if (qres != PGRES_TUPLES_OK || PQntuples(rset) != 1) { log(LSQL, "findProjectByPid: %s\n", PQerrorMessage(dbConn)); } else { uint32_t proto = ntohl(*(uint32_t*)PQgetvalue(rset, 0, 10)); if (proto == PROTOCOL_VERSION) { const char *hash = PQgetvalue(rset, 0, 1); uint64_t snapupdateid = ntohll(*(uint64_t*)PQgetvalue(rset, 0, 3)); // logln(LDEBUG, "in joinProject: " + lpid + " " + hash + " " + snapupdateid + " " + rs.getString(5) + " " + rs.getString(7)); if (snapupdateid > 0) { //pid is a snapshot pid //this should now be an error condition //logln(LINFO4, "Attempt to join snapshot " + lpid + " forking instead"); //return forkProject(c, rs.getLong(4), rs.getString(7) + " + " + rs.getString(5)); c->send_error("can't join a snapshot, you MUST fork a snapshot"); log(LERROR, "attempted to join a snapshop instead of forking\n"); return -1; } c->setPid(lpid); c->setHash(hash); const char *gpid = PQgetvalue(rset, 0, 2); c->setGpid(gpid); const char *owner = PQgetvalue(rset, 0, 9); const char *desc = PQgetvalue(rset, 0, 4); const char *pdesc = PQgetvalue(rset, 0, 6); uint32_t parent = ntohl(*(uint32_t*)PQgetvalue(rset, 0, 5)); Project *pinfo; sem_wait(&map_sem); map<uint32_t,Project*>::iterator pi = pid_project_map.find(lpid); if (pi != pid_project_map.end()) { pinfo = (*pi).second; } else { pinfo = new Project(lpid, ""); pid_project_map[lpid] = pinfo; } sem_post(&map_sem); //now make sure all project info is consistent with database, even if Project record already existed pinfo->desc = desc; pinfo->gpid = gpid; pinfo->hash = hash; pinfo->parent = parent; pinfo->pdesc = pdesc; pinfo->snapupdateid = snapupdateid; pinfo->pub = ntohll(*(uint64_t*)PQgetvalue(rset, 0, 7)); pinfo->sub = ntohll(*(uint64_t*)PQgetvalue(rset, 0, 8)); pinfo->owner = owner; pinfo->proto = proto; ClientSet *cs = projects.get(lpid); pinfo->connected = cs ? cs->size() : 0; if (c->getUser() == owner) { //project owner gets full perms, regardless of user, project, or requested perms log(LINFO3, "Project Owner joined! yay!"); c->setPub(FULL_PERMISSIONS); c->setSub(FULL_PERMISSIONS); } else { //effective permissions are user perms ANDed with project perms ANDed with the perms requested by the user c->setPub(pinfo->pub & c->getUserPub() & c->getReqPub()); c->setSub(pinfo->sub & c->getUserSub() & c->getReqSub()); } foundPid = true; } } PQclear(rset); if (foundPid) { projects.addClient(c); rval = 0; } else { // log(LERROR, "ERROR: attempt to join a non-existant project: %u\n", lpid); } return rval; }
/** * getProjectList generates a list of projects on this server, each list (vector) item is * actually a pinfo (project info) object, the list does NOT contain all projects, but * only contains projects relevant to the binary that is currently loaded in IDA * @param phash the IDA generated hash that is unique among the analysis files * @return a vector of project info objects for the provided phash */ vector<const Project*> *DatabaseConnectionManager::getProjectList(const string &phash) { vector<const Project*> *plist = new vector<const Project*>; static const int plens[1] = {0}; static const int pformats[1] = {0}; const char * const parms[1] = {phash.c_str()}; sem_wait(&fpbh_sem); PGresult *rset = PQexecPrepared(dbConn, "findProjectsByHash", 1, //int nParams, size of arrays that follow parms, //parms, //const char * const *paramValues, array of string values plens, //const int *paramLengths, pformats, //const int *paramFormats, 1); //int resultFormat); 0 == text, 1 == binary sem_post(&fpbh_sem); ExecStatusType qres = PQresultStatus(rset); if (qres != PGRES_TUPLES_OK) { log(LSQL, "findProjectsByHash: %s\n", PQerrorMessage(dbConn)); } else { int rows = PQntuples(rset); for (int i = 0; i < rows; i++) { uint32_t proto = ntohl(*(uint32_t*)PQgetvalue(rset, i, 10)); if (proto != PROTOCOL_VERSION) { continue; } uint32_t lpid = ntohl(*(uint32_t*)PQgetvalue(rset, i, 0)); char *desc = PQgetvalue(rset, i, 3); int32_t parent = -1; if (!PQgetisnull(rset, i, 4)) { parent = ntohl(*(int32_t*)PQgetvalue(rset, i, 4)); } uint64_t snapupdateid = ntohll(*(uint64_t*)PQgetvalue(rset, i, 5)); const char *pdesc = ""; if (!PQgetisnull(rset, i, 6)) { pdesc = PQgetvalue(rset, i, 6); } Project *pinfo; sem_wait(&map_sem); map<uint32_t,Project*>::iterator pi = pid_project_map.find(lpid); if (pi != pid_project_map.end()) { pinfo = (*pi).second; } else { pinfo = new Project(lpid, ""); pid_project_map[lpid] = pinfo; } sem_post(&map_sem); const char *gpid = PQgetvalue(rset, 0, 2); //now make sure all project info is consistent with database, even if Project record already existed pinfo->desc = desc; pinfo->gpid = gpid; pinfo->hash = phash; pinfo->parent = parent; pinfo->pdesc = pdesc; pinfo->snapupdateid = snapupdateid; pinfo->pub = ntohll(*(uint64_t*)PQgetvalue(rset, i, 7)); pinfo->sub = ntohll(*(uint64_t*)PQgetvalue(rset, i, 8)); pinfo->owner = PQgetvalue(rset, i, 9); pinfo->proto = proto; ClientSet *cs = projects.get(lpid); pinfo->connected = cs ? cs->size() : 0; plist->push_back(pinfo); } } PQclear(rset); return plist; }
/** * getProject gets informatio related to a local project * @param pid the local pid of a project to get info on * @return a project info object for the provided pid */ const Project *DatabaseConnectionManager::getProject(uint32_t pid) { Project *pinfo = NULL; static const int plens[1] = {4}; static const int pformats[1] = {1}; pid = htonl(pid); const char * const parms[1] = {(char*)&pid}; sem_wait(&fpbp_sem); PGresult *rset = PQexecPrepared(dbConn, "findProjectByPid", 1, //int nParams, size of arrays that follow parms, //parms, //const char * const *paramValues, array of string values plens, //const int *paramLengths, pformats, //const int *paramFormats, 1); //int resultFormat); 0 == text, 1 == binary sem_post(&fpbp_sem); ExecStatusType qres = PQresultStatus(rset); //expecting a single row returned if (qres != PGRES_TUPLES_OK || PQntuples(rset) != 1) { log(LSQL, "findProjectByPid: %s\n", PQerrorMessage(dbConn)); } else { uint32_t proto = ntohl(*(uint32_t*)PQgetvalue(rset, 0, 10)); if (proto == PROTOCOL_VERSION) { uint32_t lpid = ntohl(*(uint32_t*)PQgetvalue(rset, 0, 0)); char *desc = PQgetvalue(rset, 0, 4); int32_t parent = -1; if (!PQgetisnull(rset, 0, 5)) { parent = ntohl(*(int32_t*)PQgetvalue(rset, 0, 5)); } uint64_t snapupdateid = ntohll(*(uint64_t*)PQgetvalue(rset, 0, 3)); const char *pdesc = ""; if (!PQgetisnull(rset, 0, 6)) { pdesc = PQgetvalue(rset, 0, 6); } const char *hash = PQgetvalue(rset, 0, 1); const char *owner = PQgetvalue(rset, 0, 9); sem_wait(&map_sem); map<uint32_t,Project*>::iterator pi = pid_project_map.find(lpid); if (pi != pid_project_map.end()) { pinfo = (*pi).second; } else { pinfo = new Project(lpid, ""); pid_project_map[lpid] = pinfo; } sem_post(&map_sem); const char *gpid = PQgetvalue(rset, 0, 2); //now make sure all project info is consistent with database, even if Project record already existed pinfo->desc = desc; pinfo->gpid = gpid; pinfo->hash = hash; pinfo->parent = parent; pinfo->pdesc = pdesc; pinfo->snapupdateid = snapupdateid; pinfo->pub = ntohll(*(uint64_t*)PQgetvalue(rset, 0, 7)); pinfo->sub = ntohll(*(uint64_t*)PQgetvalue(rset, 0, 8)); pinfo->owner = owner; pinfo->proto = proto; ClientSet *cs = projects.get(lpid); pinfo->connected = cs ? cs->size() : 0; } } PQclear(rset); return pinfo; }
int main(int argc, char ** argv) { string data; stringstream buf; unsigned int i; Stats stats; ClientSet clients; SocketSet sockets; Switchboard switchboard; ArgumentSet arguments(argc, argv); if (arguments.isset("version") || arguments.isset("v")) { cout << PACKAGE_NAME << " version " << PACKAGE_VERSION << endl; return 0; } if (arguments.isset("help") || arguments.isset("h")) { cout << endl; cout << "usage: istatd [-a HOST] [-p PORT]" << endl; cout << endl; cout << " -d run in background" << endl; cout << " -h print this help text" << endl; cout << " -v print version number" << endl; cout << endl; cout << " -c FILE custom config file location" << endl; cout << " -a HOST listen on this address" << endl; cout << " -p PORT listen on this port" << endl; cout << " -u USER change running user" << endl; cout << " -g GROUP change running group" << endl; cout << endl; cout << " --pid=FILE custom pid file location" << endl; cout << " --cache=DIR custom cache file location" << endl; cout << " --socket=FILE custom socket file location" << endl; cout << " --code=CODE custom lock code" << endl; cout << endl; return 0; } // Load and parse configuration Config config(arguments.get("c", CONFIG_FILE_PATH)); config.parse(); config.validate(); // Load configuration properties from command line and config file bool arg_d = arguments.isset("d"); string cf_network_addr = arguments.get("a", config.get("network_addr", "0.0.0.0")); string cf_network_port = arguments.get("p", config.get("network_port", "5109")); string cf_server_user = arguments.get("u", config.get("server_user", "istat")); string cf_server_group = arguments.get("g", config.get("server_group", "istat")); string cf_server_pid = arguments.get("pid", config.get("server_pid", "")); string cf_cache_dir = arguments.get("cache", config.get("cache_dir", "/var/cache/istat")); string cf_server_socket = arguments.get("socket", config.get("server_socket", "/tmp/istatd.sock")); #ifdef HAVE_LIBKSTAT if(-1 == kstat_init()) return 1; #endif Daemon unixdaemon(cf_server_pid, cf_server_socket, cf_cache_dir); Socket listener(cf_network_addr, to_int(cf_network_port)); SignalResponder signalresponder(&sockets, &listener, &unixdaemon, &clients); ::pn_signalresponder = &signalresponder; // Create socket, pid file and put in background if desired unixdaemon.create(arg_d, cf_server_user, cf_server_group); // Get old sessions from disk cache clients.read_cache(cf_cache_dir); // Clear cache of saved sessions if (arguments.isset("clear-sessions")) { clients.clear_cache(); return 0; } signal(SIGHUP, handler); signal(SIGUSR1, handler); signal(SIGINT, handler); signal(SIGTERM, handler); signal(SIGPIPE, handler); if (!listener.listen()) return 1; sockets += listener; // Add disks for monitoring if (config.get("monitor_disk") != "") { // No array found. Add value given. stats.add_disk(config.get("monitor_disk").c_str()); } else { // Array found. Add all values in the array. for (i = 0; i < config.get_property("monitor_disk").get_array_size(); i++) { stats.add_disk(config.get_property("monitor_disk").get_array(i).c_str()); } } // Add network interfaces for monitoring if (config.get("monitor_net") != "") { // No array found. Add value given. stats.add_net(config.get("monitor_net").c_str()); } else { // Array found. Add all values in the array. for (i = 0; i < config.get_property("monitor_net").get_array_size(); i++) { stats.add_net(config.get_property("monitor_net").get_array(i).c_str()); } } #ifdef HAVE_LIBSENSORS unsigned int sensor_num; struct sensor_data sensor_data; sensor_num = get_sensor_num(); for (i = 0; i < sensor_num; i++) { get_sensor_data(i, &sensor_data); stats.add_sensor(&sensor_data); } #endif #ifdef HAVE_QNAPTEMP struct sensor_data sensor_data; if (have_qnaptemp()) { get_qnaptemp(0, &sensor_data); stats.add_sensor(&sensor_data); } #endif while (1) { stats.update_system_stats(); if (sockets.get_status(1)) { if (sockets == listener) { Socket new_socket(listener.accept()); sockets += new_socket; } else { Socket active_socket(sockets.get_ready()); if (active_socket.receive(data, 1024)) { switchboard.parse(&sockets, &clients, &config, &active_socket, &stats, &arguments, data); } else { sockets -= active_socket; } } } } ::pn_signalresponder = NULL; return 0; }