/* * This file is part of the Flowee project * Copyright (C) 2019 Tom Zander * * 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 3 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, see . */ #include #include #include #include #include #include #include /* Interprets env vars; FLOWEE_INDEXERS list of indexers to enable. For instance: =txid, address, spent FLOWEE_DB list of variables to connect to the DB driver=postgres,hostname=localhost,db=flowee,username=flowee_indexer,password=SECRET FLOWEE_HUB=host:port FLOWEE_LOGLEVEL info, quiet or silent */ static QProcess *indexer = new QProcess(); void HandleSignals(int) { qWarning() << "Docker: TERM received"; qint64 pid = indexer->processId(); if (pid > 0) kill(pid, SIGTERM); // politely tell the indexer to terminate } int main(int x, char**y) { QCoreApplication app(x,y); QDir data("/data"); if (!data.exists()) { qWarning() << "No volume found on /data, refusing to start"; return 1; } const QDir confDir(QDir::homePath() + "/.config/flowee/indexer"); confDir.mkpath("."); QFile configFile(confDir.absolutePath() + "/indexer.conf"); if (!configFile.open(QIODevice::WriteOnly)) { qWarning() << "Can't write conf file"; } else { QTextStream out(&configFile); out << "# autogenerated flowee indexer config\n"; QString dbSpec = getenv("FLOWEE_DB"); QStringList pairs = dbSpec.split(QRegExp("[ ,;&]"), QString::SkipEmptyParts); for (auto pair : pairs) { QStringList items = pair.split("="); if (items.size() != 2) { qWarning() << "DB item not understood:" << pair; continue; } auto token = items.at(0).toLower(); if (token == "driver") { auto driver = items.at(1).toUpper(); if (driver.startsWith("POSTGRES") || driver == "PSQL") { driver = "QPSQL"; } // TODO support other drivers out << "db_driver=" << driver << endl; } else if (token == "hostname") { out << "db_hostname=" << items.at(1) << endl; } else if (token == "db" || token == "database") { out << "db_database=" << items.at(1) << endl; } else if (token == "user" || token == "username" || token == "uname") { out << "db_username=" << items.at(1) << endl; } else if (token == "pwd" || token == "password" || token == "passwd") { out << "db_password=" << items.at(1) << endl; } } out << endl; QString indexers = getenv("FLOWEE_INDEXERS"); if (indexers.isEmpty()) indexers = "txid"; QStringList components = indexers.split(QRegExp("[, :;]"), QString::SkipEmptyParts); for (auto c : components) { if (c.compare("txid", Qt::CaseInsensitive) == 0 || c.compare("txdb", Qt::CaseInsensitive) == 0 || c == "transaction-id") { out << "[txdb]\nenabled=true\n\n"; } else if (c.compare("addressdb", Qt::CaseInsensitive) == 0 || c.compare("address", Qt::CaseInsensitive) == 0 || c.compare("addresses", Qt::CaseInsensitive) == 0) { out << "[addressdb]\nenabled=true\n\n"; } else if (c.compare("spentdb", Qt::CaseInsensitive) == 0 || c.compare("spenddb", Qt::CaseInsensitive) == 0 || c.compare("spend", Qt::CaseInsensitive) == 0 || c.compare("spent", Qt::CaseInsensitive) == 0) { out << "[spentdb]\nenabled=true\n\n"; } } configFile.close(); } QFile logsFile(confDir.absolutePath() + "/logs.conf"); QString logLevel = getenv("FLOWEE_LOGLEVEL"); if (logLevel.isEmpty() && logsFile.exists()) { qWarning() << "Not changing existing logs.conf"; } else if (!logsFile.open(QIODevice::WriteOnly)) { qWarning() << "Can't write logs.conf file" << confDir.absoluteFilePath("logs.conf"); } else { QTextStream out(&logsFile); out << "# Flowee logging config.\n" "channel console\n" " option timestamp\n" "channel file\n" " option timestamp date time\n" " option path /data/indexer.log\n"; if (logLevel.toLower() == "info") out << "\nALL info\n"; else if (logLevel.toLower() == "quiet") out << "\nALL quiet\n"; else if (logLevel.toLower() == "silent") out << "\nALL silent\n"; else if (!logLevel.isEmpty()) qWarning() << "FLOWEE_LOGLEVEL not understood. Options are 'info', 'quiet' or 'silent'"; logsFile.close(); } struct sigaction sa; sa.sa_handler = HandleSignals; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); // Ignore SIGPIPE signal(SIGPIPE, SIG_IGN); QStringList args; args << "--conf" << confDir.absolutePath() + "/indexer.conf" << "--datadir" << "/data" << "--bind" << "0.0.0.0:1234"; if (getenv("FLOWEE_HUB")) { QString hub = getenv("FLOWEE_HUB"); args << "--connect" << hub; } else { qWarning() << "WARNING: Without FLOWEE_HUB env-var we will not get updated!"; } indexer->setReadChannel(QProcess::StandardOutput); indexer->start(QLatin1String("/usr/bin/indexer"), args, QIODevice::ReadOnly); indexer->waitForReadyRead(20000); if (indexer->state() == QProcess::NotRunning) { // time out qWarning() << "ERROR: indexer fails to start, timing out"; indexer->kill(); return 1; } QTextStream out(stdout); while (true) { auto logData = indexer->readAllStandardError(); out << logData; logData = indexer->readAllStandardOutput(); out << logData; if (indexer->state() != QProcess::Running) break; out.flush(); indexer->waitForReadyRead(20000); } fflush(NULL); sync(); return indexer->exitCode(); }