/* PIP - Platform Independent Primitives PIP System Daemon Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "daemon.h" #include "file_manager.h" #include "picli.h" #include "piintrospection_server.h" #include "piliterals_time.h" #include "piprocess.h" #include "pisingleapplication.h" #include "pisysteminfo.h" #include "pisystemmonitor.h" #include "shared.h" STATIC_INITIALIZER_BEGIN randomize(); STATIC_INITIALIZER_END PISystemMonitor sys_mon; PIScreen * screen = nullptr; class MainMenu: public PITimer { PIOBJECT_SUBCLASS(MainMenu, PITimer) public: MainMenu(Daemon & d): daemon_(d) { cur_peer = -1; title = new TileSimple("title"); updateTitle(title); title->back_format.color_back = Yellow; title->size_policy = Fixed; screen->rootTile()->addTile(title); PIScreenTile * center = new PIScreenTile("center"); center->back_format.color_back = Cyan; center->size_policy = Expanding; center->setMargins(2, 2, 1, 1); screen->rootTile()->addTile(center); PIScreenTile * mt = tmenu = menuTile(); mt->show(); mt->name() = "main menu"; center->addTile(mt); mtiles << mt; mt = tinfo = infoTile(); mt->hide(); mt->name() = "local info"; center->addTile(mt); mtiles << mt; mt = tdaemon = daemon_.tile(); mt->hide(); mt->name() = "daemon"; center->addTile(mt); mtiles << mt; mt = tpeer = peerTile(); mt->hide(); mt->name() = "peer info"; center->addTile(mt); mtiles << mt; mt = tpeerdiag = peerDiagTile(); mt->hide(); mt->name() = "peer diag"; center->addTile(mt); mtiles << mt; tpicout = new TilePICout(); tpicout->hide(); tpicout->size_policy = PIScreenTypes::Expanding; screen->rootTile()->addTile(tpicout); CONNECTU(screen, tileEvent, this, tileEvent) CONNECTU(screen, keyPressed, this, keyEvent) CONNECTU(&daemon_, menuRequest, this, menuRequest) start(40_Hz); } PIScreenTile * menuTile() { TileList * ret = new TileList(); ret->content << TileList::Row("Show local info", CellFormat()); ret->content << TileList::Row("Local file manager", CellFormat()); ret->content << TileList::Row("Connect to another daemon", CellFormat()); ret->content << TileList::Row("Peer info", CellFormat()); ret->content << TileList::Row("Peer reinit", CellFormat()); ret->content << TileList::Row("Peer diagnostics", CellFormat()); ret->content << TileList::Row("Peer change self name", CellFormat()); ret->content << TileList::Row("Exit", CellFormat()); ret->selection_mode = TileList::NoSelection; return ret; } PIScreenTile * infoTile() { TileList * ret = new TileList(); local_info_base << TileList::Row("Exec command: " + PISystemInfo::instance()->execCommand, CellFormat()); local_info_base << TileList::Row(" Executed on " + PISystemInfo::instance()->execDateTime.toString(), CellFormat()); local_info_base << TileList::Row(" Hostname: " + PISystemInfo::instance()->hostname, CellFormat()); local_info_base << TileList::Row(" Username: " + PISystemInfo::instance()->user, CellFormat()); local_info_base << TileList::Row(" OS name: " + PISystemInfo::instance()->OS_name, CellFormat()); local_info_base << TileList::Row(" OS version: " + PISystemInfo::instance()->OS_version, CellFormat()); local_info_base << TileList::Row("Architecture: " + PISystemInfo::instance()->architecture, CellFormat()); local_info_base << TileList::Row(" CPU count: " + PIString::fromNumber(PISystemInfo::instance()->processorsCount), CellFormat()); local_info_base << TileList::Row("", CellFormat()); return ret; } PIScreenTile * peerDiagTile() { PIScreenTile * ret = new PIScreenTile(); TileSimple * htl = new TileSimple(); htl->size_policy = PIScreenTypes::Fixed; ret->direction = PIScreenTypes::Vertical; htl->content << TileSimple::Row("Peer: " + daemon_.name() + " | " + daemon_.selfInfo().name, CellFormat(PIScreenTypes::Default, PIScreenTypes::Default, PIScreenTypes::Bold)); PIScreenTile * diag = new PIScreenTile(); diag->direction = PIScreenTypes::Horizontal; peerdiagdata_tl = new TileSimple(); peerdiagservice_tl = new TileSimple(); ret->addTile(htl); ret->addTile(diag); diag->addTile(peerdiagdata_tl); diag->addTile(peerdiagservice_tl); return ret; } PIScreenTile * peerTile() { PIScreenTile * ret = new PIScreenTile(); ret->direction = PIScreenTypes::Vertical; peerinfo_header = new TileSimple(); peerinfo_header->size_policy = PIScreenTypes::Fixed; peerinfo_header->content << TileSimple::Row("Peer: " + daemon_.name() + " | " + daemon_.selfInfo().name, CellFormat(PIScreenTypes::Default, PIScreenTypes::Default, PIScreenTypes::Bold)); addrs_tl = new TileList(); peers_tl = new TileList(); peerinfo_tl = new TileSimple(); peermap_tl = new TileList(); peerinfo_tl->size_policy = PIScreenTypes::Fixed; ret->addTile(peerinfo_header); ret->addTile(peers_tl); ret->addTile(peerinfo_tl); ret->addTile(addrs_tl); ret->addTile(peermap_tl); return ret; } void updateTitle(TileSimple * tl) { tl->content.clear(); tl->content << TileSimple::Row("pisd (PI System Daemon, PIP version " + PIPVersion() + ")", CellFormat(Black, Transparent)); tl->content << TileSimple::Row("This daemon: \"" + daemon_.thisDaemonName() + "\"", CellFormat(Black, Transparent)); } void updatePeerDiag(TileSimple * tl, const PIDiagnostics & diag) { tl->content.clear(); PIDiagnostics::State ds = diag.state(); tl->content << TileSimple::Row(diag.name() + " diagnostics", CellFormat(PIScreenTypes::Default, PIScreenTypes::Default, PIScreenTypes::Bold)); tl->content << TileSimple::Row("Received count: " + PIString::fromNumber(ds.received_packets), CellFormat()); tl->content << TileSimple::Row("Invalid count: " + PIString::fromNumber(ds.received_packets_wrong), CellFormat()); tl->content << TileSimple::Row("Sended count: " + PIString::fromNumber(ds.sended_packets), CellFormat()); tl->content << TileSimple::Row("Immediate Frequency, Hz: " + PIString::fromNumber(ds.immediate_freq), CellFormat()); tl->content << TileSimple::Row("Integral Frequency, Hz: " + PIString::fromNumber(ds.integral_freq), CellFormat()); tl->content << TileSimple::Row("Receive speed: " + ds.receive_speed, CellFormat()); tl->content << TileSimple::Row("Send speed: " + ds.send_speed, CellFormat()); tl->content << TileSimple::Row("Quality: " + PIString::fromNumber((int)ds.quality), CellFormat()); } void updatePeerInfo() { // bool pm = daemon_.lockedPeers(); screen->lock(); daemon_.lock(); peers_tl->content.clear(); addrs_tl->content.clear(); peerinfo_tl->content.clear(); peermap_tl->content.clear(); peers_tl->content << TileList::Row( "this | 0 | 0 | " + PIString::fromNumber(daemon_.allPeers().size_s()) // + " [em = " + PIString::fromBool(daemon_.lockedEth()) + ", //" "mm = " + PIString::fromBool(daemon_.lockedMBcasts()) + ", " //"sm //= //" //+ PIString::fromBool(daemon_.lockedSends()) + ", " "ms = " + // PIString::fromBool(daemon_.lockedMCSends()) + ", " "pm = " + PIString::fromBool(pm) // + // "]" , CellFormat()); for (const auto & p: daemon_.allPeers()) peers_tl->content << TileList::Row(p.name + " | d = " + PIString::fromNumber(p.dist) + " | p = " + PIString::fromNumber(p.ping()) + " | n = " + PIString::fromBool(p.isNeighbour()), CellFormat()); PIPeer::PeerInfo pi = daemon_.selfInfo(); if (cur_peer >= 0 && cur_peer < daemon_.allPeers().size_s()) pi = daemon_.allPeers()[cur_peer]; peerinfo_tl->content << TileSimple::Row("Name: " + pi.name, CellFormat()); peerinfo_tl->content << TileSimple::Row("Addreses: " + PIString::fromNumber(pi.addresses.size()), CellFormat()); peerinfo_tl->content << TileSimple::Row("Neighbours: " + pi.neighbours.join(", "), CellFormat()); for (const auto & a: pi.addresses) addrs_tl->content << TileList::Row(a.address.toString() + " | p = " + PIString::fromNumber(a.ping) + " | a = " + PIString::fromBool(a.isAvailable()), CellFormat()); PIStringList peermap; for (auto p = daemon_._peerMap().begin(); p != daemon_._peerMap().end(); p++) { PIString s = p.key() + " | "; piForeachCR(PIPeer::PeerInfo * pp, p.value()) s += " -> " + pp->name; peermap << s; } for (const auto & s: peermap) peermap_tl->content << TileList::Row(s, CellFormat()); updatePeerDiag(peerdiagdata_tl, daemon_.diagnosticData()); updatePeerDiag(peerdiagservice_tl, daemon_.diagnosticService()); daemon_.unlock(); screen->unlock(); } void updateSysMon() { TileList * tile = (TileList *)tinfo; PIVector ts = sys_mon.threadsStatistic(); screen->lock(); tile->content = local_info_base; int num = 0, maxlen = 0; PIString line = "Process load: k "; PIString ns = PIString::fromNumber(sys_mon.statistic().cpu_load_system, 'f', 2); line += ns.expandLeftTo(5, ' ') + " %, u "; ns = PIString::fromNumber(sys_mon.statistic().cpu_load_user, 'f', 2); line += ns.expandLeftTo(5, ' ') + " %"; tile->content << TileList::Row("PID: " + PIString::fromNumber(sys_mon.statistic().ID), CellFormat()); tile->content << TileList::Row(line, CellFormat()); tile->content << TileList::Row("Threads:", CellFormat()); for (const auto & t: ts) maxlen = piMaxi(maxlen, t.name.length()); for (const auto & t: ts) { line = PIString::fromNumber(++num).expandLeftTo(2, ' ') + ": "; line += PIString(t.name).expandRightTo(maxlen, ' ') + ": k "; PIString ns = PIString::fromNumber(t.cpu_load_kernel, 'f', 2); line += ns.expandLeftTo(5, ' ') + " %, u "; ns = PIString::fromNumber(t.cpu_load_user, 'f', 2); line += ns.expandLeftTo(5, ' ') + " %"; tile->content << TileList::Row(line, CellFormat()); } screen->unlock(); } void tick(int delimiter) override { if (tpeerdiag->visible || tpeer->visible) updatePeerInfo(); if (tinfo->visible) updateSysMon(); } EVENT_HANDLER(void, menuRequest) { for (auto * t: mtiles) t->hide(); daemon_.disconnect(); tmenu->show(); tmenu->setFocus(); } EVENT_HANDLER2(void, tileEvent, PIScreenTile *, t, PIScreenTypes::TileEvent, e) { if (t == tmenu) { if (e.type == TileList::RowPressed) { for (auto * t: mtiles) t->hide(); switch (e.data.toInt()) { case 0: tinfo->show(); break; case 1: daemon_.fm.setLocal(); daemon_.showLocalFilemanager(); tdaemon->show(); break; case 2: daemon_.fm.setRemote(); daemon_.showMainList(); tdaemon->show(); break; case 3: tpeer->show(); peers_tl->setFocus(); break; case 4: daemon_.reinit(); tmenu->show(); break; case 5: tpeerdiag->show(); break; case 6: { PIString nn = askUserInput("Peer name:"); if (!nn.isEmpty()) { daemon_.changeName(pisd_prefix + nn); peerinfo_header->content.clear(); peerinfo_header->content << TileSimple::Row("Peer: " + daemon_.name() + " | " + daemon_.selfInfo().name, CellFormat(PIScreenTypes::Default, PIScreenTypes::Default, PIScreenTypes::Bold)); updateTitle(title); } menuRequest(); } break; case 7: PIKbdListener::exiting = true; break; } } return; } if (t == peers_tl) { if (e.type == TileList::RowPressed) { cur_peer = e.data.toInt() - 1; updatePeerInfo(); } return; } } EVENT_HANDLER1(void, keyEvent, PIKbdListener::KeyEvent, e) { if (e.key == PIKbdListener::F9) { tpicout->visible = !tpicout->visible; return; } if (e.key == PIKbdListener::Esc && e.modifiers[PIKbdListener::Shift]) { PIKbdListener::exiting = true; return; } if (screen->dialogTile()) return; if (tpeer->visible || tinfo->visible || tpeerdiag->visible) if (e.key == PIKbdListener::Esc) menuRequest(); // piCout << "key" << e.key; } EVENT_HANDLER1(void, messageFromApp, PIByteArray, m) { if (m[0] == 'k') PIKbdListener::exiting = true; } Daemon & daemon_; PIScreenTile *tmenu, *tinfo, *tfm, *tdaemon, *tpeer, *tpeerdiag; TileList *peers_tl, *addrs_tl, *peermap_tl; TilePICout * tpicout; TileSimple * title; TileSimple *peerinfo_tl, *peerinfo_header; TileSimple *peerdiagdata_tl, *peerdiagservice_tl; PIVector mtiles; PIDeque local_info_base; int cur_peer; }; void usage() { piCout << PICoutManipulators::Bold << "PIP System Daemon"; piCout << PICoutManipulators::Cyan << "Version" << PICoutManipulators::Bold << PIPVersion() << PICoutManipulators::NewLine; piCout << PICoutManipulators::Green << PICoutManipulators::Bold << "Usage:" << PICoutManipulators::Default << "\"pisd [-1hdfk] [-n ] [-a ]\"" << PICoutManipulators::NewLine; piCout << PICoutManipulators::Green << PICoutManipulators::Bold << "Details:"; piCout << "-h --help " << PICoutManipulators::Green << "- display this message and exit"; piCout << "-d --daemon " << PICoutManipulators::Green << "- start as daemon"; piCout << "-k --kill " << PICoutManipulators::Green << "- kill daemon"; piCout << "-f --force " << PICoutManipulators::Green << "- don`t check for another running instance"; piCout << "-n --name " << PICoutManipulators::Green << "- set daemon name"; piCout << "-a --address " << PICoutManipulators::Green << "- connect to remote daemon via tcp"; piCout << "-s --silent " << PICoutManipulators::Green << "- run without user interfase"; } int main(int argc, char * argv[]) { // piDebug = false; PICLI cli(argc, argv); cli.addArgument("help"); cli.addArgument("daemon"); cli.addArgument("force"); cli.addArgument("kill"); cli.addArgument("1"); cli.addArgument("silent"); cli.addArgument("name", true); cli.addArgument("address", true); if (cli.hasArgument("help")) { usage(); return 0; } sys_mon.startOnSelf(); PIString name = cli.argumentValue("name"); PIString sip = cli.argumentValue("address"); PISingleApplication * sapp = 0; if ((cli.hasArgument("1") && !cli.hasArgument("force")) || cli.hasArgument("kill")) { sapp = new PISingleApplication("pisd"); if (cli.hasArgument("1")) { if (!sapp->isFirst()) { piCout << "Another pisd is running, exit"; delete sapp; return 0; } } if (cli.hasArgument("kill")) { sapp->sendMessage(PIByteArray("k", 1)); delete sapp; return 0; } } PIINTROSPECTION_START(pisd) if (cli.hasArgument("daemon")) { PIStringList args; args << "-1" << "-s"; if (cli.hasArgument("force")) args << "-f"; if (cli.hasArgument("address")) args << "-a" << sip; if (!name.isEmpty()) args << "-n" << name; PIString exe; #ifdef WINDOWS exe = PISystemInfo::instance()->execCommand; #else exe = PIProcess::getEnvironmentVariable("_"); #endif piCout << "start in background:" << exe; // << "; with args" << args; PIProcess::execIndependent(exe, args); return 0; } screen = new PIScreen(false); screen->setMouseEnabled(true); Daemon * daemon = new Daemon(); if (!sip.isEmpty()) daemon->setTcpServerIP(sip); screen->enableExitCapture(PIKbdListener::F10); if (!name.isEmpty()) daemon->changeName(pisd_prefix + name); MainMenu * menu = new MainMenu(*daemon); if (sapp) CONNECTU(sapp, messageReceived, menu, messageFromApp); if (cli.hasArgument("silent")) { PICout::setOutputDevices(PICout::Console); PIKbdListener ls(0, 0, false); ls.enableExitCapture(PIKbdListener::F10); ls.start(); while (!PIKbdListener::exiting) piMSleep(PIP_MIN_MSLEEP * 5); if (!ls.stopAndWait(1_s)) ls.terminate(); } else { screen->start(); screen->waitForFinish(); screen->stop(true); } sys_mon.stop(); delete menu; delete daemon; delete screen; if (sapp) delete sapp; return 0; }