#include "tfarmserver.h"
#include "tfarmexecutor.h"
#include "tfarmcontroller.h"
#include "tthreadmessage.h"
#include "tthread.h"
#include "tsystem.h"
#include "tsmartpointer.h"
#include "service.h"
#include "tlog.h"
#include "tfilepath_io.h"
#include "tcli.h"
#include "tversion.h"
using namespace TVER;
#include <string>
#include <map>
#include <sstream>
#include <QString>
#include <QProcess>
#include <QCoreApplication>
#include <QEventLoop>
#include "tthread.h"
#ifdef _WIN32
#include <iostream>
#else
#include <sys/param.h>
#include <unistd.h>
#endif
// #define REDIRECT_OUTPUT
#ifdef _WIN32
#define QUOTE_STR "\""
#define CASMPMETER "casmpmeter.exe"
#else
#define QUOTE_STR "'"
#define CASMPMETER "casmpmeter"
#endif
#ifndef _WIN32
#define NO_ERROR 0
#endif
// forward declaration
class FarmServer;
//-------------------------------------------------------------------
namespace {
//--------------------------------------------------------------------
TFilePath getGlobalRoot() {
TVER::ToonzVersion tver;
TFilePath rootDir;
#ifdef _WIN32
std::string regpath = "SOFTWARE\\" + tver.getAppName() + "\\" +
tver.getAppName() + "\\" + tver.getAppVersionString() +
"\\FARMROOT";
TFilePath name(regpath);
rootDir = TFilePath(TSystem::getSystemValue(name).toStdString());
#else
// Leggo la localRoot da File txt
#ifdef MACOSX
// If MACOSX, change to MACOSX path
std::string unixpath =
"./" + tver.getAppName() + ".app/Contents/Resources/configfarmroot.txt";
#else
// set path to something suitable for most linux (Unix?) systems
std::string unixpath = "/etc/" + tver.getAppName() + "/opentoonz.conf";
#endif
TFilePath name(unixpath);
Tifstream is(name);
if (is) {
char line[1024];
is.getline(line, 80);
char *s = line;
while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\"') s++;
if (*s != '\0') {
char *t = s;
while (*t) t++;
std::string pathName(s, t - 1);
rootDir = TFilePath(pathName);
}
}
#endif
return rootDir;
}
//--------------------------------------------------------------------
TFilePath getLocalRoot() {
TVER::ToonzVersion tver;
TFilePath lroot;
#ifdef _WIN32
QString regpath = QString::fromStdString(
"SOFTWARE\\" + tver.getAppName() + "\\" + tver.getAppName() + "\\" +
tver.getAppVersionString() + "\\FARMROOT");
TFilePath name(regpath);
lroot = TFilePath(TSystem::getSystemValue(name).toStdString()) +
TFilePath("toonzfarm");
#else
#ifdef MACOSX
// If MACOSX, change to MACOSX path
std::string unixpath =
"./" + tver.getAppName() + ".app/Contents/Resources/configfarmroot.txt";
#else
// set path to something suitable for most linux (Unix?) systems
#ifdef FREEBSD
std::string unixpath =
"/usr/local/etc/" + tver.getAppName() + "/opentoonz.conf";
#else
std::string unixpath = "/etc/" + tver.getAppName() + "/opentoonz.conf";
#endif
#endif
TFilePath name(unixpath);
Tifstream is(name);
if (is) {
char line[1024];
is.getline(line, 80);
char *s = line;
while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\"') s++;
if (*s != '\0') {
char *t = s;
while (*t) t++;
std::string pathName(s, t - 1);
lroot = TFilePath(pathName);
}
}
#endif
return lroot;
}
//--------------------------------------------------------------------
TFilePath getBinRoot() {
#ifdef _WIN32
return TSystem::getBinDir();
#else
return getLocalRoot() + "bin";
#endif
}
//--------------------------------------------------------------------
bool dirExists(const TFilePath &dirFp) {
bool exists = false;
#ifdef _WIN32
TFileStatus fs(dirFp);
exists = fs.isDirectory();
#else
int acc = access(::to_string(dirFp).c_str(), 00); // 00 == solo esistenza
exists = acc != -1;
#endif
return exists;
}
//--------------------------------------------------------------------
bool myDoesExists(const TFilePath &fp) {
bool exists = false;
#ifdef _WIN32
TFileStatus fs(fp);
exists = fs.doesExist();
#else
int acc = access(::to_string(fp).c_str(), 00); // 00 == solo esistenza
exists = acc != -1;
#endif
return exists;
}
//-----------------------------------------------------------------------------
inline bool isBlank(char c) { return c == ' ' || c == '\t' || c == '\n'; }
} // anonymous namespace
//==============================================================================
//==============================================================================
class FarmServerService final : public TService {
public:
FarmServerService(std::ostream &os)
: TService("ToonzFarm Server", "ToonzFarm Server")
, m_os(os)
, m_userLog(0) {}
~FarmServerService() { delete m_userLog; }
void onStart(int argc, char *argv[]) override;
void onStop() override;
void loadControllerData(QString &hostName, std::string &ipAddr, int &port);
#ifdef _WIN32
void loadDiskMountingPoints(const TFilePath &fp);
void mountDisks();
void unmountDisks();
std::map<std::string, std::string> m_disks;
std::vector<std::string> m_disksMounted;
#endif
int m_port;
QString m_addr;
FarmServer *m_farmServer;
std::ostream &m_os;
TUserLog *m_userLog;
};
FarmServerService service(std::cout);
//==============================================================================
//==============================================================================
class FarmControllerProxy final : public TSmartObject {
TFarmController *m_controller;
public:
FarmControllerProxy(TFarmController *controller) : m_controller(controller) {}
~FarmControllerProxy() { delete m_controller; }
TFarmController *getController() const { return m_controller; }
private:
// not implemented
FarmControllerProxy(const FarmControllerProxy &);
FarmControllerProxy &operator=(const FarmControllerProxy &);
};
// non posso usare lo smartpointer di tnzcore perche' non linka su .NET
class FarmControllerProxyP {
FarmControllerProxy *m_proxy;
public:
FarmControllerProxyP() : m_proxy(0) {}
~FarmControllerProxyP() {
if (m_proxy) m_proxy->release();
}
FarmControllerProxyP(const FarmControllerProxyP &src) : m_proxy(src.m_proxy) {
if (m_proxy) m_proxy->addRef();
}
FarmControllerProxyP &operator=(const FarmControllerProxyP &src) {
FarmControllerProxyP tmp(*this);
std::swap(tmp.m_proxy, m_proxy);
return *this;
}
FarmControllerProxyP &operator=(TFarmController *controller) {
if (m_proxy && m_proxy->getController() == controller) return *this;
if (m_proxy) m_proxy->release();
m_proxy = new FarmControllerProxy(controller);
m_proxy->addRef();
return *this;
}
TFarmController *operator->() { return getPointer(); }
TFarmController *getPointer() const {
return m_proxy ? m_proxy->getController() : 0;
}
};
//==============================================================================
//==============================================================================
class FarmServer final : public TFarmExecutor, public TFarmServer {
public:
FarmServer(int port, TUserLog *log);
~FarmServer();
void setController(const ControllerData &data) {
m_controllerData = data;
TFarmController *controller = 0;
TFarmControllerFactory factory;
factory.create(data, &controller);
m_controller = controller;
}
TFarmController *getController() const { return m_controller.getPointer(); }
void setAppPaths(const std::vector<TFilePath> &);
QString execute(const std::vector<QString> &argv) override;
// TFarmServer overrides
int addTask(const QString &taskid, const QString &cmdline) override;
int terminateTask(const QString &taskid) override;
int getTasks(std::vector<QString> &tasks) override;
void queryHwInfo(HwInfo &hwInfo) override;
void attachController(const ControllerData &data);
void attachController(const QString &name, const QString &addr,
int port) override {
attachController(ControllerData(name, addr, port));
}
void detachController(const ControllerData &data);
void detachController(const QString &name, const QString &addr,
int port) override {
detachController(ControllerData(name, addr, port));
}
// class specific methods
void removeTask(const QString &id);
private:
TThread::Executor *m_executor;
ControllerData m_controllerData;
FarmControllerProxyP m_controller;
TThread::Mutex m_mux;
std::vector<QString> m_tasks;
TUserLog *m_userLog;
public:
// vector<TFilePath> m_appPaths;
private:
// not implemented
FarmServer(const FarmServer &);
FarmServer &operator=(const FarmServer &);
};
//===================================================================
//===================================================================
//
// class Task
//
//===================================================================
//===================================================================
class Task final : public TThread::Runnable {
public:
Task(const QString &id, const QString &cmdline, TUserLog *log,
FarmServer *server, const FarmControllerProxyP &controller)
: m_id(id)
, m_cmdline(cmdline)
, m_log(log)
, m_server(server)
, m_controller(controller) {}
void run() override;
private:
QString m_id;
QString m_cmdline;
TUserLog *m_log;
FarmServer *m_server;
FarmControllerProxyP m_controller;
private:
// not implemented
Task(const Task &);
Task &operator=(const Task &);
};
//-------------------------------------------------------------------
static QString getExeName(bool isComposer) {
QString name = isComposer ? "tcomposer" : "tcleanup";
#ifdef _WIN32
return name + ".exe ";
#elif defined(MACOSX)
TVER::ToonzVersion tver;
return "\"./" + QString::fromStdString(tver.getAppName()) +
".app/Contents/MacOS/" + name + "\" ";
#else
return name;
#endif
}
//------------------------------------------------------------------------------
void Task::run() {
QString cmdline;
// ===========
// remap commandLine to local executable
QStringList l = m_cmdline.split(" ");
QString appName = l.at(0);
// m_log->info(appName);
if (appName.contains("tcomposer") || appName.contains("tcleanup")) {
bool m_isComposerTask = appName.contains("tcomposer");
// m_log->info(QString::number(m_isComposerTask));
appName = getExeName(m_isComposerTask);
// m_log->info(appName);
int i = 0;
cmdline = appName;
// m_log->info(cmdline);
for (i = 1; i < l.size(); i++) {
cmdline += " ";
cmdline += l.at(i);
// m_log->info(cmdline);
}
// m_log->info("remap commandLine to local executable");
// m_log->info(appName);
} else {
cmdline = m_cmdline;
}
// ===========
QString logMsg("Starting task at ");
logMsg += QDateTime::currentDateTime().toString();
logMsg += "\n";
logMsg += "\"" + cmdline + "\"";
logMsg += "\n\n";
m_log->info(logMsg);
// ===========
#ifdef _WIN32
if (m_cmdline.contains("runcasm")) service.mountDisks();
#endif
if (m_cmdline.contains(".bat"))
cmdline = "cmd /C " + cmdline;
else
cmdline = cmdline;
#ifdef LEVO
else {
// metto da parte il primo token della command line che e' il nome
// dell'eseguibile
// Attenzione: case sensitive!
QStringList l = m_cmdline.split(" ");
// assert(!"CONTROLLARE QUI");
QString appName = l.at(1);
int i;
for (i = 2; i < l.size(); i++) cmdline += l.at(i);
// cerco se il nome dell'applicazione e' tra quelle del file di
// configurazione
for (auto const &appPath : m_server->m_appPaths) {
if (appPath.getName() == appName.toStdString()) {
exename = QString::fromStdWString(appPath.getWideString());
break;
}
}
}
#endif // LEVO
// cout << exename << endl;
// cout << cmdline << endl;
// parse command line
QString prgName;
QString argsStr = "";
int sepPos = cmdline.indexOf(" ");
if (sepPos == -1) {
prgName = cmdline;
} else {
prgName = cmdline.left(sepPos);
argsStr = cmdline.right(cmdline.size() - sepPos - 1);
}
QProcess process;
process.setProgram(prgName);
#if defined(_WIN32)
process.setNativeArguments(argsStr);
#else
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
process.setArguments(argsStr.split(" ", Qt::SkipEmptyParts));
#else
process.setArguments(argsStr.split(" ", QString::SkipEmptyParts));
#endif
#endif
process.start();
process.waitForFinished(-1);
int exitCode = process.exitCode();
int errorCode = process.error();
bool ret = (errorCode != QProcess::UnknownError) || exitCode;
// int ret=QProcess::execute(/*"C:\\depot\\vincenzo\\toonz\\main\\x86_debug\\"
// +*/cmdline);
if (ret != 0) {
QString logMsg("Task aborted ");
logMsg += "\n\n";
m_log->warning(logMsg);
m_controller->taskSubmissionError(m_id, exitCode);
} else {
logMsg = "Task completed at ";
logMsg += QDateTime::currentDateTime().toString();
logMsg += "\n\n";
m_log->info(logMsg);
m_controller->taskCompleted(m_id, exitCode);
// CloseHandle(hJob);
}
m_server->removeTask(m_id);
//************* COMMENTATO A CAUSA DI UN PROBLEMA SU XP
// ora i dischi vengono montati al primo task di tipo "runcasm"
// e smontati allo stop del servizio
// service.unmountDisks();
}
//==============================================================================
//==============================================================================
FarmServer::FarmServer(int port, TUserLog *log)
: TFarmExecutor(port), m_controller(), m_userLog(log) {
TFarmServer::HwInfo hwInfo;
queryHwInfo(hwInfo);
m_executor = new TThread::Executor;
m_executor->setMaxActiveTasks(1);
}
//------------------------------------------------------------------------------
FarmServer::~FarmServer() { delete m_executor; }
//------------------------------------------------------------------------------
QString FarmServer::execute(const std::vector<QString> &argv) {
if (argv.size() > 0) {
if (argv[0] == "addTask" && argv.size() == 3) {
// assert(!"Da fare");
int ret = addTask(argv[1], argv[2]);
return QString::number(ret);
} else if (argv[0] == "terminateTask" && argv.size() > 1) {
int ret = terminateTask(argv[1]);
return QString::number(ret);
} else if (argv[0] == "getTasks") {
std::vector<QString> tasks;
int ret = getTasks(tasks);
QString reply(QString::number(ret));
reply += ",";
for (auto const &e : tasks) {
reply += e;
reply += ",";
}
if (!reply.isEmpty()) reply = reply.left(reply.size() - 1);
return reply;
} else if (argv[0] == "queryHwInfo") {
TFarmServer::HwInfo hwInfo;
queryHwInfo(hwInfo);
QString ret;
ret += QString::number((unsigned long)hwInfo.m_cpuCount);
ret += ",";
ret += QString::number((unsigned long)(hwInfo.m_totPhysMem / 1024));
ret += ",";
ret += QString::number((unsigned long)(hwInfo.m_availPhysMem / 1024));
ret += ",";
ret += QString::number((unsigned long)(hwInfo.m_totVirtMem / 1024));
ret += ",";
ret += QString::number((unsigned long)(hwInfo.m_availVirtMem / 1024));
ret += ",";
ret += QString::number(hwInfo.m_type);
return ret;
} else if (argv[0] == "attachController" && argv.size() > 3) {
int port;
fromStr(port, argv[3]);
attachController(ControllerData(argv[1], argv[2], port));
return "";
} else if (argv[0] == "detachController" && argv.size() > 3) {
int port;
fromStr(port, argv[3]);
detachController(ControllerData(argv[1], argv[2], port));
return "";
}
}
return QString::number(-1);
;
}
//------------------------------------------------------------------------------
int FarmServer::addTask(const QString &id, const QString &cmdline) {
// std::cout << "Server: addTask" << id << cmdline << std::endl;
QString lcmdline = cmdline;
if (lcmdline.contains("runcasm")) {
// il task e' runcasm
TFilePath rootDir = getGlobalRoot();
TFilePath logfilePath =
(rootDir + "logs" + id.toStdString()).withType(".log");
lcmdline +=
" -logfile " + QString::fromStdWString(logfilePath.getWideString());
TFilePath casmpmeterFp = getBinRoot() + CASMPMETER;
lcmdline += " -ac " + QString::fromStdWString(casmpmeterFp.getWideString());
lcmdline += " -ac_args " + QString(QUOTE_STR);
lcmdline += "$count $total $frame $filename " + id + QUOTE_STR;
}
if (lcmdline.contains("zrender")) lcmdline += " -taskid " + id;
if (lcmdline.contains("tcomposer")) {
lcmdline += " -farm " + QString::number(m_controllerData.m_port) + "@" +
m_controllerData.m_hostName;
lcmdline += " -id " + id;
}
m_executor->addTask(new Task(id, lcmdline, m_userLog, this, m_controller));
QMutexLocker sl(&m_mux);
m_tasks.push_back(id);
return 0;
}
//------------------------------------------------------------------------------
int FarmServer::terminateTask(const QString &taskid) {
#ifdef _WIN32
HANDLE hJob = OpenJobObject(MAXIMUM_ALLOWED, // access right
TRUE, // inheritance state
taskid.toUtf8()); // job name
if (hJob != NULL) {
BOOL res = TerminateJobObject(hJob, // handle to job
2); // exit code
}
#else
#endif
return 0;
}
//------------------------------------------------------------------------------
int FarmServer::getTasks(std::vector<QString> &tasks) {
QMutexLocker sl(&m_mux);
tasks = m_tasks;
return m_tasks.size();
}
//------------------------------------------------------------------------------
void FarmServer::queryHwInfo(HwInfo &hwInfo) {
#ifdef _WIN32
MEMORYSTATUS buff;
GlobalMemoryStatus(&buff);
hwInfo.m_totPhysMem = buff.dwTotalPhys;
hwInfo.m_availPhysMem = buff.dwAvailPhys;
hwInfo.m_totVirtMem = buff.dwTotalVirtual;
hwInfo.m_availVirtMem = buff.dwAvailVirtual;
hwInfo.m_cpuCount = TSystem::getProcessorCount();
hwInfo.m_type = Windows;
#else
// We can just retrieve the overall physical memory - the rest is defaulted to
// 500 MB
hwInfo.m_totPhysMem = TSystem::getMemorySize(true);
hwInfo.m_availPhysMem = TSystem::getFreeMemorySize(true);
hwInfo.m_totVirtMem = 500000000;
hwInfo.m_availVirtMem = 500000000;
hwInfo.m_cpuCount = TSystem::getProcessorCount();
#ifdef __sgi
hwInfo.m_type = Irix;
#else
hwInfo.m_type = Linux;
#endif
#endif
}
//------------------------------------------------------------------------------
void FarmServer::attachController(const ControllerData &data) {
setController(data);
}
//------------------------------------------------------------------------------
void FarmServer::detachController(const ControllerData &data) {
if (m_controllerData == data) {
// delete m_controller;
m_controller = 0;
}
}
//------------------------------------------------------------------------------
void FarmServer::removeTask(const QString &id) {
QMutexLocker sl(&m_mux);
std::vector<QString>::iterator it = find(m_tasks.begin(), m_tasks.end(), id);
if (it != m_tasks.end()) m_tasks.erase(it);
}
//==============================================================================
namespace {
std::string getLine(std::istream &is) {
std::string out;
char c;
while (!is.eof()) {
is.get(c);
if (c != '\r') {
if (c != '\n') {
if (!is.fail()) {
out.append(1, c);
} else {
break;
}
} else {
break;
}
}
}
return out;
}
} // anonymous namespace
int inline STRICMP(const QString &a, const QString &b) {
return a.compare(b, Qt::CaseSensitive);
}
int inline STRICMP(const char *a, const char *b) {
QString str(a);
return str.compare(QString(b), Qt::CaseSensitive);
}
static bool loadServerData(const QString &hostname, QString &addr, int &port) {
TFilePath rootDir = getGlobalRoot();
TFilePath fp = rootDir + "config" + "servers.txt";
#ifndef _WIN32
int acc = access(::to_string(fp).c_str(), 00); // 00 == solo esistenza
bool fileExists = acc != -1;
if (!fileExists) return false;
#endif
Tifstream is(fp);
if (!is.good()) return false;
while (!is.eof()) {
std::string line = getLine(is);
std::istringstream iss(line);
std::string name;
std::string ipAddress;
iss >> name >> ipAddress >> port;
if (name[0] == '#') continue;
if (STRICMP(hostname.toUtf8(), name.c_str()) == 0) {
addr = QString(ipAddress.c_str());
return true;
}
}
return false;
}
//==============================================================================
void FarmServerService::onStart(int argc, char *argv[]) {
// Initialize thread components
TThread::init();
TVER::ToonzVersion tver;
#ifdef _WIN32
// DebugBreak();
#endif
TFilePath lRootDir = getLocalRoot();
TFileStatus fs(lRootDir);
bool lRootDirExists = fs.isDirectory();
if (!lRootDirExists) {
std::string errMsg("Unable to start the Server");
errMsg += "\n";
errMsg += "The directory " + ::to_string(lRootDir) +
" specified as Local Root does not exist";
errMsg += "\n";
addToMessageLog(errMsg);
// DEBUG MAC SERVIZIO (DA TOGLIERE)
#ifdef MACOSX
system("echo 'local root does not exist' >> err.log");
#endif
// exit the program
setStatus(TService::Stopped, NO_ERROR, 0);
}
TFilePath gRootDir = getGlobalRoot();
if (::to_string(gRootDir) == "") {
std::string errMsg("Unable to get TFARMGLOBALROOT environment variable (" +
::to_string(gRootDir) + ")");
addToMessageLog(errMsg);
// DEBUG MAC SERVIZIO (DA TOGLIERE)
#ifdef MACOSX
system("echo 'Unable to set the global root' >> err.log");
#endif
// exit the program
setStatus(TService::Stopped, NO_ERROR, 0);
}
bool gRootDirExists = dirExists(gRootDir);
;
if (!gRootDirExists) {
std::string errMsg("Unable to start the Server");
errMsg += "\n";
errMsg += "The directory " + ::to_string(gRootDir) +
" specified as Global Root does not exist";
;
addToMessageLog(errMsg);
// DEBUG MAC SERVIZIO (DA TOGLIERE)
#ifdef MACOSX
system("echo 'Global root does not exist' >> err.log");
#endif
// exit the program
setStatus(TService::Stopped, NO_ERROR, 0);
}
// legge dal file di configurazione le informazioni sul controller
TFilePath fp = gRootDir + "config" + "controller.txt";
ControllerData controllerData;
try {
::loadControllerData(fp, controllerData);
} catch (TException &e) {
std::string errMsg("Unable to start the Server");
errMsg += "\n";
errMsg += ::to_string(e.getMessage());
addToMessageLog(errMsg);
setStatus(TService::Stopped, NO_ERROR, 0); // exit the program
}
if (isRunningAsConsoleApp()) {
// i messaggi verranno ridiretti sullo standard output
m_userLog = new TUserLog();
} else {
TFilePath logFilePath = lRootDir + "server.log";
m_userLog = new TUserLog(logFilePath);
}
std::string appverinfo = tver.getAppVersionInfo("Farm Server") + "\n\n";
m_userLog->info(appverinfo.c_str());
// legge dal file di configurazione dei server il numero di porta da
// utilizzare
bool ret = loadServerData(TSystem::getHostName(), m_addr, m_port);
if (!ret) {
QString msg("Unable to get the port number of ");
msg += TSystem::getHostName();
msg += " from the servers config file";
msg += "\n";
msg += "Using the default port number ";
msg += QString::number(m_port);
msg += "\n";
msg += "\n";
m_userLog->info(msg);
m_port = 8002;
}
#ifdef __sgi
{
std::ofstream os("/tmp/.tfarmserverd.dat");
os << m_port;
}
#endif
m_farmServer = new FarmServer(m_port, m_userLog);
m_farmServer->setController(controllerData);
try {
m_farmServer->getController()->attachServer(TSystem::getHostName(), m_addr,
m_port);
} catch (TException const &) {
}
#ifdef _WIN32
TFilePath diskMountingsFilePath = lRootDir + "config" + "diskmap.cfg";
if (myDoesExists(diskMountingsFilePath)) {
loadDiskMountingPoints(diskMountingsFilePath);
// i dischi vengono montati al primo task di tipo "runcasm"
// e smontati allo stop del servizio
// mountDisks();
}
#endif
// Carica da un file di configurazione i path dei programmi da lanciare.
// Per tutti i programmi il cui path non e' contenuto nel file di
// configurazione
// si assume che il path del folder del programma sia specificato
// nella variabile di sistema PATH
QEventLoop eventLoop;
// Connect the server's listening finished signal to main loop quit.
QObject::connect(m_farmServer, SIGNAL(finished()), &eventLoop, SLOT(quit()));
// Run the TcpIp server's listening state
m_farmServer->start();
// Main loop starts here
eventLoop.exec();
//----------------------Farm server loops here------------------------
int rc = m_farmServer->getExitCode();
#ifdef __sgi
remove("/tmp/.tfarmserver.dat");
#endif
if (rc != 0) {
std::string msg("An error occurred starting the ToonzFarm Server");
msg += "\n";
#ifdef _WIN32
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, rc,
0, // Default language
(LPTSTR)&lpMsgBuf, 0, NULL);
msg += std::string((char *)lpMsgBuf);
// Free the buffer.
LocalFree(lpMsgBuf);
#else
#endif
addToMessageLog(msg);
m_userLog->error(QString::fromStdString(msg));
setStatus(TService::Stopped, NO_ERROR, 0);
}
std::string msg("Exiting with code ");
msg += std::to_string(ret);
msg += "\n";
m_userLog->info(QString::fromStdString(msg));
}
//------------------------------------------------------------------------------
void FarmServerService::onStop() {
try {
m_farmServer->getController()->detachServer(TSystem::getHostName(), m_addr,
m_port);
} catch (TException & /*e*/) {
}
// i dischi vengono montati al primo task di tipo "runcasm"
// e smontati allo stop del servizio
#ifdef _WIN32
unmountDisks();
#endif
TTcpIpClient client;
int socketId;
int ret = client.connect(TSystem::getHostName(), "", m_farmServer->getPort(),
socketId);
if (ret == OK) {
client.send(socketId, "shutdown");
}
}
#ifdef _WIN32
//------------------------------------------------------------------------------
void FarmServerService::loadDiskMountingPoints(const TFilePath &fp) {
Tifstream is(fp);
if (!is) throw std::string("File " + ::to_string(fp) + " not found");
char buffer[1024];
while (is.getline(buffer, sizeof(buffer))) {
char *s = buffer;
while (isBlank(*s)) s++;
if (*s == '\0' || *s == '#' || *s == '!') continue;
if (*s == '=') continue; // errore: from vuoto
char *t = s;
while (*t && *t != '=') t++;
if (*t != '=') continue; // errore: manca '='
char *q = t;
while (q > s && isBlank(*(q - 1))) q--;
if (q == s)
continue; // non dovrebbe succedere mai: prima di '=' tutti blanks
std::string from(s, q - s);
s = t + 1;
while (isBlank(*s)) s++;
if (*s == '\0') continue; // errore: dst vuoto
t = s;
while (*t) t++;
while (t > s && isBlank(*(t - 1))) t--;
if (t == s) continue; // non dovrebbe succedere mai: dst vuoto
std::string dst(s, t - s);
m_disks[from] = dst;
}
}
//------------------------------------------------------------------------------
void FarmServerService::mountDisks() {
std::map<std::string, std::string>::iterator it = m_disks.begin();
for (; it != m_disks.end(); ++it) {
std::string drive = it->first;
std::string remoteName = it->second;
NETRESOURCE NetResource;
NetResource.dwType = RESOURCETYPE_DISK;
NetResource.lpLocalName = (LPSTR)drive.c_str(); // "O:";
NetResource.lpRemoteName =
(LPSTR)remoteName.c_str(); // "\\\\vega\\PERSONALI";
NetResource.lpProvider = NULL;
DWORD res =
WNetAddConnection2(&NetResource, // connection details
0, // password
TSystem::getUserName().toUtf8(), // user name
0); // connection options
if (res == NO_ERROR) m_disksMounted.push_back(drive);
if (res != NO_ERROR && res != ERROR_ALREADY_ASSIGNED) {
// ERROR_BAD_NET_NAME
DWORD dwLastError;
char errorBuf[1024];
char nameBuf[1024];
DWORD rett =
WNetGetLastError(&dwLastError, // error code
errorBuf, // error description buffer
sizeof(errorBuf), // size of description buffer
nameBuf, // buffer for provider name
sizeof(nameBuf)); // size of provider name buffer
std::string errorMessage("Unable to map ");
errorMessage += NetResource.lpRemoteName;
errorMessage += " to logic volume ";
errorMessage += NetResource.lpLocalName;
addToMessageLog(errorMessage);
}
}
}
//------------------------------------------------------------------------------
void FarmServerService::unmountDisks() {
for (auto const &drive : m_disksMounted) {
DWORD res =
WNetCancelConnection2(drive.c_str(), // resource name
CONNECT_UPDATE_PROFILE, // connection type
TRUE); // unconditional disconnect option
if (res != NO_ERROR && res != ERROR_NOT_CONNECTED) {
std::string errorMessage("Unable to unmap ");
errorMessage += drive.c_str();
addToMessageLog(errorMessage);
}
}
m_disksMounted.clear();
}
#endif
//==============================================================================
//==============================================================================
//==============================================================================
int main(int argc, char **argv) {
QCoreApplication a(argc, argv);
bool console = false;
if (argc > 1) {
std::string serviceName(
"ToonzFarmServer"); // Must be the same of the installer's
std::string serviceDisplayName = serviceName;
TCli::SimpleQualifier consoleQualifier("-console", "Run as console app");
TCli::StringQualifier installQualifier("-install name",
"Install service as 'name'");
TCli::SimpleQualifier removeQualifier("-remove", "Remove service");
TCli::Usage usage(argv[0]);
usage.add(consoleQualifier + installQualifier + removeQualifier);
if (!usage.parse(argc, argv)) exit(1);
#ifdef _WIN32
if (installQualifier.isSelected()) {
char szPath[512];
if (installQualifier.getValue() != "")
serviceDisplayName = installQualifier.getValue();
if (GetModuleFileName(NULL, szPath, 512) == 0) {
std::cout << "Unable to install";
std::cout << serviceName << " - ";
std::cout << getLastErrorText().c_str() << std::endl << std::endl;
return 0;
}
TService::install(serviceName, serviceDisplayName, TFilePath(szPath));
return 0;
}
if (removeQualifier.isSelected()) {
TService::remove(serviceName);
return 0;
}
#endif
if (consoleQualifier.isSelected()) console = true;
}
TSystem::hasMainLoop(false);
TService::instance()->run(argc, argv, console);
return 0;
}