From: Maksym Mamontov Date: Sun, 17 Jan 2021 17:48:47 +0000 (+0200) Subject: Move projects back into subfolder. X-Git-Url: https://git.stg.codes/stg.git/commitdiff_plain/0907aa4037b12b6b88ee24495d4577a064d4f8db?hp=4271ab433cd55bbd2612292bcf39e4dc3d7274f1 Move projects back into subfolder. --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ca56686..30deed57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,25 +143,8 @@ endif () include_directories ( include ) add_subdirectory ( libs ) - -if ( BUILD_RSCRIPTD ) - add_subdirectory ( rscriptd ) -endif ( BUILD_RSCRIPTD ) - -if ( BUILD_SGAUTH ) - add_subdirectory ( sgauth ) -endif ( BUILD_SGAUTH ) - -if ( BUILD_SGCONF ) - add_subdirectory ( sgconf ) -endif ( BUILD_SGCONF ) - -if ( BUILD_STG ) - add_subdirectory ( stargazer ) -endif ( BUILD_STG ) +add_subdirectory ( projects ) if ( BUILD_TESTS ) add_subdirectory ( tests ) endif ( BUILD_TESTS ) - -add_custom_target (cppcheck COMMAND cppcheck --enable=all --std=c++14 ${CMAKE_SOURCE_DIR}/rlm_stg ${CMAKE_SOURCE_DIR}/rscriptd ${CMAKE_SOURCE_DIR}/sgauth ${CMAKE_SOURCE_DIR}/sgconf ${CMAKE_SOURCE_DIR}/sgconv ${CMAKE_SOURCE_DIR}/stargazer) diff --git a/functest/test.sh b/functest/test.sh index 00728cc8..83ec1b99 100755 --- a/functest/test.sh +++ b/functest/test.sh @@ -16,7 +16,7 @@ then exit -1 fi -STGPATH="$BASEPATH/stg/build/stargazer" +STGPATH="$BASEPATH/stg/build/projects/stargazer" cp "stuff/stargazer-files.conf" "$STGPATH/stargazer.conf" cp "stuff/rules" "$STGPATH/" diff --git a/functest/test_admins.sh b/functest/test_admins.sh index 3bedb37a..562d5e1d 100755 --- a/functest/test_admins.sh +++ b/functest/test_admins.sh @@ -4,7 +4,7 @@ source `dirname $0`/functions BASEPATH=$1 -SGCONFPATH="$BASEPATH/stg/build/sgconf" +SGCONFPATH="$BASEPATH/stg/build/projects/sgconf" printf "Check initial admin list... " diff --git a/functest/test_server_info.sh b/functest/test_server_info.sh index f41f470e..ac729b9f 100755 --- a/functest/test_server_info.sh +++ b/functest/test_server_info.sh @@ -4,7 +4,7 @@ source `dirname $0`/functions BASEPATH=$1 -SGCONFPATH="$BASEPATH/stg/build/sgconf" +SGCONFPATH="$BASEPATH/stg/build/projects/sgconf" printf "Check server info... " diff --git a/functest/test_services.sh b/functest/test_services.sh index aaca9d01..97c3c744 100755 --- a/functest/test_services.sh +++ b/functest/test_services.sh @@ -4,7 +4,7 @@ source `dirname $0`/functions BASEPATH=$1 -SGCONFPATH="$BASEPATH/stg/build/sgconf" +SGCONFPATH="$BASEPATH/stg/build/projects/sgconf" printf "Check initial service list... " diff --git a/projects/rscriptd/CMakeLists.txt b/projects/rscriptd/CMakeLists.txt new file mode 100644 index 00000000..9228b370 --- /dev/null +++ b/projects/rscriptd/CMakeLists.txt @@ -0,0 +1,10 @@ +set ( CPP_FILES main.cpp listener.cpp pidfile.cpp ) + +set ( THREADS_PREFER_PTHREAD_FLAG ON ) +find_package ( Threads REQUIRED ) + +add_executable ( rscriptd ${CPP_FILES} ) + +target_link_libraries ( rscriptd scriptexecuter conffiles logger crypto common Threads::Threads ) + +# TODO: install diff --git a/projects/rscriptd/listener.cpp b/projects/rscriptd/listener.cpp new file mode 100644 index 00000000..3d8fd48e --- /dev/null +++ b/projects/rscriptd/listener.cpp @@ -0,0 +1,513 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Author : Boris Mikhailenko + * Author : Maxim Mamontov + */ + +#include +#include // readv +#include // for historical versions of BSD +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "stg/scriptexecuter.h" +#include "stg/locker.h" +#include "stg/common.h" +#include "stg/const.h" +#include "listener.h" + +void InitEncrypt(BLOWFISH_CTX * ctx, const std::string & password); +void Decrypt(BLOWFISH_CTX * ctx, char * dst, const char * src, int len8); + +//----------------------------------------------------------------------------- +LISTENER::LISTENER() + : WriteServLog(STG::Logger::get()), + port(0), + running(false), + receiverStopped(true), + processorStopped(true), + userTimeout(0), + listenSocket(0), + version("rscriptd listener v.1.2") +{ +pthread_mutex_init(&mutex, NULL); +} +//----------------------------------------------------------------------------- +void LISTENER::SetPassword(const std::string & p) +{ +password = p; +printfd(__FILE__, "Encryption initiated with password \'%s\'\n", password.c_str()); +InitEncrypt(&ctxS, password); +} +//----------------------------------------------------------------------------- +bool LISTENER::Start() +{ +printfd(__FILE__, "LISTENER::Start()\n"); +running = true; + +if (PrepareNet()) + { + return true; + } + +if (receiverStopped) + { + if (pthread_create(&receiverThread, NULL, Run, this)) + { + errorStr = "Cannot create thread."; + return true; + } + } + +if (processorStopped) + { + if (pthread_create(&processorThread, NULL, RunProcessor, this)) + { + errorStr = "Cannot create thread."; + return true; + } + } + +errorStr = ""; + +return false; +} +//----------------------------------------------------------------------------- +bool LISTENER::Stop() +{ +running = false; + +printfd(__FILE__, "LISTENER::Stop()\n"); + +struct timespec ts = {0, 500000000}; +nanosleep(&ts, NULL); + +if (!processorStopped) + { + //5 seconds to thread stops itself + for (int i = 0; i < 25 && !processorStopped; i++) + { + struct timespec ts = {0, 200000000}; + nanosleep(&ts, NULL); + } + + //after 5 seconds waiting thread still running. now killing it + if (!processorStopped) + { + //TODO pthread_cancel() + if (pthread_kill(processorThread, SIGINT)) + { + errorStr = "Cannot kill thread."; + return true; + } + printfd(__FILE__, "LISTENER killed Timeouter\n"); + } + } + +if (!receiverStopped) + { + //5 seconds to thread stops itself + for (int i = 0; i < 25 && !receiverStopped; i++) + { + struct timespec ts = {0, 200000000}; + nanosleep(&ts, NULL); + } + + //after 5 seconds waiting thread still running. now killing it + if (!receiverStopped) + { + //TODO pthread_cancel() + if (pthread_kill(receiverThread, SIGINT)) + { + errorStr = "Cannot kill thread."; + return true; + } + printfd(__FILE__, "LISTENER killed Run\n"); + } + } + +pthread_join(receiverThread, NULL); +pthread_join(processorThread, NULL); + +pthread_mutex_destroy(&mutex); + +FinalizeNet(); + +std::for_each(users.begin(), users.end(), DisconnectUser(*this)); + +printfd(__FILE__, "LISTENER::Stoped successfully.\n"); + +return false; +} +//----------------------------------------------------------------------------- +void * LISTENER::Run(void * d) +{ +sigset_t signalSet; +sigfillset(&signalSet); +pthread_sigmask(SIG_BLOCK, &signalSet, NULL); + +LISTENER * listener = static_cast(d); + +listener->Runner(); + +return NULL; +} +//----------------------------------------------------------------------------- +void LISTENER::Runner() +{ +receiverStopped = false; + +while (running) + { + RecvPacket(); + } + +receiverStopped = true; +} +//----------------------------------------------------------------------------- +void * LISTENER::RunProcessor(void * d) +{ +sigset_t signalSet; +sigfillset(&signalSet); +pthread_sigmask(SIG_BLOCK, &signalSet, NULL); + +LISTENER * listener = static_cast(d); + +listener->ProcessorRunner(); + +return NULL; +} +//----------------------------------------------------------------------------- +void LISTENER::ProcessorRunner() +{ +processorStopped = false; + +while (running) + { + struct timespec ts = {0, 500000000}; + nanosleep(&ts, NULL); + if (!pending.empty()) + ProcessPending(); + ProcessTimeouts(); + } + +processorStopped = true; +} +//----------------------------------------------------------------------------- +bool LISTENER::PrepareNet() +{ +listenSocket = socket(AF_INET, SOCK_DGRAM, 0); + +if (listenSocket < 0) + { + errorStr = "Cannot create socket."; + return true; + } + +struct sockaddr_in listenAddr; +listenAddr.sin_family = AF_INET; +listenAddr.sin_port = htons(port); +listenAddr.sin_addr.s_addr = inet_addr("0.0.0.0"); + +if (bind(listenSocket, (struct sockaddr*)&listenAddr, sizeof(listenAddr)) < 0) + { + errorStr = "LISTENER: Bind failed."; + return true; + } + +printfd(__FILE__, "LISTENER::PrepareNet() >>>> Start successfull.\n"); + +return false; +} +//----------------------------------------------------------------------------- +bool LISTENER::FinalizeNet() +{ +close(listenSocket); + +return false; +} +//----------------------------------------------------------------------------- +bool LISTENER::RecvPacket() +{ +struct iovec iov[2]; + +char buffer[RS_MAX_PACKET_LEN]; +RS::PACKET_HEADER packetHead; + +iov[0].iov_base = reinterpret_cast(&packetHead); +iov[0].iov_len = sizeof(packetHead); +iov[1].iov_base = buffer; +iov[1].iov_len = sizeof(buffer) - sizeof(packetHead); + +size_t dataLen = 0; +while (dataLen < sizeof(buffer)) + { + if (!WaitPackets(listenSocket)) + { + if (!running) + return false; + continue; + } + int portion = readv(listenSocket, iov, 2); + if (portion < 0) + { + return true; + } + dataLen += portion; + } + +if (CheckHeader(packetHead)) + { + printfd(__FILE__, "Invalid packet or incorrect protocol version!\n"); + return true; + } + +std::string userLogin((char *)packetHead.login); +PendingData data; +data.login = userLogin; +data.ip = ntohl(packetHead.ip); +data.id = ntohl(packetHead.id); + +if (packetHead.packetType == RS_ALIVE_PACKET) + { + data.type = PendingData::ALIVE; + } +else if (packetHead.packetType == RS_CONNECT_PACKET) + { + data.type = PendingData::CONNECT; + if (GetParams(buffer, data)) + { + return true; + } + } +else if (packetHead.packetType == RS_DISCONNECT_PACKET) + { + data.type = PendingData::DISCONNECT; + if (GetParams(buffer, data)) + { + return true; + } + } + +STG_LOCKER lock(&mutex); +pending.push_back(data); + +return false; +} +//----------------------------------------------------------------------------- +bool LISTENER::GetParams(char * buffer, UserData & data) +{ +RS::PACKET_TAIL packetTail; + +Decrypt(&ctxS, (char *)&packetTail, buffer, sizeof(packetTail) / 8); + +if (strncmp((char *)packetTail.magic, RS_ID, RS_MAGIC_LEN)) + { + printfd(__FILE__, "Invalid crypto magic\n"); + return true; + } + +std::ostringstream params; +params << "\"" << data.login << "\" " + << inet_ntostring(data.ip) << " " + << data.id << " " + << (char *)packetTail.params; + +data.params = params.str(); + +return false; +} +//----------------------------------------------------------------------------- +void LISTENER::ProcessPending() +{ +std::list::iterator it(pending.begin()); +size_t count = 0; +printfd(__FILE__, "Pending: %d\n", pending.size()); +while (it != pending.end() && count < 256) + { + std::vector::iterator uit( + std::lower_bound( + users.begin(), + users.end(), + it->login) + ); + if (it->type == PendingData::CONNECT) + { + printfd(__FILE__, "Connect packet\n"); + if (uit == users.end() || uit->login != it->login) + { + printfd(__FILE__, "Connect new user '%s'\n", it->login.c_str()); + // Add new user + Connect(*it); + users.insert(uit, AliveData(static_cast(*it))); + } + else if (uit->login == it->login) + { + printfd(__FILE__, "Update existing user '%s'\n", it->login.c_str()); + // Update already existing user + time(&uit->lastAlive); + uit->params = it->params; + } + else + { + printfd(__FILE__, "Hmmm... Strange connect for '%s'\n", it->login.c_str()); + } + } + else if (it->type == PendingData::ALIVE) + { + printfd(__FILE__, "Alive packet\n"); + if (uit != users.end() && uit->login == it->login) + { + printfd(__FILE__, "Alive user '%s'\n", it->login.c_str()); + // Update existing user + time(&uit->lastAlive); + } + else + { + printfd(__FILE__, "Alive user '%s' is not found\n", it->login.c_str()); + } + } + else if (it->type == PendingData::DISCONNECT) + { + printfd(__FILE__, "Disconnect packet\n"); + if (uit != users.end() && uit->login == it->login.c_str()) + { + printfd(__FILE__, "Disconnect user '%s'\n", it->login.c_str()); + // Disconnect existing user + uit->params = it->params; + Disconnect(*uit); + users.erase(uit); + } + else + { + printfd(__FILE__, "Cannot find user '%s' for disconnect\n", it->login.c_str()); + } + } + else + { + printfd(__FILE__, "Unknown packet type\n"); + } + ++it; + ++count; + } +STG_LOCKER lock(&mutex); +pending.erase(pending.begin(), it); +} +//----------------------------------------------------------------------------- +void LISTENER::ProcessTimeouts() +{ +const std::vector::iterator it( + std::stable_partition( + users.begin(), + users.end(), + IsNotTimedOut(userTimeout) + ) + ); + +if (it != users.end()) + { + printfd(__FILE__, "Total users: %d, users to disconnect: %d\n", users.size(), std::distance(it, users.end())); + + std::for_each( + it, + users.end(), + DisconnectUser(*this) + ); + + users.erase(it, users.end()); + } +} +//----------------------------------------------------------------------------- +bool LISTENER::Connect(const UserData & data) const +{ +printfd(__FILE__, "Connect %s\n", data.login.c_str()); +if (access(scriptOnConnect.c_str(), X_OK) == 0) + { + if (ScriptExec((scriptOnConnect + " " + data.params).c_str())) + { + WriteServLog("Script %s cannot be executed for an unknown reason.", scriptOnConnect.c_str()); + return true; + } + } +else + { + WriteServLog("Script %s cannot be executed. File not found.", scriptOnConnect.c_str()); + return true; + } +return false; +} +//----------------------------------------------------------------------------- +bool LISTENER::Disconnect(const UserData & data) const +{ +printfd(__FILE__, "Disconnect %s\n", data.login.c_str()); +if (access(scriptOnDisconnect.c_str(), X_OK) == 0) + { + if (ScriptExec((scriptOnDisconnect + " " + data.params).c_str())) + { + WriteServLog("Script %s cannot be executed for an unknown reson.", scriptOnDisconnect.c_str()); + return true; + } + } +else + { + WriteServLog("Script %s cannot be executed. File not found.", scriptOnDisconnect.c_str()); + return true; + } +return false; +} +//----------------------------------------------------------------------------- +bool LISTENER::CheckHeader(const RS::PACKET_HEADER & header) const +{ +if (strncmp((char *)header.magic, RS_ID, RS_MAGIC_LEN)) + { + return true; + } +if (strncmp((char *)header.protoVer, "02", RS_PROTO_VER_LEN)) + { + return true; + } +return false; +} +//----------------------------------------------------------------------------- +inline +void InitEncrypt(BLOWFISH_CTX * ctx, const std::string & password) +{ +unsigned char keyL[PASSWD_LEN]; +memset(keyL, 0, PASSWD_LEN); +strncpy((char *)keyL, password.c_str(), PASSWD_LEN); +Blowfish_Init(ctx, keyL, PASSWD_LEN); +} +//----------------------------------------------------------------------------- +inline +void Decrypt(BLOWFISH_CTX * ctx, char * dst, const char * src, int len8) +{ +if (dst != src) + memcpy(dst, src, len8 * 8); + +for (int i = 0; i < len8; i++) + Blowfish_Decrypt(ctx, (uint32_t *)(dst + i * 8), (uint32_t *)(dst + i * 8 + 4)); +} +//----------------------------------------------------------------------------- diff --git a/projects/rscriptd/listener.h b/projects/rscriptd/listener.h new file mode 100644 index 00000000..7fb71ee3 --- /dev/null +++ b/projects/rscriptd/listener.h @@ -0,0 +1,144 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Author : Boris Mikhailenko + * Author : Maxim Mamontov + */ + +#include + +#include +#include +#include +#include +#include + +#include "stg/blowfish.h" +#include "stg/rs_packets.h" +#include "stg/logger.h" + +struct UserData +{ + std::string params; + std::string login; + uint32_t ip; + uint32_t id; +}; + +struct PendingData : public UserData +{ + enum {CONNECT, ALIVE, DISCONNECT} type; +}; + +struct AliveData : public UserData +{ + explicit AliveData(const UserData & data) + : UserData(data), + lastAlive(time(NULL)) + {}; + bool operator<(const std::string & rvalue) const { return login < rvalue; }; + time_t lastAlive; +}; + +class IsNotTimedOut : public std::unary_function { + public: + explicit IsNotTimedOut(double to) : timeout(to), now(time(NULL)) {} + bool operator()(const AliveData & data) const + { + return difftime(now, data.lastAlive) < timeout; + } + private: + double timeout; + time_t now; +}; + +class LISTENER +{ +public: + LISTENER(); + ~LISTENER(){}; + + void SetPort(uint16_t p) { port = p; }; + void SetPassword(const std::string & p); + void SetUserTimeout(int t) { userTimeout = t; }; + void SetScriptOnConnect(const std::string & script) { scriptOnConnect = script; }; + void SetScriptOnDisconnect(const std::string & script) { scriptOnDisconnect = script; }; + + bool Start(); + bool Stop(); + bool IsRunning() const { return !receiverStopped && !processorStopped; }; + + const std::string & GetStrError() const { return errorStr; }; + const std::string & GetVersion() const { return version; }; + +private: + // Threading stuff + static void * Run(void * self); + static void * RunProcessor(void * self); + void Runner(); + void ProcessorRunner(); + // Networking stuff + bool PrepareNet(); + bool FinalizeNet(); + bool RecvPacket(); + // Parsing stuff + bool CheckHeader(const RS::PACKET_HEADER & header) const; + bool GetParams(char * buffer, UserData & data); + // Processing stuff + void ProcessPending(); + void ProcessTimeouts(); + bool Disconnect(const UserData & data) const; + bool Connect(const UserData & data) const; + + BLOWFISH_CTX ctxS; + STG::Logger& WriteServLog; + + mutable std::string errorStr; + std::string scriptOnConnect; + std::string scriptOnDisconnect; + std::string password; + uint16_t port; + + bool running; + bool receiverStopped; + bool processorStopped; + std::vector users; + std::list pending; + int userTimeout; + + pthread_t receiverThread; + pthread_t processorThread; + pthread_mutex_t mutex; + + int listenSocket; + + std::string version; + + friend class DisconnectUser; +}; + +class DisconnectUser : public std::unary_function { + public: + explicit DisconnectUser(LISTENER & l) : listener(l) {}; + void operator()(const UserData & data) + { + listener.Disconnect(data); + }; + private: + LISTENER & listener; +}; +//----------------------------------------------------------------------------- diff --git a/projects/rscriptd/main.cpp b/projects/rscriptd/main.cpp new file mode 100644 index 00000000..e7c54eec --- /dev/null +++ b/projects/rscriptd/main.cpp @@ -0,0 +1,337 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Author : Boris Mikhailenko + * Author : Maxim Mamontov + */ + + /* + $Revision: 1.19 $ + $Author: faust $ + $Date: 2010/09/10 06:37:45 $ + */ + +#include +#include +#include +#include +#include +#include // creat +#include + +#include +#include +#include +#include +#include // strerror +#include + +#include "stg/common.h" +#include "stg/logger.h" +#include "stg/scriptexecuter.h" +#include "stg/conffiles.h" +#include "stg/version.h" +#include "listener.h" +#include "pidfile.h" + +#ifdef DEBUG +# define MAIN_DEBUG 1 +# define NO_DAEMON 1 +#endif + +#define START_FILE "/._ST_ART_ED_" + +std::set executersPid; +volatile time_t stgTime = time(NULL); + +//----------------------------------------------------------------------------- +void KillExecuters() +{ +std::set::iterator pid; +pid = executersPid.begin(); +while (pid != executersPid.end()) + { + printfd(__FILE__, "KillExecuters pid=%d\n", *pid); + kill(*pid, SIGUSR1); + ++pid; + } +} +//----------------------------------------------------------------------------- +#if defined(LINUX) || defined(DARWIN) +int StartScriptExecuter(char * procName, int msgKey, int * msgID) +#else +int StartScriptExecuter(char *, int msgKey, int * msgID) +#endif +{ +auto & WriteServLog = STG::Logger::get(); + +if (*msgID == -11) // If msgID == -11 - first call. Create queue + { + for (int i = 0; i < 2; i++) + { + *msgID = msgget(msgKey, IPC_CREAT | IPC_EXCL | 0600); + + if (*msgID == -1) + { + *msgID = msgget(msgKey, 0); + if (*msgID == -1) + { + WriteServLog("Message queue not created."); + return -1; + } + else + { + msgctl(*msgID, IPC_RMID, NULL); + } + } + else + { + WriteServLog("Message queue created successfully. msgKey=%d msgID=%d", msgKey, *msgID); + break; + } + } + } + +pid_t executerPid = fork(); + +switch (executerPid) + { + case -1: // Failure + WriteServLog("Fork error!"); + return -1; + + case 0: // Child + //close(0); + //close(1); + //close(2); + //setsid(); +#if defined(LINUX) || defined(DARWIN) + Executer(*msgID, executerPid, procName); +#else + Executer(*msgID, executerPid); +#endif + return 1; + + default: // Parent + if (executersPid.empty()) +#if defined(LINUX) || defined(DARWIN) + Executer(*msgID, executerPid, NULL); +#else + Executer(*msgID, executerPid); +#endif + executersPid.insert(executerPid); + } +return 0; +} +//----------------------------------------------------------------------------- +void StopScriptExecuter(int msgID) +{ +auto & WriteServLog = STG::Logger::get(); + +for (int i = 0; i < 5; ++i) + { + struct msqid_ds data; + if (msgctl(msgID, IPC_STAT, &data)) + { + int e = errno; + printfd(__FILE__, "StopScriptExecuter() - msgctl for IPC_STAT failed: '%s'\n", strerror(e)); + WriteServLog( "Failed to check queue emptiness: '%s'", strerror(e)); + break; + } + + WriteServLog("Messages in queue: %d", data.msg_qnum); + + if (data.msg_qnum == 0) + break; + + struct timespec ts = {1, 0}; + nanosleep(&ts, NULL); + } + +if (msgctl(msgID, IPC_RMID, NULL)) + { + int e = errno; + printfd(__FILE__, "StopScriptExecuter() - msgctl for IPC_STAT failed: '%s'\n", strerror(e)); + WriteServLog("Failed to remove queue: '%s'", strerror(e)); + } +else + { + WriteServLog("Queue removed successfully."); + } + +KillExecuters(); +} +//----------------------------------------------------------------------------- +#ifdef NO_DAEMON +int ForkAndWait(const std::string &) +#else +int ForkAndWait(const std::string & confDir) +#endif +{ +#ifndef NO_DAEMON +pid_t childPid = fork(); + +switch (childPid) + { + case -1: // Failure + return -1; + break; + + case 0: // Child + //close(0); + close(1); + close(2); + setsid(); + break; + + default: // Parent + exit(1); + break; + } +#endif +return 0; +} +//----------------------------------------------------------------------------- +int main(int argc, char * argv[]) +{ +CONFIGFILE * cfg = NULL; +LISTENER * listener = NULL; +int msgID = -11; +int execNum = 0; +int execMsgKey = 0; + +std::string logFileName; +std::string confDir; +std::string password; +std::string onConnect; +std::string onDisconnect; +int port; +int userTimeout; + +if (getuid()) + { + printf("You must be root. Exit.\n"); + exit(1); + } + +if (argc == 2) + cfg = new CONFIGFILE(argv[1]); +else + cfg = new CONFIGFILE("/etc/rscriptd/rscriptd.conf"); + +if (cfg->Error()) + { + auto & WriteServLog = STG::Logger::get(); + WriteServLog.setFileName("/var/log/rscriptd.log"); + WriteServLog("Error reading config file!"); + delete cfg; + return EXIT_FAILURE; + } + +cfg->ReadString("LogFileName", &logFileName, "/var/log/rscriptd.log"); +cfg->ReadInt("ExecutersNum", &execNum, 1); +cfg->ReadInt("ExecMsgKey", &execMsgKey, 5555); +cfg->ReadString("ConfigDir", &confDir, "/etc/rscriptd"); +cfg->ReadString("Password", &password, ""); +cfg->ReadInt("Port", &port, 5555); +cfg->ReadInt("UserTimeout", &userTimeout, 60); +cfg->ReadString("ScriptOnConnect", &onConnect, "/etc/rscriptd/OnConnect"); +cfg->ReadString("ScriptOnDisconnect", &onDisconnect, "/etc/rscriptd/OnDisconnect"); + +if (ForkAndWait(confDir) < 0) + { + auto & WriteServLog = STG::Logger::get(); + WriteServLog("Fork error!"); + delete cfg; + return EXIT_FAILURE; + } + +auto & WriteServLog = STG::Logger::get(); +PIDFile pidFile("/var/run/rscriptd.pid"); +WriteServLog.setFileName(logFileName); +WriteServLog("rscriptd v. %s", SERVER_VERSION); + +for (int i = 0; i < execNum; i++) + { + int ret = StartScriptExecuter(argv[0], execMsgKey, &msgID); + if (ret < 0) + { + STG::Logger::get()("Start Script Executer error!"); + delete cfg; + return EXIT_FAILURE; + } + if (ret == 1) + { + delete cfg; + return EXIT_SUCCESS; + } + } + +listener = new LISTENER(); +listener->SetPort(port); +listener->SetPassword(password); +listener->SetUserTimeout(userTimeout); +listener->SetScriptOnConnect(onConnect); +listener->SetScriptOnDisconnect(onDisconnect); + +listener->Start(); + +WriteServLog("rscriptd started successfully."); +WriteServLog("+++++++++++++++++++++++++++++++++++++++++++++"); + +sigset_t signalSet; +sigfillset(&signalSet); +pthread_sigmask(SIG_BLOCK, &signalSet, NULL); + +while (true) + { + sigfillset(&signalSet); + int sig = 0; + printfd(__FILE__, "Before sigwait\n"); + sigwait(&signalSet, &sig); + printfd(__FILE__, "After sigwait. Signal: %d\n", sig); + bool stop = false; + switch (sig) + { + case SIGTERM: + stop = true; + break; + case SIGINT: + stop = true; + break; + default: + WriteServLog("Ignore signel %d", sig); + break; + } + if (stop) + break; + } + +listener->Stop(); + +WriteServLog("+++++++++++++++++++++++++++++++++++++++++++++"); + +StopScriptExecuter(msgID); + +WriteServLog("rscriptd stopped successfully."); +WriteServLog("---------------------------------------------"); + +delete listener; +delete cfg; +return EXIT_SUCCESS; +} +//----------------------------------------------------------------------------- diff --git a/projects/rscriptd/pidfile.cpp b/projects/rscriptd/pidfile.cpp new file mode 100644 index 00000000..5f3f4979 --- /dev/null +++ b/projects/rscriptd/pidfile.cpp @@ -0,0 +1,53 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Author : Maxim Mamontov + */ + +/* + $Revision: 1.1 $ + $Date: 2010/02/11 12:32:25 $ + $Author: faust $ + */ + +/* + * An implementation of RAII pid-file writer + */ + +#include +#include + +#include "pidfile.h" + +PIDFile::PIDFile(const std::string & fn) + : fileName(fn) +{ +if (fileName != "") + { + std::ofstream pf(fileName.c_str()); + pf << getpid() << std::endl; + pf.close(); + } +} + +PIDFile::~PIDFile() +{ +if (fileName != "") + { + unlink(fileName.c_str()); + } +} diff --git a/projects/rscriptd/pidfile.h b/projects/rscriptd/pidfile.h new file mode 100644 index 00000000..82ff0038 --- /dev/null +++ b/projects/rscriptd/pidfile.h @@ -0,0 +1,44 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Author : Maxim Mamontov + */ + +/* + * Header file for RAII pid-file writer + */ + +/* + $Revision: 1.1 $ + $Date: 2010/02/11 12:32:25 $ + $Author: faust $ + */ + +#ifndef __PID_FILE_H__ +#define __PID_FILE_H__ + +#include + +class PIDFile { +public: + explicit PIDFile(const std::string & fn); + ~PIDFile(); +private: + std::string fileName; +}; + +#endif diff --git a/projects/rscriptd/rscriptd.conf b/projects/rscriptd/rscriptd.conf new file mode 100644 index 00000000..45b7b5c0 --- /dev/null +++ b/projects/rscriptd/rscriptd.conf @@ -0,0 +1,68 @@ +################################################################################ +# Rscriptd Configuration file # +################################################################################ + +# LOG file name +# Parameter: optional +# Value: file path +# Default: /var/log/rscriptd.log +LogFileName = /var/log/rscriptd.log + +# Amount of rscriptd-exec processes. +# These processes are responsible for the execution of scripts +# OnConnect and OnDisconnect. +# Amount of processes means how many scripts can be executed simultaneously. +# Recommend to leave 1 to avoid errors when executing scripts +# Parameter: optional +# Value: 1 ... 1024 +# Default: 1 +ExecutersNum = 1 + +# Message queue identifier for the script executer. +# It may be changed if there're a needs to run multiple copies of rscriptd. +# Warning: If you do not understand it, do not touch this setting! +# Parameter: optional +# Value: 0 ... 2 ^ 32 +# Default: 5555 +# ExecMsgKey = 5555 + +# The path to directory where config files are +# Parameter: optional +# Value: directory path +# Default: /etc/rscriptd +ConfigDir = /etc/rscriptd + +# Defines password for the encryption exchange between +# Stargazer server and rscriptd. +# Parameter: optional +# Value: any +# Default: 123456 +Password = 123456 + +# Defines port number for communication between +# Stargazer server and rscriptd. +# Parameter: optional +# Value: 1 ... 65535 +# Default: 9999 +Port = 9999 + +# User timeout. If Stargazer does not respond during this time, +# the user will be disconnected. +# Parameter: optional +# Values: 5 ... 600 +# Default: 60 +UserTimeout = 60 + +# Defines file which runs when user gets access +# Parameter: optional +# Value: file path +# Default: /etc/rscriptd/OnConnect +ScriptOnConnect = /etc/rscriptd/OnConnect + +# Defines file which runs when user loses access +# Parameter: optional +# Value: file path +# Default: /etc/rscriptd/OnDisconnect +ScriptOnDisconnect = /etc/rscriptd/OnDisconnect + +################################################################################ diff --git a/projects/sgauth/.gitignore b/projects/sgauth/.gitignore new file mode 100644 index 00000000..f4bcc898 --- /dev/null +++ b/projects/sgauth/.gitignore @@ -0,0 +1,2 @@ +css.h +sgauth diff --git a/projects/sgauth/CMakeLists.txt b/projects/sgauth/CMakeLists.txt new file mode 100644 index 00000000..2a87f3f6 --- /dev/null +++ b/projects/sgauth/CMakeLists.txt @@ -0,0 +1,18 @@ +set ( CPP_FILES main.cpp settings_impl.cpp web.cpp ) + +set ( THREADS_PREFER_PTHREAD_FLAG ON ) +find_package ( Threads REQUIRED ) +find_package ( Intl REQUIRED ) + +file ( READ sgauth.css CSS_DATA ) +configure_file ( css.h.in css.h ESCAPE_QUOTES @ONLY ) + +set ( CMAKE_INCLUDE_CURRENT_DIR ON ) + +include_directories ( ${Intl_INCLUDE_DIRS} ) + +add_executable ( sgauth ${CPP_FILES} ) + +target_link_libraries ( sgauth conffiles ia crypto common ${Intl_LIBRARIES} Threads::Threads ) + +# TODO: install diff --git a/projects/sgauth/css.h.in b/projects/sgauth/css.h.in new file mode 100644 index 00000000..5a141254 --- /dev/null +++ b/projects/sgauth/css.h.in @@ -0,0 +1,10 @@ +#pragma once + +namespace SGAuth +{ + +const auto css = R"EOR( +@CSS_DATA@ +)EOR"; + +} diff --git a/projects/sgauth/main.cpp b/projects/sgauth/main.cpp new file mode 100644 index 00000000..3256ae52 --- /dev/null +++ b/projects/sgauth/main.cpp @@ -0,0 +1,278 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Author : Boris Mikhailenko + */ + + /* + $Revision: 1.13 $ + $Date: 2010/04/14 09:01:29 $ + $Author: faust $ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "stg/ia.h" +#include "stg/common.h" +#include "web.h" +#include "settings_impl.h" + +int mes; +char infoText[256]; +char messageText[256]; + +const int winKOI = 0; + +IA_CLIENT_PROT * clnp; +WEB * web = NULL; + +time_t stgTime; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void Usage() +{ +printf("sgauth \n"); +} +//----------------------------------------------------------------------------- +void SetDirName(const std::vector & dn, void *) +{ +for (int j = 0; j < DIR_NUM; j++) + { + if (winKOI) + { + std::string dir; + KOIToWin(dn[j], &dir); + if (web) + web->SetDirName(dir, j); + } + else + { + if (web) + web->SetDirName(dn[j], j); + } + } +} +//----------------------------------------------------------------------------- +void StatUpdate(const LOADSTAT & ls, void *) +{ +if (web) + web->UpdateStat(ls); +} +//----------------------------------------------------------------------------- +void StatusChanged(int, void *) +{ +} +//----------------------------------------------------------------------------- +void ShowMessage(const std::string & message, int i, int, int, void *) +{ +if (web) + web->AddMessage(message, i); +} +//----------------------------------------------------------------------------- +void ShowError(const std::string & message, int, void *) +{ +if (web) + web->AddMessage(message, 0); +} +//----------------------------------------------------------------------------- +void CatchUSR1(int) +{ +if (clnp->GetAuthorized()) + { + std::cout << "Connect" << std::endl; + clnp->Connect(); + } +} +//----------------------------------------------------------------------------- +void CatchUSR2(int) +{ +std::cout << "Disconnect" << std::endl; +clnp->Disconnect(); +} +//----------------------------------------------------------------------------- +void CatchTERM(int) +{ +std::cout << "Terminated" << std::endl; +clnp->Disconnect(); +sleep(2); +exit(0); +} +//----------------------------------------------------------------------------- +static void SetSignalHandlers() +{ +struct sigaction newsa, oldsa; +sigset_t sigmask; + +sigemptyset(&sigmask); +sigaddset(&sigmask, SIGTERM); +newsa.sa_handler = CatchTERM; +newsa.sa_mask = sigmask; +newsa.sa_flags = 0; +sigaction(SIGTERM, &newsa, &oldsa); + +sigemptyset(&sigmask); +sigaddset(&sigmask, SIGINT); +newsa.sa_handler = CatchTERM; +newsa.sa_mask = sigmask; +newsa.sa_flags = 0; +sigaction(SIGINT, &newsa, &oldsa); + +sigemptyset(&sigmask); +sigaddset(&sigmask, SIGUSR1); +newsa.sa_handler = CatchUSR1; +newsa.sa_mask = sigmask; +newsa.sa_flags = 0; +sigaction(SIGUSR1, &newsa, &oldsa); + +sigemptyset(&sigmask); +sigaddset(&sigmask, SIGUSR2); +newsa.sa_handler = CatchUSR2; +newsa.sa_mask = sigmask; +newsa.sa_flags = 0; +sigaction(SIGUSR2, &newsa, &oldsa); + +return; +} +//----------------------------------------------------------------------------- +int main(int argc, char *argv[]) +{ +SETTINGS_IMPL settings; + +if (argc == 2) + { + settings.SetConfFile(argv[1]); + } +else + { + // Usage + } + +if (settings.ReadSettings()) + { + printf("ReadSettingsError\n"); + printf("%s\n", settings.GetStrError().c_str()); + exit(-1); + } +settings.Print(); + +if (settings.GetDaemon()) + { + switch (fork()) + { + case -1: + exit(1); + break; + + case 0: + setsid(); + break; + + default: + exit(0); + break; + } + } + +clnp = new IA_CLIENT_PROT(settings.GetServerName(), settings.GetServerPort(), settings.GetLocalName(), settings.GetLocalPort()); + +if (!settings.GetNoWeb()) + { + web = new WEB(); + web->SetRefreshPagePeriod(settings.GetRefreshPeriod()); + web->SetListenAddr(settings.GetListenWebIP()); + web->Start(); + } + +clnp->SetLogin(settings.GetLogin()); +clnp->SetPassword(settings.GetPassword()); + +clnp->SetStatusChangedCb(StatusChanged, NULL); +clnp->SetInfoCb(ShowMessage, NULL); +clnp->SetErrorCb(ShowError, NULL); +clnp->SetDirNameCb(SetDirName, NULL); +clnp->SetStatChangedCb(StatUpdate, NULL); +clnp->SetReconnect(settings.GetReconnect()); + +clnp->Start(); + +SetSignalHandlers(); + +#ifdef LINUX +for (int i = 1; i < argc; i++) + memset(argv[i], 0, strlen(argv[i])); + +if(argc > 1) + strcpy(argv[1], "Connecting..."); +#endif + +#ifdef FREEBSD +setproctitle("Connecting..."); +#endif +clnp->Connect(); + +while (1) + { + struct timespec ts = {0, 200000000}; + nanosleep(&ts, NULL); + + char state[20]; + + if (clnp->GetAuthorized()) + { + if (settings.GetShowPid()) + sprintf(state, "On %d", getpid()); + else + strcpy(state, "Online"); + } + else + { + if (settings.GetShowPid()) + sprintf(state, "Off %d", getpid()); + else + strcpy(state, "Offline"); + } + + #ifdef LINUX + for (int i = 1; i < argc; i++) + memset(argv[i], 0, strlen(argv[i])); + if(argc > 1) + strcpy(argv[1], state); + #endif + + #ifdef FREEBSD + setproctitle(state); + #endif + + #ifdef FREEBSD_5 + setproctitle(state); + #endif + } + +return 0; +} +//----------------------------------------------------------------------------- diff --git a/projects/sgauth/settings_impl.cpp b/projects/sgauth/settings_impl.cpp new file mode 100644 index 00000000..1a7b8dcd --- /dev/null +++ b/projects/sgauth/settings_impl.cpp @@ -0,0 +1,148 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Author : Maxim Mamontov + */ + +#include +#include + +#include "stg/common.h" +#include "stg/conffiles.h" +#include "settings_impl.h" + +SETTINGS_IMPL::SETTINGS_IMPL() + : port(0), + localPort(0), + listenWebIP(0), + refreshPeriod(0), + daemon(false), + noWeb(false), + reconnect(false), + showPid(false), + confFile("/etc/sgauth.conf") +{ +} +//----------------------------------------------------------------------------- +int SETTINGS_IMPL::ReadSettings() +{ +CONFIGFILE cf(confFile); + +if (cf.Error()) + { + strError = "Cannot read file '" + confFile + "'"; + return -1; + } + +cf.ReadString("Login", &login, "/?--?--?*"); +if (login == "/?--?--?*") + { + strError = "Parameter 'Login' not found."; + return -1; + } + +cf.ReadString("Password", &password, "/?--?--?*"); +if (login == "/?--?--?*") + { + strError = "Parameter 'Password' not found."; + return -1; + } + +cf.ReadString("ServerName", &serverName, "?*?*?"); +if (serverName == "?*?*?") + { + strError = "Parameter 'ServerName' not found."; + return -1; + } + +std::string temp; +cf.ReadString("ListenWebIP", &temp, "127.0.0.1"); +listenWebIP = inet_strington(temp); +if (listenWebIP == 0) + { + strError = "Parameter 'ListenWebIP' is not valid."; + return -1; + } + +cf.ReadString("ServerPort", &temp, "5555"); +if (ParseIntInRange(temp, 1, 65535, &port)) + { + strError = "Parameter 'ServerPort' is not valid."; + return -1; + } + +cf.ReadString("LocalName", &localName, ""); + +cf.ReadString("LocalPort", &temp, "0"); +if (ParseIntInRange(temp, 0, 65535, &localPort)) + { + strError = "Parameter 'LocalPort' is not valid."; + return -1; + } + +cf.ReadString("RefreshPeriod", &temp, "5"); +if (ParseIntInRange(temp, 1, 24*3600, &refreshPeriod)) + { + strError = "Parameter 'RefreshPeriod' is not valid."; + return -1; + } + +cf.ReadString("Reconnect", &temp, "yes"); +if (ParseYesNo(temp, &reconnect)) + { + strError = "Parameter 'Reconnect' is not valid."; + return -1; + } + +cf.ReadString("Daemon", &temp, "yes"); +if (ParseYesNo(temp, &daemon)) + { + strError = "Parameter 'Daemon' is not valid."; + return -1; + } + +cf.ReadString("ShowPid", &temp, "no"); +if (ParseYesNo(temp, &showPid)) + { + strError = "Parameter 'ShowPid' is not valid."; + return -1; + } + +cf.ReadString("DisableWeb", &temp, "no"); +if (ParseYesNo(temp, &noWeb)) + { + strError = "Parameter 'DisableWeb' is not valid."; + return -1; + } + +return 0; +} +//----------------------------------------------------------------------------- +void SETTINGS_IMPL::Print() const +{ +std::cout << "Login = " << login << "\n" + << "Password = " << password << "\n" + << "Ip = " << serverName << "\n" + << "Port = " << port << "\n" + << "LocalPort = " << localPort << "\n" + << "ListenWebIP = " << inet_ntostring(listenWebIP) << "\n" + << "RefreshPeriod = " << refreshPeriod << "\n" + << "Daemon = " << daemon << "\n" + << "DisableWeb = " << noWeb << "\n" + << "Reconnect = " << reconnect << "\n" + << "ShowPid = " << showPid << std::endl; +} diff --git a/projects/sgauth/settings_impl.h b/projects/sgauth/settings_impl.h new file mode 100644 index 00000000..c5305b89 --- /dev/null +++ b/projects/sgauth/settings_impl.h @@ -0,0 +1,73 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Author : Maxim Mamontov + */ + +#ifndef SETTINGS_IMPL_H +#define SETTINGS_IMPL_H + +#include +#include + +class SETTINGS_IMPL { +public: + SETTINGS_IMPL(); + ~SETTINGS_IMPL() {} + int Reload() { return 0; } + void SetConfFile(const std::string & cf) { confFile = cf; } + int ReadSettings(); + + const std::string & GetStrError() const { return strError; } + + const std::string & GetServerName() const { return serverName; } + uint16_t GetServerPort() const { return port; } + const std::string & GetLocalName() const { return localName; } + uint16_t GetLocalPort() const { return localPort; } + + const std::string & GetLogin() const { return login; } + const std::string & GetPassword() const { return password; } + + bool GetDaemon() const { return daemon; } + bool GetShowPid() const { return showPid; } + bool GetNoWeb() const { return noWeb; } + bool GetReconnect() const { return reconnect; } + int GetRefreshPeriod() const { return refreshPeriod; } + uint32_t GetListenWebIP() const { return listenWebIP; } + + void Print() const; + +private: + std::string login; + std::string password; + std::string serverName; + int port; + std::string localName; + int localPort; + uint32_t listenWebIP; + int refreshPeriod; + + bool daemon; + bool noWeb; + bool reconnect; + bool showPid; + + std::string confFile; + std::string strError; +}; + +#endif diff --git a/projects/sgauth/sgauth.conf b/projects/sgauth/sgauth.conf new file mode 100644 index 00000000..bb5fca32 --- /dev/null +++ b/projects/sgauth/sgauth.conf @@ -0,0 +1,78 @@ +################################################################################ +# Sgauth Configuration file # +################################################################################ + +# Stargazer server +# Parameter: required +# Values: IP address or DNS name +# Default: +ServerName = 192.168.1.2 + +# Port on which Stargazer interacts with sgauth +# Parameter: optional +# Value: 1 ... 65535 +# Default: 5555 +ServerPort = 5555 + +# User's login in Stargazer +# Parameter: required +# Value: any +# Default: +Login = test + +# Local host to bind +# Parameter: optional +# Values: IP address or DNS name +# Default: 0.0.0.0 +LocalName = localhost + +# Port on which sgauth interacts with Stargazer +# Parameter: optional +# Value: 1 ... 65535 +# Default: 0 +LocalPort = 12345 + +# User's password in Stargazer +# Parameter: required +# Value: any +# Default: +Password = 123456 + +# Defines whether sgauth should try to reestablish connection to Stargazer +# if it was lost +# Parameter: optional +# Value: yes, no +# Default: yes +Reconnect = yes + +# Defines whether sgauth should run as daemon +# Parameter: optional +# Value: yes, no +# Default: yes +Daemon = yes + +# Web-page refresh period in built-in webserver +# Parameter: optional +# Value: any numeric (minutes) +# Default: 10 +RefreshPeriod = 10 + +# Defines whether sgauth should use built-in webserver +# Parameter: optional +# Value: yes, no +# Default: no +DisableWeb = no + +# Defines address on which sgauth's built-in webserver will listen +# Parameter: optional +# Value: IP address or DNS name +# Default: 127.0.0.1 +ListenWebIP = 127.0.0.1 + +# Defines whether sgauth should show its process ID +# Parameter: optional +# Value: yes, no +# Default: no +ShowPid = no + +################################################################################ diff --git a/projects/sgauth/sgauth.css b/projects/sgauth/sgauth.css new file mode 100644 index 00000000..f816dbf9 --- /dev/null +++ b/projects/sgauth/sgauth.css @@ -0,0 +1,78 @@ +H3 +{ +color: black; +} + +body +{ +background-color: silver; +} + +#TraffTable +{ +background-color: white; +} + +#TraffTableCaptionRow +{ +background-color: silver; +} + +#TraffTableCaptionCellC, +#TraffTableUMCellC, +#TraffTableDMCellC, +#TraffTableUSCellC, +#TraffTableDSCellC +{ +background-color: silver; +} + +#TraffTableDMRow, +#TraffTableDSRow +{ +background-color: #f2f0cc; +} + +#TraffTableUMRow, +#TraffTableUSRow +{ +background-color: white; +} + +#ConnectionStateOnline +{ +color: green; +font-size: 20px +} + +#ConnectionStateOffline +{ +color: red; +font-size: 20px +} + +p +{ +padding: 2px; +margin: 0px; +} + +#MessagesTable +{ +background-color: white; +} + +#MessagesTableRowC +{ +background-color: silver; +} + + +#MessagesTableRow0, +#MessagesTableRow2, +#MessagesTableRow4, +#MessagesTableRow6, +#MessagesTableRow8 +{ +background-color: #f2f0cc; +} diff --git a/projects/sgauth/web.cpp b/projects/sgauth/web.cpp new file mode 100644 index 00000000..a162acc4 --- /dev/null +++ b/projects/sgauth/web.cpp @@ -0,0 +1,461 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Author : Boris Mikhailenko + */ + + /* + $Revision: 1.7 $ + $Date: 2010/03/15 12:58:17 $ + */ + +#include + +#include +#include +#include +#include + +#include "stg/common.h" +#include "stg/ia.h" +#include "web.h" + +extern WEB * web; +extern IA_CLIENT_PROT * clnp; + +#define LISTEN_PORT (5580) + +#include "css.h" + +//--------------------------------------------------------------------------- +#ifndef WIN32 +void * RunWeb(void *) +{ +sigset_t signalSet; +sigfillset(&signalSet); +pthread_sigmask(SIG_BLOCK, &signalSet, NULL); + +#else +unsigned long WINAPI RunWeb(void *) +{ +#endif +while (1) + web->Run(); +return NULL; +} +//--------------------------------------------------------------------------- +WEB::WEB() + : res(0), + listenSocket(0), + outerSocket(0), + refreshPeriod(0), + listenWebAddr(0) +{ +#ifdef WIN32 +res = WSAStartup(MAKEWORD(2,0), &wsaData); +#endif + +for (int i = 0; i < DIR_NUM; i++) + dirName[i] = "-"; + +refreshPeriod = 5; + +memset(&ls, 0, sizeof(ls)); +} +//--------------------------------------------------------------------------- +void WEB::Start() +{ +#ifdef WIN32 +unsigned long pt; +CreateThread( + NULL, // pointer to thread security attributes + 16384, // initial thread stack size, in bytes + RunWeb, // pointer to thread function + NULL, // argument for new thread + 0, // CREATE_SUSPENDED, // creation flags + &pt // pointer to returned thread identifier + ); +#else +pthread_create(&thread, NULL, RunWeb, NULL); +#endif +} +//--------------------------------------------------------------------------- +void WEB::PrepareNet() +{ +listenSocket = socket(PF_INET, SOCK_STREAM, 0); + +struct sockaddr_in listenAddr; +listenAddr.sin_family = AF_INET; +listenAddr.sin_port = htons(LISTEN_PORT); +listenAddr.sin_addr.s_addr = listenWebAddr; + +#ifndef WIN32 +int lng = 1; +if (0 != setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, &lng, 4)) + { + printf("Setsockopt Fail\n"); + printf(">>> Error %s\n", strerror(errno)); + } +#else +//??? TODO +#endif + + +res = bind(listenSocket, (struct sockaddr*)&listenAddr, sizeof(listenAddr)); + +if (res == -1) + { + printf("Bind failed.\n"); + exit(0); + } + +res = listen(listenSocket, 0); +if (res == -1) + { + printf("Listen failed.\n"); + exit(0); + } +} +//--------------------------------------------------------------------------- +void WEB::SetRefreshPagePeriod(int p) +{ +refreshPeriod = p; +if (refreshPeriod <= 0 || refreshPeriod > 24*3600) + refreshPeriod = 5; +} +//--------------------------------------------------------------------------- +void WEB::SetListenAddr(uint32_t ip) +{ +listenWebAddr = ip; +} +//--------------------------------------------------------------------------- +void WEB::Run() +{ +PrepareNet(); +char recvBuffer[4096]; +while (1) + { + struct sockaddr_in outerAddr; + + #ifndef WIN32 + socklen_t outerAddrLen = sizeof(outerAddr); + #else + int outerAddrLen = sizeof(outerAddr); + #endif + + outerSocket = accept(listenSocket, (struct sockaddr*)&outerAddr, &outerAddrLen); + if (outerSocket == -1) + { + printf(">>> Error %s\n", strerror(errno)); + continue; + } + recv(outerSocket, recvBuffer, sizeof(recvBuffer), 0); + + if (strncmp(recvBuffer, "GET /sgauth.css", strlen("GET /sgauth.css")) == 0) + { + SendCSS(); + //printf("(1) recvBuffer=%s\n", recvBuffer); + } + else if (strncmp(recvBuffer, "GET /disconnect", strlen("GET /disconnect")) == 0) + { + clnp->Disconnect(); + Redirect("/"); + //printf("(2) recvBuffer=%s\n", recvBuffer); + } + else if (strncmp(recvBuffer, "GET /connect", strlen("GET /connect")) == 0) + { + clnp->Connect(); + Redirect("/"); + //printf("(3) recvBuffer=%s\n", recvBuffer); + } + else if (strncmp(recvBuffer, "GET /exit", strlen("GET /exit")) == 0) + { + Redirect("/"); + clnp->Disconnect(); + #ifdef WIN32 + Sleep(1000); + #else + struct timespec ts = {1, 0}; + nanosleep(&ts, NULL); + #endif + exit(0); + } + else + { + SendReply(); + //printf("(4) recvBuffer=%s\n", recvBuffer); + } + + #ifdef WIN32 + closesocket(outerSocket); + #else + close(outerSocket); + #endif + } +} +//--------------------------------------------------------------------------- +int WEB::Redirect(const char * url) +{ +const char * redirect = + "HTTP/1.0 200 OK\n" + "Content-Type: text/html\n" + "Connection: close" + "\n\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n\n"; + +char buff[2000]; +sprintf(buff, redirect, url); +send(outerSocket, buff, strlen(buff), 0); + +return 0; +} +//--------------------------------------------------------------------------- +int WEB::SendReply() +{ +int j, rowNum; + +const char * replyHeader = + "HTTP/1.0 200 OK\n" + "Content-Type: text/html\n" + "Connection: close" + "\n\n" + "\n" + "\n" + "\n" + "\n" + "sgauth\n" + "" + "\n" + "\n" + "

