#include "daemon.h" #include "shared.h" #include "pisysteminfo.h" #include "picrypt.h" const char self_name[] = "__self__"; extern PIScreen * screen; extern PISystemMonitor sys_mon; Daemon::Remote::Remote(const PIString & n): PIThread() { setName(n); term = 0; ft.setName(n); ft.setPacketSize(3984); ft.setCRCEnabled(false); CONNECTU(&ft, sendRequest, this, ftSendRequest) CONNECTU(&ft, receiveFilesFinished, this, ftReceived) CONNECTU(&ft, sendFilesFinished, this, ftSended) CONNECTU(&term_timer, tickEvent, this, termTimerTick) dir_my = PIDir::current(); } Daemon::Remote::~Remote() { shellClose(); ft.stop(); stopAndWait(); } void Daemon::Remote::shellOpen() { if (term) return; piCoutObj << "shell open"; term = new PITerminal(); term->initialize(); term_timer.start(50); } void Daemon::Remote::shellClose() { if (!term) return; piCoutObj << "shell close"; term_timer.stop(); term->destroy(); delete term; term = 0; //piCoutObj << "shellClose ok"; } void Daemon::Remote::shellResize(int w, int h) { if (!term) return; term->resize(w, h); } void Daemon::Remote::shellKeySend(PIKbdListener::KeyEvent k) { if (!term) return; term->write(k); } void Daemon::Remote::termTimerTick() { if (!term) return; PIVector > cells = term->content(); if (pcells == cells) return; pcells = cells; PIByteArray ba; ba << int(ShellContent) << cells; //piCout << ba.size_s() << pcells.back().back().symbol.unicode16Code() << cells.back().back().symbol.unicode16Code(); sendRequest(name(), ba); } void Daemon::Remote::startAction(Daemon::PacketType a, const PIString & dir, const PIStringList & fl) { _fl = fl; if (!dir.isEmpty()) piForeach (PIString & s, _fl) s.prepend(dir); //piCout << "send" << _fl; action = a; startOnce(); } void Daemon::Remote::run() { PIDir d(dir_my); switch (action) { case CopyFiles: ft.send(_fl); break; case RemoveFiles: ::removeFiles(d, _fl); updateDirEntries(); changeDirFinished(name(), d.absolutePath()); break; case RequestChangeDir: updateDirEntries(); changeDirFinished(name(), d.absolutePath()); break; case MkDir: if (!_fl.isEmpty()) PIDir::make(d.absolutePath() + PIDir::separator + _fl.at(0)); updateDirEntries(); changeDirFinished(name(), d.absolutePath()); break; case CryptFiles: piCoutObj << "CryptFiles"; if (!_fl.isEmpty()) { PIString p = askUserInput("Password:"); ::cryptFiles(d, _fl, PICrypt::hash(p)); } updateDirEntries(); changeDirFinished(name(), d.absolutePath()); break; default: break; }; } void Daemon::Remote::updateDirEntries() { //piCout << dir_my; if (!dir_my.isExists()) dir_my = PIDir::current(); my_filelist = dir_my.entries(); piForeach (PIFile::FileInfo & f, my_filelist) f.path = f.name(); } Daemon::TileFileProgress::TileFileProgress(): PIScreenTile() { size_policy = PIScreenTypes::Fixed; ft = 0; rec = false; setMargins(1, 1, 1, 1); spacing = 1; back_format.color_back = Yellow; label_file = new TileSimple(); label_speed = new TileSimple(); label_cnt = new TileSimple(); prog_file = new TileProgress(); prog_all = new TileProgress(); buttons = new TileButtons("fd_buttons"); buttons->content << TileButtons::Button("Pause", CellFormat()); buttons->content << TileButtons::Button("Cancel", CellFormat()); label_file->content.resize(1); label_speed->content.resize(1); label_cnt->content.resize(1); buttons->back_format.color_back = Yellow; label_file->back_format.color_back = label_speed->back_format.color_back = label_cnt->back_format.color_back = Yellow; label_file->content[0].second = label_speed->content[0].second = label_cnt->content[0].second = CellFormat(Black, Transparent); addTile(label_file); addTile(label_speed); addTile(label_cnt); addTile(prog_file); addTile(prog_all); addTile(buttons); CONNECTU((::screen), tileEvent, this, tileEvent) } void Daemon::TileFileProgress::resizeEvent(int w, int h) { PIScreenTile::resizeEvent(w, h); w = ::screen->windowWidth() / 1.4; minimumWidth = maximumWidth = w; } void Daemon::TileFileProgress::sizeHint(int & w, int & h) const { PIScreenTile::sizeHint(w, h); w = ::screen->windowWidth() / 1.4; } void Daemon::TileFileProgress::drawEvent(PIScreenDrawer * d) { if (ft) { label_file->content[0].first = ft->stateString() + " " + ft->curFile(); if (!label_file->content[0].first.isEmpty()) label_file->content[0].first[0] = label_file->content[0].first[0].toUpper(); PIString spd("Speed: "), cnt; if (ft->isReceiving()) spd = ft->diagnostic().receiveSpeed(); else spd = ft->diagnostic().sendSpeed().expandRightTo(10, ' '); spd += " ETA: "; if (!ft->isReceiving() && !ft->isSending()) { tm.reset(); spd += "-"; } else { double el = tm.elapsed_s(); int cb = ft->bytesCur(); if ((el <= 0.) || (cb <= 0)) spd += "-"; else { double s = (el / cb) * (ft->bytesAll() - cb); spd += readableTime(PITime::fromSystemTime(PISystemTime::fromSeconds(s))); } } spd += " Elapsed: " + readableTime(PITime::fromSystemTime(PISystemTime::fromSeconds(tme.elapsed_s()))); label_speed->content[0].first = spd; cnt = "File: " + PIString::readableSize(ft->bytesFileCur()).expandLeftTo(8, ' '); cnt += " / " + PIString::readableSize(ft->bytesFileAll()).expandLeftTo(8, ' '); cnt += " All: " + PIString::readableSize(ft->bytesCur()).expandLeftTo(8, ' '); cnt += " / " + PIString::readableSize(ft->bytesAll()).expandLeftTo(8, ' '); label_cnt->content[0].first = cnt; if (ft->bytesFileAll() > 0) prog_file->value = piRoundd(ft->bytesFileCur() / double(ft->bytesFileAll()) * 100.); if (ft->bytesAll() > 0) prog_all->value = piRoundd(ft->bytesCur() / double(ft->bytesAll()) * 100.); } PIScreenTile::drawEvent(d); } void Daemon::TileFileProgress::show(PIFileTransfer * f) { ft = f; if (ft) { conn_name = ft->name(); ::screen->setDialogTile(this); label_file->content[0].first = "Preparing ..."; prog_file->value = prog_all->value = 0; buttons->cur = 0; buttons->setFocus(); tm.reset(); tme.reset(); } } void Daemon::TileFileProgress::close(bool ok) { buttons->content[0].first = "Pause"; ft = 0; ::screen->setDialogTile(0); if (!ok) { piCout << "file transfer aborted"; showInfo("Error while file transfer, files may be invalid"); } } void Daemon::TileFileProgress::tileEvent(PIScreenTile * t, TileEvent e) { if (t->name() == "fd_buttons") { if (e.type != TileButtons::ButtonSelected || !ft) return; switch (e.data.toInt()) { case 0: if (buttons->content[0].first == "Pause") { buttons->content[0].first = "Resume"; ft->pause(); } else { buttons->content[0].first = "Pause"; ft->resume(); } break; case 1: ft->stop(); close(); break; }; } } Daemon::Daemon(): PIPeer(pisd_prefix + PISystemInfo::instance()->hostname + "_" + PIString::fromNumber(randomi() % 100)) { // setName("Daemon"); dtimer.setName("__S__Daemon_timer"); mode = rmNone; offset = cur = height = 0; CONNECTU(screen, keyPressed, this, keyEvent) CONNECTU(&dtimer, tickEvent, this, timerEvent) CONNECTU(&fm, tileKey, this, fmKeyEvent) CONNECTU(&fm, actionRequest, this, fmActionRequest) _self = new Remote(self_name); CONNECTU(_self, sendRequest, this, localSend) CONNECTU(_self, receiveFinished, this, closeFileDialog) CONNECTU(_self, sendFinished, this, closeFileDialog) CONNECTU(_self, changeDirFinished, this, dirChanged) localft.setName(self_name); localft.setCRCEnabled(false); CONNECTU(&localft, sendRequest, _self, received) dtimer.addDelimiter(5); dtimer.start(200); tile_root = new PIScreenTile(); tile_root->direction = Vertical; tile_header = new TileSimple("daemon header"); tile_header->size_policy = Fixed; list_daemons = new TileList("daemons list"); list_daemons->hide(); list_actions = new TileList("actions list"); list_actions->hide(); list_actions->content << TileList::Row("Information", CellFormat()); list_actions->content << TileList::Row("File manager", CellFormat()); list_actions->content << TileList::Row("Shell", CellFormat()); tile_info = new TileSimple("daemon info"); tile_info->hide(); fm.setRemote(); tile_fm = fm.tile(); tile_fm->hide(); tile_file_progress = new TileFileProgress(); tile_shell = new TileTerminal("shell"); tile_shell->hide(); CONNECTU(tile_shell, resizeRequest, this, shResizeRequest) CONNECTU(tile_shell, keyPressed, this, shKeyEvent) CONNECTU(tile_shell, closeRequest, this, requestCloseShell) CONNECTU(tile_shell, closeRequest, this, escPressed) tile_root->addTile(tile_header); tile_root->addTile(list_daemons); tile_root->addTile(list_actions); tile_root->addTile(tile_info); tile_root->addTile(tile_fm); tile_root->addTile(tile_shell); CONNECTU(screen, tileEvent, this, tileEvent) reinit(); inited__ = true; } Daemon::~Daemon() { requestCloseShell(); PIVector rl = remotes.values(); piForeach (Remote * r, rl) { r->shellClose(); delete r; } remotes.clear(); delete _self; } PIScreenTile * Daemon::tile() const { return tile_root; } void Daemon::escPressed() { if (mode == rmNone || mode == rmLocalFileManager) menuRequest(); else { if (mode > rmSelectMode) { mode = rmSelectMode; screen->lock(); tile_info->content.clear(); screen->unlock(); showActionList(); } else disconnect(); } } void Daemon::shResizeRequest() { Remote * r = remotes.value(conn_name, 0); if (!r) return; PIByteArray ba; ba << int(ShellResizeRequest) << int(tile_shell->width()) << int(tile_shell->height()); send(conn_name, ba); } void Daemon::shKeyEvent(PIKbdListener::KeyEvent k) { Remote * r = remotes.value(conn_name, 0); if (!r) return; PIByteArray ba; ba << int(ShellKeyEvent) << k; send(conn_name, ba); } void Daemon::localSend(const PIString & p_name, const PIByteArray & data) { //piCoutObj << "localSend" << localft.stateString(); PIByteArray h; h << int(FileTransfer); PIByteArray ba = data; ba.remove(0, h.size()); localft.received(ba); } void Daemon::hideAll() { list_actions->hide(); tile_info->hide(); list_daemons->hide(); tile_fm->hide(); tile_shell->hide(); } void Daemon::showTile(PIScreenTile * t, const PIString & header) { hideAll(); t->show(); t->setFocus(); tile_header->content.resize(1); tile_header->content[0].first = header; tile_header->content[0].second.flags = Bold; lock(); if (!conn_name.isEmpty()) tile_header->content[0].first.insert(0, PIString("Daemon \"") + connectedDaemon() + "\": "); unlock(); } void Daemon::fillInfoTile(const Daemon::HostInfo & hi) { screen->lock(); tile_info->content.clear(); tile_info->content << TileSimple::Row("Exec command: " + hi.execCommand, CellFormat()); tile_info->content << TileSimple::Row(" Executed on " + hi.execDateTime.toString(), CellFormat()); tile_info->content << TileSimple::Row(" Hostname: " + hi.hostname, CellFormat()); tile_info->content << TileSimple::Row(" Username: " + hi.user, CellFormat()); tile_info->content << TileSimple::Row(" OS name: " + hi.OS_name, CellFormat()); tile_info->content << TileSimple::Row(" OS version: " + hi.OS_version, CellFormat()); tile_info->content << TileSimple::Row("Architecture: " + hi.architecture, CellFormat()); tile_info->content << TileSimple::Row(" CPU count: " + PIString::fromNumber(hi.processorsCount), CellFormat()); screen->unlock(); } void Daemon::tileEvent(PIScreenTile * t, TileEvent e) { if (t == list_daemons) { if (e.type == TileList::RowPressed) { PIMutexLocker ml(remote_mutex); connectToDaemon(list_daemons->content[e.data.toInt()].first); showActionList(); } return; } if (t == list_actions) { if (e.type == TileList::RowPressed) { PIMutexLocker ml(remote_mutex); switch (e.data.toInt()) { case 0: mode = rmInformation; showTile(tile_info, "Information"); break; case 1: mode = rmFileManager; showTile(tile_fm, "File manager"); requestChDir("."); break; case 2: mode = rmShell; showTile(tile_shell, "Shell"); requestOpenShell(); break; default: break; } } return; } } void Daemon::keyEvent(PIKbdListener::KeyEvent key) { if (!tile_root->visible) return; if (screen->dialogTile()) return; switch (key.key) { case PIKbdListener::Esc: escPressed(); break; default: break; } } void Daemon::fmKeyEvent(PIKbdListener::KeyEvent key) { PIMutexLocker ml(remote_mutex); // piCoutObj << key.key << key.modifiers; switch (key.key) { case PIKbdListener::Return: { PIFile::FileInfo fi = fm.currentRemoteEntry(); if (!fi.isDir()) break; requestChDir(fi.name()); } break; case 'R': requestChDir("."); break; default: break; } } void Daemon::fmActionRequest(bool remote_tile, FileManager::Action type, PIVariant data) { Remote * r = 0; r = remotes.value(conn_name, 0); if (!r && type != FileManager::LocalCopy && type != FileManager::LocalCrypt) return; switch (type) { case FileManager::Copy: if (remote_tile) { PIByteArray ba; //piCout << fm.selectedRemote(); ba << int(CopyFiles) << fm.selectedRemote(); r->ft.setDirectory(fm.localDir()); send(conn_name, ba); tile_file_progress->rec = true; tile_file_progress->show(&(r->ft)); } else { r->sendFiles(fm.localDir() + PIDir::separator, data.toStringList()); tile_file_progress->rec = false; tile_file_progress->show(&(r->ft)); } break; case FileManager::Remove: if (remote_tile) { PIByteArray ba; ba << int(RemoveFiles) << fm.selectedRemote(); send(conn_name, ba); } break; case FileManager::MkDir: if (remote_tile) { PIByteArray ba; ba << int(MkDir) << data.toString(); send(conn_name, ba); } break; case FileManager::LocalCopy: //piCoutObj << "localCopy"; if (remote_tile) { localft.setDirectory(fm.localDir()); _self->sendFiles(fm.remoteDir() + PIDir::separator, data.toStringList()); } else { localft.setDirectory(fm.remoteDir()); _self->sendFiles(fm.localDir() + PIDir::separator, data.toStringList()); } tile_file_progress->rec = false; tile_file_progress->show(&(_self->ft)); case FileManager::Crypt: if (remote_tile) { PIByteArray ba; ba << int(CryptFiles) << fm.selectedRemote(); send(conn_name, ba); } else { _self->dir_my.setDir(fm.localDir()); _self->cryptFiles(data.toStringList()); } break; case FileManager::LocalCrypt: piCoutObj << "LocalCrypt"; if (remote_tile) { _self->dir_my.setDir(fm.remoteDir()); } else { _self->dir_my.setDir(fm.localDir()); } _self->cryptFiles(data.toStringList()); break; default: break; }; //piCout << remote_tile << type << data; } void Daemon::timerEvent(void * _d, int delim) { screen->lock(); list_daemons->content.clear(); availableDaemons(); piForeachC (PIString & i, available_daemons) list_daemons->content << TileList::Row(i, CellFormat()); screen->unlock(); if (delim == 5 && mode == rmInformation) { if (conn_name.isEmpty()) return; PIByteArray ba; ba << int(RequestHostInfo); send(conn_name, ba); //std::cout << "send " << std::hex << ba; } } PIStringList Daemon::availableDaemons() { available_daemons.clear(); lock(); piForeachC (PIPeer::PeerInfo & p, allPeers()) { if (!p.name.startsWith(pisd_prefix)) continue; available_daemons << p.name.mid(6); } unlock(); return available_daemons; } void Daemon::connectToDaemon(const PIString & dn) { if (dn.isEmpty()) return; conn_name = pisd_prefix + dn; mode = rmSelectMode; } void Daemon::disconnect() { screen->enableExitCapture(PIKbdListener::F10); conn_name.clear(); mode = rmNone; showMainList(); } PIString Daemon::connectedDaemon() const { return conn_name.mid(6); } void Daemon::peerConnected(const PIString & p_name) { while (!inited__) piMSleep(PIP_MIN_MSLEEP*5); if (!p_name.startsWith(pisd_prefix)) return; Remote * r = new Remote(p_name); piCoutObj << "peer connected" << p_name; CONNECTU(r, sendRequest, this, sendRequest) CONNECTU(r, receiveFinished, this, filesReceived) CONNECTU(r, sendFinished, this, filesSended) CONNECTU(r, changeDirFinished, this, dirChanged) PIMutexLocker ml2(remote_mutex); remotes.insert(p_name, r); } void Daemon::peerDisconnected(const PIString & p_name) { while (!inited__) piMSleep(PIP_MIN_MSLEEP*5); piCoutObj << "peer disconnect" << p_name; if (p_name == conn_name) { disconnect(); } PIMutexLocker ml(remote_mutex); Remote * dt = remotes.value(p_name, 0); if (!dt) return; dt->shellClose(); if (tile_file_progress->ft == &(dt->ft)) { tile_file_progress->close(false); } remotes.remove(p_name); delete dt; } void Daemon::filesReceived(const PIString & p_name, bool ok) { if (ok) { piCout << "files received from" << p_name; } else { piCout << "warning, files not received fromsended" << p_name; } closeFileDialog(p_name, ok); Remote * r = remotes.value(p_name, 0); if (!r) return; r->updateDir(); } void Daemon::filesSended(const PIString & p_name, bool ok) { if (ok) { piCout << "files sended to" << p_name; } else { piCout << "warning, files not sended to" << p_name; } closeFileDialog(p_name, ok); } void Daemon::dirChanged(const PIString & p_name, const PIString & dir) { if (p_name == self_name) { fm.clearSelectionLocal(); fm.clearSelectionRemote(); fm.updateLocalDir(); fm.updateRemoteDir(); return; } PIMutexLocker ml(remote_mutex); Remote * r = remotes.value(p_name, 0); if (!r) return; if (r->dir_my.absolutePath() != dir) return; sendDirToRemote(r); } void Daemon::closeFileDialog(const PIString & p_name, bool ok) { //piCout << "CLOSE" << tile_file_progress->conn_name << name << ok; if (p_name == self_name) { tile_file_progress->close(ok); fm.clearSelectionLocal(); fm.clearSelectionRemote(); fm.updateLocalDir(); fm.updateRemoteDir(); return; } //piCout << "file transfer with" << p_name << (ok ? "success" : "failure"); if (tile_file_progress->conn_name != p_name) return; tile_file_progress->close(ok); if (tile_file_progress->rec) { fm.remoteTile()->setFocus(); fm.clearSelectionRemote(); fm.updateLocalDir(); } else { fm.localTile()->setFocus(); fm.clearSelectionLocal(); requestChDir("."); } } void Daemon::dataReceived(const PIString & from, const PIByteArray & data) { // piCout << "rec" << data.size(); if (data.size() < 4) return; PIMutexLocker ml(remote_mutex); // piCout << "lock in dataReceived"; PIByteArray ba(data), rba; Remote * r = remotes.value(from); PIString dir; int type; ba >> type; //piCout << "rec from " << from << type << r; switch (type) { case RequestHostInfo: makeMyHostInfo(); rba << int(ReplyHostInfo) << info_my; break; case RequestChangeDir: if (!r) break; ba >> dir; r->dir_my.cd(dir); r->ft.setDirectory(r->dir_my); //piCout << "store to" << r->dir_my.absolutePath(); piCoutObj << "cd to" << r->dir_my.absolutePath(); r->updateDir(); break; case ReplyHostInfo: ba >> info_other; makeOtherHostInfo(); fillInfoTile(info_other); break; case ReplyChangeDir: if (!r) break; { PIVector fil; ba >> dir >> fil; fm.setRemoteDir(dir); fm.setRemoteContent(fil); fm.remoteRestoreDir(); } break; case CopyFiles: if (!r) return; if (r->isRunning()) return; { PIStringList files; ba >> files; //piCout << "send" << files << "from" << r->dir_my.absolutePath(); r->sendFiles(r->dir_my.absolutePath() + PIDir::separator, files); } break; case RemoveFiles: if (!r) return; if (r->isRunning()) return; { PIStringList files; ba >> files; //piCout << "send" << files << "from" << r->dir_my.absolutePath(); r->removeFiles(files); } break; case MkDir: if (!r) return; { PIString dn; ba >> dn; //piCout << "send" << files << "from" << r->dir_my.absolutePath(); r->makeDir(dn); } break; case FileTransfer: if (r) r->received(ba); break; case CryptFiles: if (!r) return; if (r->isRunning()) return; { PIStringList files; ba >> files; r->cryptFiles(files); } break; case ShellOpen: if (!r) return; r->shellOpen(); break; case ShellClose: if (!r) return; r->shellClose(); break; case ShellContent: { if (!r) return; PIVector > cells; ba >> cells; tile_shell->setContent(cells); } break; case ShellResizeRequest: { if (!r) return; int w, h; ba >> w >> h; r->shellResize(w, h); } break; case ShellKeyEvent: { if (!r) return; PIKbdListener::KeyEvent k; ba >> k; r->shellKeySend(k); } break; }; if (!rba.isEmpty()) send(from, rba); } void Daemon::sendDirToRemote(Remote * r) { if (!r) return; PIVector fil = r->my_filelist; PIByteArray ba; ba << int(ReplyChangeDir) << r->dir_my.absolutePath() << fil; send(r->name(), ba); } void Daemon::requestOpenShell() { Remote * r = remotes.value(conn_name, 0); if (!r) return; screen->disableExitCapture(); PIByteArray ba; ba << int(ShellOpen); send(conn_name, ba); executeQueued(this, "shResizeRequest"); } void Daemon::requestCloseShell() { Remote * r = remotes.value(conn_name, 0); if (!r) return; screen->enableExitCapture(PIKbdListener::F10); PIByteArray ba; ba << int(ShellClose); send(conn_name, ba); } void Daemon::makeMyHostInfo() { info_my.execCommand = PISystemInfo::instance()->execCommand; info_my.hostname = PISystemInfo::instance()->hostname; info_my.user = PISystemInfo::instance()->user; info_my.OS_name = PISystemInfo::instance()->OS_name; info_my.OS_version = PISystemInfo::instance()->OS_version; info_my.architecture = PISystemInfo::instance()->architecture; info_my.execDateTime = PISystemInfo::instance()->execDateTime; info_my.processorsCount = PISystemInfo::instance()->processorsCount; info_my.ID = sys_mon.statistic().ID; info_my.threads = sys_mon.statistic().threads; info_my.priority = sys_mon.statistic().priority; info_my.physical_memsize = sys_mon.statistic().physical_memsize; info_my.share_memsize = sys_mon.statistic().share_memsize; info_my.cpu_load_system = sys_mon.statistic().cpu_load_system; info_my.cpu_load_user = sys_mon.statistic().cpu_load_user; } void Daemon::makeOtherHostInfo() { PISystemMonitor::ProcessStats ps(sys_mon_other.statistic()); ps.ID = info_other.ID; ps.threads = info_other.threads; ps.priority = info_other.priority; ps.physical_memsize = info_other.physical_memsize; ps.share_memsize = info_other.share_memsize; ps.cpu_load_system = info_other.cpu_load_system; ps.cpu_load_user = info_other.cpu_load_user; sys_mon_other.setStatistic(ps); } void Daemon::requestChDir(const PIString & d) { if (d.isEmpty()) return; Remote * r = remotes.value(conn_name, 0); if (!r) return; fm.remoteSaveDir(); fm.readingRemote(); PIByteArray ba; ba << int(RequestChangeDir) << d; send(conn_name, ba); }