Stargazer

\n"; + +const char * replyFooter = "\n\n"; + +char replyHeaderBuffer[2000]; +sprintf(replyHeaderBuffer, replyHeader, refreshPeriod); + +send(outerSocket, replyHeaderBuffer, strlen(replyHeaderBuffer), 0); + +char str[512]; + +int st = clnp->GetAuthorized(); + +sprintf(str, "%s

\n", gettext("Connect")); +res = send(outerSocket, str, strlen(str), 0); + +sprintf(str, "%s

\n", gettext("Disconnect")); +res = send(outerSocket, str, strlen(str), 0); + +sprintf(str, "%s

\n", gettext("Refresh")); +res = send(outerSocket, str, strlen(str), 0); + +sprintf(str, "%s

\n", gettext("Exit")); +res = send(outerSocket, str, strlen(str), 0); + +sprintf(str, "

%s

\n" , st ? "ConnectionStateOnline":"ConnectionStateOffline", st ? "Online":"Offline"); +res = send(outerSocket, str, strlen(str), 0); + +sprintf(str, "

%s: %.3f

\n" , gettext("Cash"), ls.cash / 1000.0); +res = send(outerSocket, str, strlen(str), 0); + +sprintf(str, "

%s: %s

\n" , + gettext("PrepaidTraffic"), + ls.freeMb[0] == 'C' ? ls.freeMb + 1 : ls.freeMb); +res = send(outerSocket, str, strlen(str), 0); + +sprintf(str, "\n"); +res = send(outerSocket, str, strlen(str), 0); +sprintf(str, " \n"); +res = send(outerSocket, str, strlen(str), 0); +sprintf(str, " \n"); +res = send(outerSocket, str, strlen(str), 0); + +rowNum = 0; +for (j = 0; j < DIR_NUM; j++) + { + if (dirName[j][0] == 0) + continue; + std::string s; + KOIToWin(dirName[j], &s);// +++++++++ sigsegv ========== TODO too long dir name crashes sgauth + sprintf(str, " \n", rowNum++, s.c_str()); + send(outerSocket, str, strlen(str), 0); + } + +sprintf(str," \n"); +send(outerSocket, str, strlen(str), 0); + +sprintf(str," \n"); +send(outerSocket, str, strlen(str), 0); + +sprintf(str," \n", gettext("Month Upload")); +send(outerSocket, str, strlen(str), 0); + +rowNum = 0; +for (j = 0; j < DIR_NUM; j++) + { + if (dirName[j][0] == 0) + continue; + sprintf(str," \n", rowNum++, IntToKMG(ls.mu[j], ST_F)); + res = send(outerSocket, str, strlen(str), 0); + } + +sprintf(str," \n"); +res = send(outerSocket, str, strlen(str), 0); +sprintf(str," \n"); +res = send(outerSocket, str, strlen(str), 0); +sprintf(str," \n", gettext("Month Download")); +res = send(outerSocket, str, strlen(str), 0); + +rowNum = 0; +for (j = 0; j < DIR_NUM; j++) + { + if (dirName[j][0] == 0) + continue; + sprintf(str," \n", rowNum++, IntToKMG(ls.md[j], ST_F)); + res = send(outerSocket, str, strlen(str), 0); + } +sprintf(str," \n"); +res = send(outerSocket, str, strlen(str), 0); + + +sprintf(str," \n"); +res = send(outerSocket, str, strlen(str), 0); +sprintf(str," \n", gettext("Session Upload")); +res = send(outerSocket, str, strlen(str), 0); + +rowNum = 0; +for (j = 0; j < DIR_NUM; j++) + { + if (dirName[j][0] == 0) + continue; + sprintf(str," \n", rowNum++, IntToKMG(ls.su[j], ST_F)); + res = send(outerSocket, str, strlen(str), 0); + } + +sprintf(str," \n"); +res = send(outerSocket, str, strlen(str), 0); +sprintf(str," \n"); +res = send(outerSocket, str, strlen(str), 0); +sprintf(str," \n", gettext("Session Download")); +res = send(outerSocket, str, strlen(str), 0); + +for (j = 0; j < DIR_NUM; j++) + { + if (dirName[j][0] == 0) + continue; + sprintf(str," \n", j, IntToKMG(ls.sd[j], ST_F)); + res = send(outerSocket, str, strlen(str), 0); + } + +sprintf(str," \n"); +res = send(outerSocket, str, strlen(str), 0); + +sprintf(str,"
 %s
%s%s
%s%s
%s%s
%s%s
\n"); +res = send(outerSocket, str, strlen(str), 0); + +rowNum = 0; +if (!messages.empty()) + { + sprintf(str," \n"); + res = send(outerSocket, str, strlen(str), 0); + + sprintf(str," \n"); + send(outerSocket, str, strlen(str), 0); + sprintf(str," \n"); + send(outerSocket, str, strlen(str), 0); + sprintf(str," \n"); + send(outerSocket, str, strlen(str), 0); + sprintf(str," \n"); + send(outerSocket, str, strlen(str), 0); + + std::list::reverse_iterator it; + it = messages.rbegin(); + while (it != messages.rend()) + { + sprintf(str," \n", rowNum); + send(outerSocket, str, strlen(str), 0); + sprintf(str," \n", it->recvTime.c_str()); + send(outerSocket, str, strlen(str), 0); + sprintf(str," \n", it->msg.c_str()); + send(outerSocket, str, strlen(str), 0); + sprintf(str," \n"); + send(outerSocket, str, strlen(str), 0); + ++it; + ++rowNum; + } + + sprintf(str,"
DateText
%s%s
\n"); + res = send(outerSocket, str, strlen(str), 0); + } + +time_t t = time(NULL); +sprintf(str,"Îáíîâëåíî: %s" , ctime(&t)); +res = send(outerSocket, str, strlen(str), 0); + +send(outerSocket, replyFooter, strlen(replyFooter), 0); + +return 0; +} +//--------------------------------------------------------------------------- +int WEB::SendCSS() +{ +const char * replyHeader = + "HTTP/1.0 200 OK\n" + "Content-Type: text/css\n" + "Connection: close\n\n"; + +const char * replyFooter= "\n\n"; + +send(outerSocket, replyHeader, strlen(replyHeader), 0); +send(outerSocket, SGAuth::css, strlen(SGAuth::css), 0); +send(outerSocket, replyFooter, strlen(replyFooter), 0); + +return 0; +} +//--------------------------------------------------------------------------- +void WEB::SetDirName(const std::string & dn, int n) +{ +web->dirName[n] = dn; +} +//--------------------------------------------------------------------------- +void WEB::AddMessage(const std::string & message, int type) +{ +time_t t = time(NULL); +STG_MESSAGE m; + +m.msg = message; +m.type = type; +m.recvTime = ctime(&t); + +messages.push_back(m); + +if (messages.size() > MAX_MESSAGES) + messages.pop_front(); + +} +//--------------------------------------------------------------------------- +void WEB::UpdateStat(const LOADSTAT & ls) +{ +memcpy((void*)&(WEB::ls), &ls, sizeof(LOADSTAT)); +} +//--------------------------------------------------------------------------- + diff --git a/projects/sgauth/web.h b/projects/sgauth/web.h new file mode 100644 index 00000000..a933230a --- /dev/null +++ b/projects/sgauth/web.h @@ -0,0 +1,88 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Author : Boris Mikhailenko + */ + + /* + $Revision: 1.3 $ + $Date: 2007/12/17 08:39:08 $ + */ + +#ifndef WIN32 +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#endif + +#include +#include + +#include "stg/const.h" +#include "stg/ia_packets.h" + +#define MAX_MESSAGES (10) +//----------------------------------------------------------------------------- +struct STG_MESSAGE +{ +std::string msg; +std::string recvTime; +int type; +}; +//----------------------------------------------------------------------------- +class WEB +{ +public: + WEB(); + void Run(); + void SetDirName(const std::string & dn, int n); + void SetRefreshPagePeriod(int p); + void SetListenAddr(uint32_t ip); + void AddMessage(const std::string & message, int type); + void UpdateStat(const LOADSTAT & ls); + void Start(); +private: + void PrepareNet(); + int SendReply(); + int SendCSS(); + int Redirect(const char * url); + + #ifdef WIN32 + WSADATA wsaData; + #else + pthread_t thread; + #endif + + std::string dirName[DIR_NUM]; + int res; + int listenSocket; + int outerSocket; + int refreshPeriod; + + uint32_t listenWebAddr; + LOADSTAT ls; + + std::list messages; +}; +//----------------------------------------------------------------------------- diff --git a/projects/sgconf/CHANGES b/projects/sgconf/CHANGES new file mode 100644 index 00000000..e69de29b diff --git a/projects/sgconf/CMakeLists.txt b/projects/sgconf/CMakeLists.txt new file mode 100644 index 00000000..c2666b80 --- /dev/null +++ b/projects/sgconf/CMakeLists.txt @@ -0,0 +1,12 @@ +find_package ( EXPAT REQUIRED ) + +set ( CPP_FILES main.cpp options.cpp api_action.cpp actions.cpp admins.cpp tariffs.cpp users.cpp services.cpp corps.cpp info.cpp xml.cpp ) + +set ( THREADS_PREFER_PTHREAD_FLAG ON ) +find_package ( Threads REQUIRED ) + +add_executable ( sgconf ${CPP_FILES} ) + +target_link_libraries ( sgconf srvconf crypto common EXPAT::EXPAT ) + +# TODO: install diff --git a/projects/sgconf/README.txt b/projects/sgconf/README.txt new file mode 100644 index 00000000..bb2db5b9 --- /dev/null +++ b/projects/sgconf/README.txt @@ -0,0 +1,4 @@ +Compiling: +> ./build + + diff --git a/projects/sgconf/TODO b/projects/sgconf/TODO new file mode 100644 index 00000000..19b51f5e --- /dev/null +++ b/projects/sgconf/TODO @@ -0,0 +1,2 @@ +1. No default value for server. +2. No default value for port. diff --git a/projects/sgconf/action.h b/projects/sgconf/action.h new file mode 100644 index 00000000..940ea7f6 --- /dev/null +++ b/projects/sgconf/action.h @@ -0,0 +1,57 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Author : Maxim Mamontov + */ + +#ifndef __STG_SGCONF_ACTION_H__ +#define __STG_SGCONF_ACTION_H__ + +#include +#include +#include + +namespace SGCONF +{ + +class OPTION_BLOCK; +struct PARSER_STATE; +struct CONFIG; + +class ACTION +{ + public: + virtual ~ACTION() {} + + virtual ACTION * Clone() const = 0; + virtual std::string ParamDescription() const = 0; + virtual std::string DefaultDescription() const = 0; + virtual OPTION_BLOCK & Suboptions() = 0; + virtual PARSER_STATE Parse(int argc, char ** argv, void * data = NULL) = 0; + virtual void ParseValue(const std::string &) {} + + class ERROR : public std::runtime_error + { + public: + ERROR(const std::string & message) + : std::runtime_error(message.c_str()) {} + }; +}; + +} // namespace SGCONF + +#endif diff --git a/projects/sgconf/actions.cpp b/projects/sgconf/actions.cpp new file mode 100644 index 00000000..afa51621 --- /dev/null +++ b/projects/sgconf/actions.cpp @@ -0,0 +1,19 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Author : Maxim Mamontov + */ diff --git a/projects/sgconf/actions.h b/projects/sgconf/actions.h new file mode 100644 index 00000000..3181a105 --- /dev/null +++ b/projects/sgconf/actions.h @@ -0,0 +1,243 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Author : Maxim Mamontov + */ + +#pragma once + +#include "action.h" +#include "options.h" +#include "parser_state.h" + +#include "stg/common.h" +#include "stg/optional.h" + +#include + +#include + +namespace SGCONF +{ + +typedef void (* FUNC0)(); + +template +class FUNC0_ACTION : public ACTION +{ + public: + FUNC0_ACTION(const F & func) : m_func(func) {} + + virtual ACTION * Clone() const { return new FUNC0_ACTION(*this); } + + virtual std::string ParamDescription() const { return ""; } + virtual std::string DefaultDescription() const { return ""; } + virtual OPTION_BLOCK & Suboptions() { return m_suboptions; } + virtual PARSER_STATE Parse(int argc, char ** argv, void * /*data*/) + { + m_func(); + return PARSER_STATE(true, argc, argv); + } + + private: + F m_func; + OPTION_BLOCK m_suboptions; +}; + +template +inline +FUNC0_ACTION * MakeFunc0Action(F func) +{ +return new FUNC0_ACTION(func); +} + +template +class PARAM_ACTION : public ACTION +{ + public: + PARAM_ACTION(STG::Optional & param, + const T & defaultValue, + const std::string & paramDescription) + : m_param(param), + m_defaltValue(defaultValue), + m_description(paramDescription), + m_hasDefault(true) + {} + PARAM_ACTION(STG::Optional & param) + : m_param(param), + m_hasDefault(false) + {} + PARAM_ACTION(STG::Optional & param, + const std::string & paramDescription) + : m_param(param), + m_description(paramDescription), + m_hasDefault(false) + {} + + virtual ACTION * Clone() const { return new PARAM_ACTION(*this); } + + virtual std::string ParamDescription() const { return m_description; } + virtual std::string DefaultDescription() const; + virtual OPTION_BLOCK & Suboptions() { return m_suboptions; } + virtual PARSER_STATE Parse(int argc, char ** argv, void * /*data*/); + virtual void ParseValue(const std::string & value); + + private: + STG::Optional & m_param; + T m_defaltValue; + std::string m_description; + bool m_hasDefault; + OPTION_BLOCK m_suboptions; +}; + +template +inline +std::string PARAM_ACTION::DefaultDescription() const +{ +return m_hasDefault ? " (default: '" + std::to_string(m_defaltValue) + "')" + : ""; +} + +template <> +inline +std::string PARAM_ACTION::DefaultDescription() const +{ +return m_hasDefault ? " (default: '" + m_defaltValue + "')" + : ""; +} + +template +inline +PARSER_STATE PARAM_ACTION::Parse(int argc, char ** argv, void * /*data*/) +{ +if (argc == 0 || + argv == NULL || + *argv == NULL) + throw ERROR("Missing argument."); +T value; +if (str2x(*argv, value)) + throw ERROR(std::string("Bad argument: '") + *argv + "'"); +m_param = value; +return PARSER_STATE(false, --argc, ++argv); +} + +template <> +inline +PARSER_STATE PARAM_ACTION::Parse(int argc, char ** argv, void * /*data*/) +{ +m_param = true; +return PARSER_STATE(false, argc, argv); +} + +template +inline +void PARAM_ACTION::ParseValue(const std::string & stringValue) +{ +if (stringValue.empty()) + throw ERROR("Missing value."); +T value; +if (str2x(stringValue, value)) + throw ERROR(std::string("Bad value: '") + stringValue + "'"); +m_param = value; +} + +template <> +inline +void PARAM_ACTION::ParseValue(const std::string & stringValue) +{ +m_param = stringValue; +} + +template <> +inline +PARSER_STATE PARAM_ACTION::Parse(int argc, char ** argv, void * /*data*/) +{ +if (argc == 0 || + argv == NULL || + *argv == NULL) + throw ERROR("Missing argument."); +m_param = *argv; +return PARSER_STATE(false, --argc, ++argv); +} + +template +inline +PARAM_ACTION * MakeParamAction(STG::Optional & param, + const T & defaultValue, + const std::string & paramDescription) +{ +return new PARAM_ACTION(param, defaultValue, paramDescription); +} + +template +inline +PARAM_ACTION * MakeParamAction(STG::Optional & param) +{ +return new PARAM_ACTION(param); +} + +template +inline +PARAM_ACTION * MakeParamAction(STG::Optional & param, + const std::string & paramDescription) +{ +return new PARAM_ACTION(param, paramDescription); +} + +class KV_ACTION : public ACTION +{ + public: + KV_ACTION(const std::string & name, + const std::string & paramDescription) + : m_name(name), + m_description(paramDescription) + {} + + virtual ACTION * Clone() const { return new KV_ACTION(*this); } + + virtual std::string ParamDescription() const { return m_description; } + virtual std::string DefaultDescription() const { return ""; } + virtual OPTION_BLOCK & Suboptions() { return m_suboptions; } + virtual PARSER_STATE Parse(int argc, char ** argv, void * data); + + private: + std::string m_name; + std::string m_description; + OPTION_BLOCK m_suboptions; +}; + +inline +PARSER_STATE KV_ACTION::Parse(int argc, char ** argv, void * data) +{ +if (argc == 0 || + argv == NULL || + *argv == NULL) + throw ERROR("Missing argument."); +assert(data != NULL && "Expecting container pointer."); +std::map & kvs = *static_cast*>(data); +kvs[m_name] = *argv; +return PARSER_STATE(false, --argc, ++argv); +} + +inline +KV_ACTION * MakeKVAction(const std::string & name, + const std::string & paramDescription) +{ +return new KV_ACTION(name, paramDescription); +} + +} // namespace SGCONF diff --git a/projects/sgconf/admins.cpp b/projects/sgconf/admins.cpp new file mode 100644 index 00000000..ea796214 --- /dev/null +++ b/projects/sgconf/admins.cpp @@ -0,0 +1,205 @@ +#include "admins.h" + +#include "api_action.h" +#include "options.h" +#include "config.h" +#include "utils.h" + +#include "stg/servconf.h" +#include "stg/servconf_types.h" +#include "stg/admin_conf.h" + +#include +#include +#include +#include +#include + +namespace +{ + +std::string Indent(size_t level, bool dash = false) +{ +if (level == 0) + return ""; +return dash ? std::string(level * 4 - 2, ' ') + "- " : std::string(level * 4, ' '); +} + +std::string PrivToString(const STG::Priv& priv) +{ +return std::string("") + + (priv.corpChg ? "1" : "0") + + (priv.serviceChg ? "1" : "0") + + (priv.tariffChg ? "1" : "0") + + (priv.adminChg ? "1" : "0") + + (priv.userAddDel ? "1" : "0") + + (priv.userPasswd ? "1" : "0") + + (priv.userCash ? "1" : "0") + + (priv.userConf ? "1" : "0") + + (priv.userStat ? "1" : "0"); +} + +void PrintAdmin(const STG::GetAdmin::Info & info, size_t level = 0) +{ +std::cout << Indent(level, true) << "login: " << info.login << "\n" + << Indent(level) << "priviledges: " << PrivToString(info.priv) << "\n"; +} + +std::vector GetAdminParams() +{ +std::vector params; +params.push_back(SGCONF::API_ACTION::PARAM("password", "", "password")); +params.push_back(SGCONF::API_ACTION::PARAM("priv", "", "priviledges")); +return params; +} + +void ConvPriv(const std::string & value, STG::Optional & res) +{ +if (value.length() != 9) + throw SGCONF::ACTION::ERROR("Priviledges value should be a 9-digits length binary number."); +STG::Priv priv; +priv.corpChg = (value[0] == '0' ? 0 : 1); +priv.serviceChg = (value[1] == '0' ? 0 : 1); +priv.tariffChg = (value[2] == '0' ? 0 : 1); +priv.adminChg = (value[3] == '0' ? 0 : 1); +priv.userAddDel = (value[4] == '0' ? 0 : 1); +priv.userPasswd = (value[5] == '0' ? 0 : 1); +priv.userCash = (value[6] == '0' ? 0 : 1); +priv.userConf = (value[7] == '0' ? 0 : 1); +priv.userStat = (value[8] == '0' ? 0 : 1); +res = priv; +} + +void SimpleCallback(bool result, + const std::string & reason, + void * /*data*/) +{ +if (!result) + { + std::cerr << "Operation failed. Reason: '" << reason << "'." << std::endl; + return; + } +std::cout << "Success.\n"; +} + +void GetAdminsCallback(bool result, + const std::string & reason, + const std::vector & info, + void * /*data*/) +{ +if (!result) + { + std::cerr << "Failed to get admin list. Reason: '" << reason << "'." << std::endl; + return; + } +std::cout << "Admins:\n"; +for (size_t i = 0; i < info.size(); ++i) + PrintAdmin(info[i], 1); +} + +void GetAdminCallback(bool result, + const std::string & reason, + const std::vector & info, + void * data) +{ +assert(data != NULL && "Expecting pointer to std::string with the admin's login."); +const std::string & login = *static_cast(data); +if (!result) + { + std::cerr << "Failed to get admin. Reason: '" << reason << "'." << std::endl; + return; + } +for (size_t i = 0; i < info.size(); ++i) + if (info[i].login == login) + PrintAdmin(info[i]); +} + + +bool GetAdminsFunction(const SGCONF::CONFIG & config, + const std::string & /*arg*/, + const std::map & /*options*/) +{ +STG::ServConf proto(config.server.data(), + config.port.data(), + config.localAddress.data(), + config.localPort.data(), + config.userName.data(), + config.userPass.data()); +return proto.GetAdmins(GetAdminsCallback, NULL) == STG::st_ok; +} + +bool GetAdminFunction(const SGCONF::CONFIG & config, + const std::string & arg, + const std::map & /*options*/) +{ +STG::ServConf proto(config.server.data(), + config.port.data(), + config.localAddress.data(), + config.localPort.data(), + config.userName.data(), + config.userPass.data()); +// STG currently doesn't support . +// So get a list of admins and filter it. 'data' param holds a pointer to 'login'. +std::string login(arg); +return proto.GetAdmins(GetAdminCallback, &login) == STG::st_ok; +} + +bool DelAdminFunction(const SGCONF::CONFIG & config, + const std::string & arg, + const std::map & /*options*/) +{ +STG::ServConf proto(config.server.data(), + config.port.data(), + config.localAddress.data(), + config.localPort.data(), + config.userName.data(), + config.userPass.data()); +return proto.DelAdmin(arg, SimpleCallback, NULL) == STG::st_ok; +} + +bool AddAdminFunction(const SGCONF::CONFIG & config, + const std::string & arg, + const std::map & options) +{ +STG::AdminConfOpt conf; +conf.login = arg; +SGCONF::MaybeSet(options, "priv", conf.priv, ConvPriv); +SGCONF::MaybeSet(options, "password", conf.password); +STG::ServConf proto(config.server.data(), + config.port.data(), + config.localAddress.data(), + config.localPort.data(), + config.userName.data(), + config.userPass.data()); +return proto.AddAdmin(arg, conf, SimpleCallback, NULL) == STG::st_ok; +} + +bool ChgAdminFunction(const SGCONF::CONFIG & config, + const std::string & arg, + const std::map & options) +{ +STG::AdminConfOpt conf; +conf.login = arg; +SGCONF::MaybeSet(options, "priv", conf.priv, ConvPriv); +SGCONF::MaybeSet(options, "password", conf.password); +STG::ServConf proto(config.server.data(), + config.port.data(), + config.localAddress.data(), + config.localPort.data(), + config.userName.data(), + config.userPass.data()); +return proto.ChgAdmin(conf, SimpleCallback, NULL) == STG::st_ok; +} + +} // namespace anonymous + +void SGCONF::AppendAdminsOptionBlock(COMMANDS & commands, OPTION_BLOCKS & blocks) +{ +std::vector params(GetAdminParams()); +blocks.Add("Admin management options") + .Add("get-admins", SGCONF::MakeAPIAction(commands, GetAdminsFunction), "\tget admin list") + .Add("get-admin", SGCONF::MakeAPIAction(commands, "", GetAdminFunction), "get admin") + .Add("add-admin", SGCONF::MakeAPIAction(commands, "", params, AddAdminFunction), "add admin") + .Add("del-admin", SGCONF::MakeAPIAction(commands, "", DelAdminFunction), "del admin") + .Add("chg-admin", SGCONF::MakeAPIAction(commands, "", params, ChgAdminFunction), "change admin"); +} diff --git a/projects/sgconf/admins.h b/projects/sgconf/admins.h new file mode 100644 index 00000000..303d6d17 --- /dev/null +++ b/projects/sgconf/admins.h @@ -0,0 +1,11 @@ +#pragma once + +namespace SGCONF +{ + +class OPTION_BLOCKS; +class COMMANDS; + +void AppendAdminsOptionBlock(COMMANDS & commands, OPTION_BLOCKS & blocks); + +} // namespace SGCONF diff --git a/projects/sgconf/api_action.cpp b/projects/sgconf/api_action.cpp new file mode 100644 index 00000000..d5b1e8e6 --- /dev/null +++ b/projects/sgconf/api_action.cpp @@ -0,0 +1,40 @@ +#include "api_action.h" + +#include "actions.h" +#include "parser_state.h" + +SGCONF::PARSER_STATE SGCONF::API_ACTION::Parse(int argc, char ** argv, void * /*data*/) +{ +PARSER_STATE state(false, argc, argv); +if (!m_argument.empty()) + { + if (argc == 0 || + argv == NULL || + *argv == NULL) + throw ERROR("Missing argument."); + m_argument = *argv; + --state.argc; + ++state.argv; + } +state = m_suboptions.Parse(state.argc, state.argv, &m_params); +m_commands.Add(m_funPtr, m_argument, m_params); +return state; +} + +SGCONF::API_ACTION::API_ACTION(COMMANDS & commands, + const std::string & paramDescription, + bool needArgument, + const std::vector & params, + API_FUNCTION funPtr) + : m_commands(commands), + m_description(paramDescription), + m_argument(needArgument ? "1" : ""), // Hack + m_funPtr(funPtr) +{ +std::vector::const_iterator it(params.begin()); +while (it != params.end()) + { + m_suboptions.Add(it->name, MakeKVAction(it->name, it->shortDescr), it->longDescr); + ++it; + } +} diff --git a/projects/sgconf/api_action.h b/projects/sgconf/api_action.h new file mode 100644 index 00000000..f27715ca --- /dev/null +++ b/projects/sgconf/api_action.h @@ -0,0 +1,143 @@ +#ifndef __STG_SGCONF_API_ACTION_H__ +#define __STG_SGCONF_API_ACTION_H__ + +#include "action.h" + +#include "options.h" + +#include +#include +#include + +namespace SGCONF +{ + +typedef bool (* API_FUNCTION) (const CONFIG &, + const std::string &, + const std::map &); + +class COMMAND +{ + public: + COMMAND(API_FUNCTION funPtr, + const std::string & arg, + const std::map & options) + : m_funPtr(funPtr), + m_arg(arg), + m_options(options) + {} + bool Execute(const SGCONF::CONFIG & config) const + { + return m_funPtr(config, m_arg, m_options); + } + + private: + API_FUNCTION m_funPtr; + std::string m_arg; + std::map m_options; +}; + +class COMMANDS +{ + public: + void Add(API_FUNCTION funPtr, + const std::string & arg, + const std::map & options) { m_commands.push_back(COMMAND(funPtr, arg, options)); } + bool Execute(const SGCONF::CONFIG & config) const + { + std::vector::const_iterator it(m_commands.begin()); + bool res = true; + while (it != m_commands.end() && res) + { + res = res && it->Execute(config); + ++it; + } + return res; + } + private: + std::vector m_commands; +}; + +class API_ACTION : public ACTION +{ + public: + struct PARAM + { + PARAM(const std::string & n, + const std::string & s, + const std::string & l) + : name(n), + shortDescr(s), + longDescr(l) + {} + std::string name; + std::string shortDescr; + std::string longDescr; + }; + + API_ACTION(COMMANDS & commands, + const std::string & paramDescription, + bool needArgument, + const std::vector & params, + API_FUNCTION funPtr); + API_ACTION(COMMANDS & commands, + const std::string & paramDescription, + bool needArgument, + API_FUNCTION funPtr) + : m_commands(commands), + m_description(paramDescription), + m_argument(needArgument ? "1" : ""), // Hack + m_funPtr(funPtr) + {} + + virtual ACTION * Clone() const { return new API_ACTION(*this); } + + virtual std::string ParamDescription() const { return m_description; } + virtual std::string DefaultDescription() const { return ""; } + virtual OPTION_BLOCK & Suboptions() { return m_suboptions; } + virtual PARSER_STATE Parse(int argc, char ** argv, void * /*data*/); + + private: + COMMANDS & m_commands; + std::string m_description; + std::string m_argument; + OPTION_BLOCK m_suboptions; + std::map m_params; + API_FUNCTION m_funPtr; +}; + +inline +ACTION * MakeAPIAction(COMMANDS & commands, + const std::string & paramDescription, + const std::vector & params, + API_FUNCTION funPtr) +{ +return new API_ACTION(commands, paramDescription, true, params, funPtr); +} + +inline +ACTION * MakeAPIAction(COMMANDS & commands, + const std::vector & params, + API_FUNCTION funPtr) +{ +return new API_ACTION(commands, "", false, params, funPtr); +} + +inline +ACTION * MakeAPIAction(COMMANDS & commands, + const std::string & paramDescription, + API_FUNCTION funPtr) +{ +return new API_ACTION(commands, paramDescription, true, funPtr); +} + +inline +ACTION * MakeAPIAction(COMMANDS & commands, + API_FUNCTION funPtr) +{ +return new API_ACTION(commands, "", false, funPtr); +} + +} + +#endif diff --git a/projects/sgconf/config.h b/projects/sgconf/config.h new file mode 100644 index 00000000..f5313ef9 --- /dev/null +++ b/projects/sgconf/config.h @@ -0,0 +1,85 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Author : Maxim Mamontov + */ + +#pragma once + +#include "stg/common.h" +#include "stg/optional.h" + +#include +#include + +namespace SGCONF +{ + +struct CONFIG +{ + STG::Optional configFile; + STG::Optional server; + STG::Optional port; + STG::Optional localAddress; + STG::Optional localPort; + STG::Optional userName; + STG::Optional userPass; + STG::Optional showConfig; + + CONFIG & operator=(const CONFIG & rhs) + { + if (!rhs.configFile.empty()) + configFile = rhs.configFile; + if (!rhs.server.empty()) + server = rhs.server; + if (!rhs.port.empty()) + port = rhs.port; + if (!rhs.localAddress.empty()) + localAddress = rhs.localAddress; + if (!rhs.localPort.empty()) + localPort = rhs.localPort; + if (!rhs.userName.empty()) + userName = rhs.userName; + if (!rhs.userPass.empty()) + userPass = rhs.userPass; + if (!rhs.showConfig.empty()) + showConfig = rhs.showConfig; + return *this; + } + + std::string Serialize() const + { + std::string res; + if (!configFile.empty()) + res += "configFile: '" + configFile.data() + "'\n"; + if (!server.empty()) + res += "server: '" + server.data() + "'\n"; + if (!port.empty()) + res += "port: " + std::to_string(port.data()) + "\n"; + if (!localAddress.empty()) + res += "local address: '" + localAddress.data() + "'\n"; + if (!localPort.empty()) + res += "local port: " + std::to_string(localPort.data()) + "\n"; + if (!userName.empty()) + res += "userName: '" + userName.data() + "'\n"; + if (!userPass.empty()) + res += "userPass: '" + userPass.data() + "\n"; + return res; + } +}; + +} // namespace SGCONF diff --git a/projects/sgconf/corps.cpp b/projects/sgconf/corps.cpp new file mode 100644 index 00000000..586ddcd5 --- /dev/null +++ b/projects/sgconf/corps.cpp @@ -0,0 +1,162 @@ +#include "corps.h" + +#include "api_action.h" +#include "options.h" +#include "config.h" +#include "utils.h" + +#include "stg/servconf.h" +#include "stg/servconf_types.h" +#include "stg/corp_conf.h" +#include "stg/common.h" + +#include +#include +#include + +namespace +{ + +std::string Indent(size_t level, bool dash = false) +{ +if (level == 0) + return ""; +return dash ? std::string(level * 4 - 2, ' ') + "- " : std::string(level * 4, ' '); +} + +void PrintCorp(const STG::GetCorp::Info & info, size_t level = 0) +{ +std::cout << Indent(level, true) << "name: " << info.name << "\n" + << Indent(level) << "cash: " << info.cash << "\n"; +} + +std::vector GetCorpParams() +{ +std::vector params; +params.push_back(SGCONF::API_ACTION::PARAM("cash", "", "\tcorporation's cash")); +return params; +} + +void SimpleCallback(bool result, + const std::string & reason, + void * /*data*/) +{ +if (!result) + { + std::cerr << "Operation failed. Reason: '" << reason << "'." << std::endl; + return; + } +std::cout << "Success.\n"; +} + +void GetCorpsCallback(bool result, + const std::string & reason, + const std::vector & info, + void * /*data*/) +{ +if (!result) + { + std::cerr << "Failed to get corp list. Reason: '" << reason << "'." << std::endl; + return; + } +std::cout << "Corps:\n"; +for (size_t i = 0; i < info.size(); ++i) + PrintCorp(info[i], 1); +} + +void GetCorpCallback(bool result, + const std::string & reason, + const STG::GetCorp::Info & info, + void * /*data*/) +{ +if (!result) + { + std::cerr << "Failed to get corp. Reason: '" << reason << "'." << std::endl; + return; + } +PrintCorp(info); +} + +bool GetCorpsFunction(const SGCONF::CONFIG & config, + const std::string & /*arg*/, + const std::map & /*options*/) +{ +STG::ServConf proto(config.server.data(), + config.port.data(), + config.localAddress.data(), + config.localPort.data(), + config.userName.data(), + config.userPass.data()); +return proto.GetCorporations(GetCorpsCallback, NULL) == STG::st_ok; +} + +bool GetCorpFunction(const SGCONF::CONFIG & config, + const std::string & arg, + const std::map & /*options*/) +{ +STG::ServConf proto(config.server.data(), + config.port.data(), + config.localAddress.data(), + config.localPort.data(), + config.userName.data(), + config.userPass.data()); +return proto.GetCorp(arg, GetCorpCallback, NULL) == STG::st_ok; +} + +bool DelCorpFunction(const SGCONF::CONFIG & config, + const std::string & arg, + const std::map & /*options*/) +{ +STG::ServConf proto(config.server.data(), + config.port.data(), + config.localAddress.data(), + config.localPort.data(), + config.userName.data(), + config.userPass.data()); +return proto.DelCorp(arg, SimpleCallback, NULL) == STG::st_ok; +} + +bool AddCorpFunction(const SGCONF::CONFIG & config, + const std::string & arg, + const std::map & options) +{ +STG::CorpConfOpt conf; +conf.name = arg; +SGCONF::MaybeSet(options, "cash", conf.cash); +STG::ServConf proto(config.server.data(), + config.port.data(), + config.localAddress.data(), + config.localPort.data(), + config.userName.data(), + config.userPass.data()); +return proto.AddCorp(arg, conf, SimpleCallback, NULL) == STG::st_ok; +} + +bool ChgCorpFunction(const SGCONF::CONFIG & config, + const std::string & arg, + const std::map & options) +{ +STG::CorpConfOpt conf; +conf.name = arg; +SGCONF::MaybeSet(options, "cash", conf.cash); +STG::ServConf proto(config.server.data(), + config.port.data(), + config.localAddress.data(), + config.localPort.data(), + config.userName.data(), + config.userPass.data()); +return proto.ChgCorp(conf, SimpleCallback, NULL) == STG::st_ok; +} + +} // namespace anonymous + +void SGCONF::AppendCorpsOptionBlock(COMMANDS & commands, OPTION_BLOCKS & blocks) +{ +std::vector params(GetCorpParams()); +blocks.Add("Corporation management options") + .Add("get-corps", SGCONF::MakeAPIAction(commands, GetCorpsFunction), "\tget corporation list") + .Add("get-corp", SGCONF::MakeAPIAction(commands, "", GetCorpFunction), "get corporation") + .Add("add-corp", SGCONF::MakeAPIAction(commands, "", params, AddCorpFunction), "add corporation") + .Add("del-corp", SGCONF::MakeAPIAction(commands, "", DelCorpFunction), "delete corporation") + .Add("chg-corp", SGCONF::MakeAPIAction(commands, "", params, ChgCorpFunction), "change corporation"); +} diff --git a/projects/sgconf/corps.h b/projects/sgconf/corps.h new file mode 100644 index 00000000..550f4539 --- /dev/null +++ b/projects/sgconf/corps.h @@ -0,0 +1,11 @@ +#pragma once + +namespace SGCONF +{ + +class OPTION_BLOCKS; +class COMMANDS; + +void AppendCorpsOptionBlock(COMMANDS & commands, OPTION_BLOCKS & blocks); + +} // namespace SGCONF diff --git a/projects/sgconf/info.cpp b/projects/sgconf/info.cpp new file mode 100644 index 00000000..421e4389 --- /dev/null +++ b/projects/sgconf/info.cpp @@ -0,0 +1,60 @@ +#include "info.h" + +#include "api_action.h" +#include "options.h" +#include "config.h" + +#include "stg/servconf.h" + +#include +#include +#include + +#include + +namespace +{ + +void PrintInfo(const STG::ServerInfo::Info& info) +{ + std::cout << "Server version: '" << info.version << "'\n" + << "Number of tariffs: " << info.tariffNum << "\n" + << "Tariff subsystem version: " << info.tariffType << "\n" + << "Number of users: " << info.usersNum << "\n" + << "UName: '" << info.uname << "\n" + << "Number of directions: " << info.dirNum << "\n" + << "Dirs:\n"; + for (size_t i = 0; i < info.dirName.size(); ++i) + std::cout << "\t - '" << info.dirName[i] << "'\n"; +} + +void InfoCallback(bool result, const std::string & reason, const STG::ServerInfo::Info & info, void * /*data*/) +{ +if (!result) + { + std::cerr << "Failed to get server info. Reason: '" << reason << "'." << std::endl; + return; + } +PrintInfo(info); +} + +bool InfoFunction(const SGCONF::CONFIG & config, + const std::string& /*arg*/, + const std::map & /*options*/) +{ +STG::ServConf proto(config.server.data(), + config.port.data(), + config.localAddress.data(), + config.localPort.data(), + config.userName.data(), + config.userPass.data()); +return proto.ServerInfo(InfoCallback, NULL) == STG::st_ok; +} + +} + +void SGCONF::AppendServerInfoBlock(COMMANDS & commands, OPTION_BLOCKS & blocks) +{ +blocks.Add("Server info") + .Add("server-info", SGCONF::MakeAPIAction(commands, InfoFunction), "\tget server info"); +} diff --git a/projects/sgconf/info.h b/projects/sgconf/info.h new file mode 100644 index 00000000..3eb47a46 --- /dev/null +++ b/projects/sgconf/info.h @@ -0,0 +1,31 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Author : Maxim Mamontov + */ + +#pragma once + +namespace SGCONF +{ + +class OPTION_BLOCKS; +class COMMANDS; + +void AppendServerInfoBlock(COMMANDS & commands, OPTION_BLOCKS & blocks); + +} diff --git a/projects/sgconf/main.cpp b/projects/sgconf/main.cpp new file mode 100644 index 00000000..97908195 --- /dev/null +++ b/projects/sgconf/main.cpp @@ -0,0 +1,465 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Author : Boris Mikhailenko + * Author : Maxim Mamontov + */ + +#include "xml.h" +#include "admins.h" +#include "tariffs.h" +#include "users.h" +#include "services.h" +#include "corps.h" +#include "info.h" + +#include "api_action.h" +#include "options.h" +#include "actions.h" +#include "config.h" + +#include +#include + +#include // getenv +#include // str* + +#include // access +#include // basename + +namespace +{ + +template +struct nullary_function +{ +typedef T result_type; +}; + +template +class binder0 : public nullary_function +{ + public: + binder0(const F & func, const typename F::argument_type & arg) + : m_func(func), m_arg(arg) {} + typename F::result_type operator()() const { return m_func(m_arg); } + private: + F m_func; + typename F::argument_type m_arg; +}; + +template +inline +binder0 bind0(const F & func, const typename F::argument_type & arg) +{ +return binder0(func, arg); +} + +template +class FUNC1_ADAPTER : public std::unary_function +{ + public: + FUNC1_ADAPTER(R (*func)(A)) : m_func(func) {} + const R operator()(A arg) const { return (m_func)(arg); } + private: + R (*m_func)(A); +}; + +template +class METHOD1_ADAPTER : public std::unary_function +{ + public: + METHOD1_ADAPTER(R (C::* func)(A), C & obj) : m_func(func), m_obj(obj) {} + R operator()(A arg) { return (m_obj.*m_func)(arg); } + private: + R (C::* m_func)(A); + C & m_obj; +}; + +template +class CONST_METHOD1_ADAPTER : public std::unary_function +{ + public: + CONST_METHOD1_ADAPTER(R (C::* func)(A) const, C & obj) : m_func(func), m_obj(obj) {} + R operator()(A arg) const { return (m_obj.*m_func)(arg); } + private: + R (C::* m_func)(A) const; + C & m_obj; +}; + +template +FUNC1_ADAPTER Func1Adapt(R (func)(A)) +{ +return FUNC1_ADAPTER(func); +} + +template +METHOD1_ADAPTER Method1Adapt(R (C::* func)(A), C & obj) +{ +return METHOD1_ADAPTER(func, obj); +} + +template +CONST_METHOD1_ADAPTER Method1Adapt(R (C::* func)(A) const, C & obj) +{ +return CONST_METHOD1_ADAPTER(func, obj); +} + +void Version(const std::string & self) +{ +std::cout << self << ", version: 2.0.0.\n"; +} + +void ReadUserConfigFile(SGCONF::OPTION_BLOCK & block) +{ +std::vector paths; +const char * configHome = getenv("XDG_CONFIG_HOME"); +if (configHome == NULL) + { + const char * home = getenv("HOME"); + if (home == NULL) + return; + paths.push_back(std::string(home) + "/.config/sgconf/sgconf.conf"); + paths.push_back(std::string(home) + "/.sgconf/sgconf.conf"); + } +else + paths.push_back(std::string(configHome) + "/sgconf/sgconf.conf"); +for (std::vector::const_iterator it = paths.begin(); it != paths.end(); ++it) + if (access(it->c_str(), R_OK) == 0) + { + block.ParseFile(*it); + return; + } +} + +} // namespace anonymous + +namespace SGCONF +{ + +class CONFIG_ACTION : public ACTION +{ + public: + CONFIG_ACTION(SGCONF::CONFIG & config, + const std::string & paramDescription) + : m_config(config), + m_description(paramDescription) + {} + + virtual ACTION * Clone() const { return new CONFIG_ACTION(*this); } + + virtual std::string ParamDescription() const { return m_description; } + virtual std::string DefaultDescription() const { return ""; } + virtual OPTION_BLOCK & Suboptions() { return m_suboptions; } + virtual PARSER_STATE Parse(int argc, char ** argv, void * /*data*/); + + private: + SGCONF::CONFIG & m_config; + std::string m_description; + OPTION_BLOCK m_suboptions; + + void ParseCredentials(const std::string & credentials); + void ParseHostAndPort(const std::string & hostAndPort); +}; + + +PARSER_STATE CONFIG_ACTION::Parse(int argc, char ** argv, void * /*data*/) +{ +if (argc == 0 || + argv == NULL || + *argv == NULL) + throw ERROR("Missing argument."); +char * pos = strchr(*argv, '@'); +if (pos != NULL) + { + ParseCredentials(std::string(*argv, pos)); + ParseHostAndPort(std::string(pos + 1)); + } +else + { + ParseHostAndPort(std::string(*argv)); + } +return PARSER_STATE(false, --argc, ++argv); +} + +void CONFIG_ACTION::ParseCredentials(const std::string & credentials) +{ +std::string::size_type pos = credentials.find_first_of(':'); +if (pos != std::string::npos) + { + m_config.userName = credentials.substr(0, pos); + m_config.userPass = credentials.substr(pos + 1); + } +else + { + m_config.userName = credentials; + } +} + +void CONFIG_ACTION::ParseHostAndPort(const std::string & hostAndPort) +{ +std::string::size_type pos = hostAndPort.find_first_of(':'); +if (pos != std::string::npos) + { + m_config.server = hostAndPort.substr(0, pos); + uint16_t port = 0; + if (str2x(hostAndPort.substr(pos + 1), port)) + throw ERROR("Invalid port value: '" + hostAndPort.substr(pos + 1) + "'"); + m_config.port = port; + } +else + { + m_config.server = hostAndPort; + } +} + +inline +CONFIG_ACTION * MakeParamAction(SGCONF::CONFIG & config, + const std::string & paramDescription) +{ +return new CONFIG_ACTION(config, paramDescription); +} + +} // namespace SGCONF + +//----------------------------------------------------------------------------- +int main(int argc, char **argv) +{ +std::string self(basename(argv[0])); +SGCONF::CONFIG config; +SGCONF::COMMANDS commands; + +SGCONF::OPTION_BLOCKS blocks; +blocks.Add("General options") + .Add("c", "config", SGCONF::MakeParamAction(config.configFile, std::string("~/.config/stg/sgconf.conf"), ""), "override default config file") + .Add("h", "help", SGCONF::MakeFunc0Action(bind0(Method1Adapt(&SGCONF::OPTION_BLOCKS::Help, blocks), 0)), "\t\tshow this help and exit") + //.Add("help-all", SGCONF::MakeFunc0Action(UsageAll), "\t\tshow full help and exit") + .Add("v", "version", SGCONF::MakeFunc0Action(bind0(Func1Adapt(Version), self)), "\t\tshow version information and exit"); +SGCONF::OPTION_BLOCK & block = blocks.Add("Connection options") + .Add("s", "server", SGCONF::MakeParamAction(config.server, std::string("localhost"), "

"), "\t\thost to connect") + .Add("p", "port", SGCONF::MakeParamAction(config.port, uint16_t(5555), ""), "\t\tport to connect") + .Add("local-address", SGCONF::MakeParamAction(config.localAddress, std::string(""), "
"), "\tlocal address to bind") + .Add("local-port", SGCONF::MakeParamAction(config.localPort, uint16_t(0), ""), "\t\tlocal port to bind") + .Add("u", "username", SGCONF::MakeParamAction(config.userName, std::string("admin"), ""), "\tadministrative login") + .Add("w", "userpass", SGCONF::MakeParamAction(config.userPass, ""), "\tpassword for the administrative login") + .Add("a", "address", SGCONF::MakeParamAction(config, ""), "connection params as a single string in format: :@:"); +blocks.Add("Debug options") + .Add("show-config", SGCONF::MakeParamAction(config.showConfig), "\tshow config and exit"); +SGCONF::AppendXMLOptionBlock(commands, blocks); +SGCONF::AppendServerInfoBlock(commands, blocks); +SGCONF::AppendAdminsOptionBlock(commands, blocks); +SGCONF::AppendTariffsOptionBlock(commands, blocks); +SGCONF::AppendUsersOptionBlock(commands, blocks); +SGCONF::AppendServicesOptionBlock(commands, blocks); +SGCONF::AppendCorpsOptionBlock(commands, blocks); + +SGCONF::PARSER_STATE state(false, argc, argv); + +try +{ +state = blocks.Parse(--argc, ++argv); // Skipping self name +} +catch (const SGCONF::OPTION::ERROR& ex) +{ +std::cerr << ex.what() << "\n"; +return -1; +} + +if (state.stop) + return 0; + +if (state.argc > 0) + { + std::cerr << "Unknown option: '" << *state.argv << "'\n"; + return -1; + } + +try +{ +SGCONF::CONFIG configOverride(config); + +if (config.configFile.empty()) + { + const char * mainConfigFile = "/etc/sgconf/sgconf.conf"; + if (access(mainConfigFile, R_OK) == 0) + block.ParseFile(mainConfigFile); + ReadUserConfigFile(block); + } +else + { + block.ParseFile(config.configFile.data()); + } + +config = configOverride; + +if (!config.showConfig.empty() && config.showConfig.data()) + { + std::cout << config.Serialize() << std::endl; + return 0; + } +return commands.Execute(config) ? 0 : -1; +} +catch (const std::exception& ex) +{ +std::cerr << ex.what() << "\n"; +return -1; +} +} +//----------------------------------------------------------------------------- + +namespace +{ + +/*void UsageTariffs(bool full) +{ +std::cout << "Tariffs management options:\n" + << "\t--get-tariffs\t\t\t\tget a list of tariffs (subsequent options will define what to show)\n"; +if (full) + std::cout << "\t\t--name\t\t\t\tshow tariff's name\n" + << "\t\t--fee\t\t\t\tshow tariff's fee\n" + << "\t\t--free\t\t\t\tshow tariff's prepaid traffic in terms of cost\n" + << "\t\t--passive-cost\t\t\tshow tariff's cost of \"freeze\"\n" + << "\t\t--traff-type\t\t\tshow what type of traffix will be accounted by the tariff\n" + << "\t\t--dirs\t\t\t\tshow tarification rules for directions\n\n"; +std::cout << "\t--get-tariff\t\t\t\tget the information about tariff\n"; +if (full) + std::cout << "\t\t--name \t\t\tname of the tariff to show\n" + << "\t\t--fee\t\t\t\tshow tariff's fee\n" + << "\t\t--free\t\t\t\tshow tariff's prepaid traffic in terms of cost\n" + << "\t\t--passive-cost\t\t\tshow tariff's cost of \"freeze\"\n" + << "\t\t--traff-type\t\t\tshow what type of traffix will be accounted by the tariff\n" + << "\t\t--dirs\t\t\t\tshow tarification rules for directions\n\n"; +std::cout << "\t--add-tariff\t\t\t\tadd a new tariff\n"; +if (full) + std::cout << "\t\t--name \t\t\tname of the tariff to add\n" + << "\t\t--fee \t\t\tstariff's fee\n" + << "\t\t--free \t\t\ttariff's prepaid traffic in terms of cost\n" + << "\t\t--passive-cost \t\ttariff's cost of \"freeze\"\n" + << "\t\t--traff-type \t\twhat type of traffi will be accounted by the tariff\n" + << "\t\t--times \t\t\tslash-separated list of \"day\" time-spans (in form \"hh:mm-hh:mm\") for each direction\n" + << "\t\t--prices-day-a \t\tslash-separated list of prices for \"day\" traffic before threshold for each direction\n" + << "\t\t--prices-night-a \tslash-separated list of prices for \"night\" traffic before threshold for each direction\n" + << "\t\t--prices-day-b \t\tslash-separated list of prices for \"day\" traffic after threshold for each direction\n" + << "\t\t--prices-night-b \tslash-separated list of prices for \"night\" traffic after threshold for each direction\n" + << "\t\t--single-prices \tslash-separated list of \"single price\" flags for each direction\n" + << "\t\t--no-discounts \t\tslash-separated list of \"no discount\" flags for each direction\n" + << "\t\t--thresholds \tslash-separated list of thresholds (in Mb) for each direction\n\n"; +std::cout << "\t--del-tariff\t\t\t\tdelete an existing tariff\n"; +if (full) + std::cout << "\t\t--name \t\t\tname of the tariff to delete\n\n"; +std::cout << "\t--chg-tariff\t\t\t\tchange an existing tariff\n"; +if (full) + std::cout << "\t\t--name \t\t\tname of the tariff to change\n" + << "\t\t--fee \t\t\tstariff's fee\n" + << "\t\t--free \t\t\ttariff's prepaid traffic in terms of cost\n" + << "\t\t--passive-cost \t\ttariff's cost of \"freeze\"\n" + << "\t\t--traff-type \t\twhat type of traffix will be accounted by the tariff\n" + << "\t\t--dir \t\t\tnumber of direction data to change\n" + << "\t\t\t--time