#include "surec_kopru.hpp" #include #include #include #include #include #include "fiko/cekirdek/olay.hpp" // OlayGunlugu/OlayKaydi (yalnız .cpp — header nötr kalır) namespace fiko { using namespace surecproto; namespace { // ---- Ayarlar ---- constexpr uint64_t HEARTBEAT_ARALIK_MS = 1000; // kendi kaydını tazeleme periyodu constexpr uint64_t FORWARD_ARALIK_MS = 250; // olay forward periyodu (throttle) constexpr uint64_t ABONE_YAYIN_ARALIK_MS = 2000; // OlayAbone yeniden-yayın periyodu constexpr uint64_t ABONE_TTL_MS = 6000; // abone bayatlarsa forward'ı durdur constexpr size_t FORWARD_BATCH_MAX = 64; // çerçeve başına en çok olay constexpr uint64_t YANITSIZ_ESIK_S = 5; // heartbeat > bu → Yanitsiz std::wstring pipeAdi(uint32_t pid) { return L"\\\\.\\pipe\\fikocad_" + std::to_wstring(pid); } std::wstring kayitYolu() { wchar_t temp[MAX_PATH]{}; GetTempPathW(MAX_PATH, temp); return std::wstring(temp) + L"fikocad_surecler.txt"; } uint64_t simdiUnix() { return (uint64_t)std::time(nullptr); } uint64_t simdiMs() { return GetTickCount64(); } uint32_t kendiBellekKB() { PROCESS_MEMORY_COUNTERS pmc{}; if (K32GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) return (uint32_t)(pmc.WorkingSetSize / 1024); return 0; } // Kayıt dosyası çok-süreç erişimi için isimli mutex (basit koordinasyon). struct KayitKilit { HANDLE m; KayitKilit() : m(CreateMutexW(nullptr, FALSE, L"fikocad_surec_kilit")) { if (m) WaitForSingleObject(m, 2000); } ~KayitKilit() { if (m) { ReleaseMutex(m); CloseHandle(m); } } }; bool pidCanli(uint32_t pid) { HANDLE h = OpenProcess(SYNCHRONIZE, FALSE, pid); if (!h) return false; const bool canli = WaitForSingleObject(h, 0) == WAIT_TIMEOUT; CloseHandle(h); return canli; } // Hedefin fikocad gelen-kutusu pipe'ı var mı (fikocad imzası — kill guard). bool pipeVar(uint32_t pid) { const std::wstring pn = pipeAdi(pid); if (WaitNamedPipeW(pn.c_str(), 200)) return true; // instance hazır return GetLastError() == ERROR_SEM_TIMEOUT; // var ama meşgul → yine fikocad } std::string kayitOku() { HANDLE f = CreateFileW(kayitYolu().c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (f == INVALID_HANDLE_VALUE) return {}; std::string s; char tampon[4096]; DWORD okundu = 0; while (ReadFile(f, tampon, sizeof(tampon), &okundu, nullptr) && okundu > 0) s.append(tampon, okundu); CloseHandle(f); return s; } void kayitYaz(const std::string& icerik) { HANDLE f = CreateFileW(kayitYolu().c_str(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (f == INVALID_HANDLE_VALUE) return; DWORD yazildi = 0; WriteFile(f, icerik.data(), (DWORD)icerik.size(), &yazildi, nullptr); CloseHandle(f); } // \t ve \n'i boşluğa çevir (kayıt satırı alan-ayracını korur). std::string temizAlan(const std::string& s) { std::string r = s; for (char& c : r) if (c == '\t' || c == '\n' || c == '\r') c = ' '; return r; } // Tek SurecBilgi → kayıt satırı (yeni şema, 6 alan). std::string satirYaz(const SurecBilgi& s) { return std::to_string(s.pid) + "\t" + temizAlan(s.belge) + "\t" + std::to_string(s.baslatUnix) + "\t" + std::to_string((uint32_t)s.durum) + "\t" + std::to_string(s.sonHeartbeatUnix) + "\t" + std::to_string(s.bellekKB) + "\n"; } // Bir satırı ayrıştır (yeni 6-alan VEYA eski 2-alan). false = geçersiz/boş. bool satirCoz(const std::string& satir, SurecBilgi& out) { if (satir.empty()) return false; std::vector alan; size_t i = 0; while (i <= satir.size()) { size_t t = satir.find('\t', i); if (t == std::string::npos) { alan.push_back(satir.substr(i)); break; } alan.push_back(satir.substr(i, t - i)); i = t + 1; } if (alan.empty()) return false; out.pid = (uint32_t)std::strtoul(alan[0].c_str(), nullptr, 10); if (!out.pid) return false; out.belge = alan.size() > 1 && !alan[1].empty() ? alan[1] : "Adsız"; out.baslatUnix = alan.size() > 2 ? std::strtoull(alan[2].c_str(), nullptr, 10) : 0; out.durum = alan.size() > 3 ? (SurecDurum)std::strtoul(alan[3].c_str(), nullptr, 10) : SurecDurum::Aktif; out.sonHeartbeatUnix = alan.size() > 4 ? std::strtoull(alan[4].c_str(), nullptr, 10) : 0; out.bellekKB = alan.size() > 5 ? (uint32_t)std::strtoul(alan[5].c_str(), nullptr, 10) : 0; return true; } // Kayıt metnini ayrıştır, ölü PID'leri süz, yanıtsızlığı türet. std::vector ayristirVeSuz(const std::string& metin) { std::vector cikti; const uint64_t simdi = simdiUnix(); size_t i = 0; while (i < metin.size()) { size_t satirSonu = metin.find('\n', i); if (satirSonu == std::string::npos) satirSonu = metin.size(); SurecBilgi s; if (satirCoz(metin.substr(i, satirSonu - i), s) && pidCanli(s.pid)) { if (s.durum != SurecDurum::Yanitsiz && s.sonHeartbeatUnix && simdi > s.sonHeartbeatUnix + YANITSIZ_ESIK_S) s.durum = SurecDurum::Yanitsiz; // canlı ama heartbeat bayat (zombi UI) cikti.push_back(std::move(s)); } i = satirSonu + 1; } return cikti; } // Pipe'tan TAM bir mesaj oku (message-mode; ERROR_MORE_DATA'da parça biriktir). std::string mesajOku(HANDLE pipe) { std::string msg; char buf[4096]; for (;;) { DWORD n = 0; const BOOL ok = ReadFile(pipe, buf, sizeof(buf), &n, nullptr); if (n) msg.append(buf, n); if (ok) break; if (GetLastError() == ERROR_MORE_DATA) continue; break; } return msg; } // Olay batch serialize/deserialize (.cpp — ForwardOlay POD). std::string olayBatchSerialize(const std::vector& v) { std::string s; yazU16(s, (uint16_t)(v.size() > 0xFFFF ? 0xFFFF : v.size())); for (const ForwardOlay& fo : v) { yazU64(s, fo.sira); uint64_t zbits; std::memcpy(&zbits, &fo.zaman, 8); yazU64(s, zbits); yazU16(s, fo.tur); yazU32(s, fo.varlik); s.append(fo.ozet, sizeof(fo.ozet)); } return s; } std::vector olayBatchCoz(const std::string& govde, uint32_t kaynakPid) { std::vector out; const char* p = govde.data(); const char* son = p + govde.size(); if (son - p < 2) return out; const uint16_t adet = okuU16(p); p += 2; for (uint16_t i = 0; i < adet; ++i) { if (son - p < 8 + 8 + 2 + 4 + 48) break; ForwardOlay fo; fo.kaynakPid = kaynakPid; fo.sira = okuU64(p); p += 8; const uint64_t zbits = okuU64(p); p += 8; std::memcpy(&fo.zaman, &zbits, 8); fo.tur = okuU16(p); p += 2; fo.varlik = okuU32(p); p += 4; std::memcpy(fo.ozet, p, 48); p += 48; fo.ozet[sizeof(fo.ozet) - 1] = 0; out.push_back(fo); } return out; } } // namespace void SurecKopru::baslat(const std::string& baslik, bool anaMi) { benimPid_ = GetCurrentProcessId(); anaMi_ = anaMi; baslik_ = baslik; belgeAdi_ = baslik.empty() ? "Adsız" : baslik; baslatUnix_ = simdiUnix(); // Kayıt: canlı süreçleri süz + kendini ekle (mükerrer PID temizle). { KayitKilit kilit; auto liste = ayristirVeSuz(kayitOku()); std::string yeni; for (const SurecBilgi& s : liste) if (s.pid != benimPid_) yeni += satirYaz(s); SurecBilgi ben; ben.pid = benimPid_; ben.belge = baslik.empty() ? "Adsız" : baslik; ben.baslatUnix = baslatUnix_; ben.durum = SurecDurum::Aktif; ben.sonHeartbeatUnix = baslatUnix_; ben.bellekKB = kendiBellekKB(); yeni += satirYaz(ben); kayitYaz(yeni); } pipeIptal_ = CreateEventW(nullptr, TRUE, FALSE, nullptr); // manual-reset iptal calisiyor_ = true; sunucu_ = std::thread([this] { sunucuDongusu(); }); } void SurecKopru::durdur() { if (!calisiyor_) return; calisiyor_ = false; if (pipeIptal_) SetEvent((HANDLE)pipeIptal_); // Kendi pipe'ına sahte bağlan (accept'i uyandır) — overlapped iptal yedeği. HANDLE uyandir = CreateFileW(pipeAdi(benimPid_).c_str(), GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); if (uyandir != INVALID_HANDLE_VALUE) CloseHandle(uyandir); if (sunucu_.joinable()) sunucu_.join(); if (pipeIptal_) { CloseHandle((HANDLE)pipeIptal_); pipeIptal_ = nullptr; } // Kayıttan kendini çıkar. KayitKilit kilit; auto liste = ayristirVeSuz(kayitOku()); std::string yeni; for (const SurecBilgi& s : liste) if (s.pid != benimPid_) yeni += satirYaz(s); kayitYaz(yeni); } void SurecKopru::sunucuDongusu() { while (calisiyor_) { HANDLE pipe = CreateNamedPipeW(pipeAdi(benimPid_).c_str(), PIPE_ACCESS_INBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 0, 65536, 0, nullptr); if (pipe == INVALID_HANDLE_VALUE) { Sleep(100); continue; } const BOOL bagli = ConnectNamedPipe(pipe, nullptr) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); if (!calisiyor_) { CloseHandle(pipe); break; } if (bagli) { const std::string mesaj = mesajOku(pipe); SurecCerceve c; if (!mesaj.empty() && cerceveDeserialize(mesaj.data(), mesaj.size(), c)) { switch (c.tip) { case SurecMesajTip::Komut: case SurecMesajTip::Broadcast: { std::lock_guard g(kilit_); kuyruk_.push_back({SurecMesajTip::Komut, c.govde, {}, c.kaynakPid, c.korelasyon, false}); break; } case SurecMesajTip::Parametre: { IpcKomut k; k.tip = SurecMesajTip::Parametre; k.kaynakPid = c.kaynakPid; k.korelasyon = c.korelasyon; if (paramCoz(c.govde, k.ad, k.parametreler)) { std::lock_guard g(kilit_); kuyruk_.push_back(std::move(k)); } break; } case SurecMesajTip::Kapat: { IpcKomut k; k.tip = SurecMesajTip::Kapat; k.kaynakPid = c.kaynakPid; k.korelasyon = c.korelasyon; k.zorla = !c.govde.empty() && c.govde[0] == '1'; std::lock_guard g(kilit_); kuyruk_.push_back(std::move(k)); break; } case SurecMesajTip::Olay: { auto batch = olayBatchCoz(c.govde, c.kaynakPid); if (!batch.empty()) { std::lock_guard g(kilit_); for (auto& fo : batch) gelenOlaylar_.push_back(fo); } break; } case SurecMesajTip::OlayAbone: merkezPid_ = c.kaynakPid; merkezAboneMs_ = simdiMs(); break; case SurecMesajTip::OlayAboneIptal: if (merkezPid_ == c.kaynakPid) merkezPid_ = 0; break; case SurecMesajTip::Onay: { IpcOnay o; o.kaynakPid = c.kaynakPid; o.korelasyon = c.korelasyon; if (onayCoz(c.govde, o.basarili, o.sonuc)) { std::lock_guard g(kilit_); gelenOnaylar_.push_back(std::move(o)); } break; } default: break; // Durum/Heartbeat/bilinmeyen → no-op (ileri-uyum) } } } DisconnectNamedPipe(pipe); CloseHandle(pipe); } } std::vector SurecKopru::beklemedekiOnaylar() { std::vector cikti; std::lock_guard g(kilit_); cikti.swap(gelenOnaylar_); return cikti; } std::vector SurecKopru::beklemedekiCerceveler() { std::vector cikti; std::lock_guard g(kilit_); cikti.swap(kuyruk_); return cikti; } std::vector SurecKopru::beklemedekiKomutlar() { std::vector cikti; for (const IpcKomut& k : beklemedekiCerceveler()) if (k.tip == SurecMesajTip::Komut || k.tip == SurecMesajTip::Parametre) cikti.push_back(k.ad); return cikti; } std::vector SurecKopru::beklemedekiOlaylar() { std::vector cikti; std::lock_guard g(kilit_); cikti.swap(gelenOlaylar_); return cikti; } void SurecKopru::heartbeatYaz(const std::string& belge, SurecDurum durum) { KayitKilit kilit; auto liste = ayristirVeSuz(kayitOku()); std::string yeni; bool benVar = false; for (SurecBilgi& s : liste) { if (s.pid == benimPid_) { s.belge = belge.empty() ? "Adsız" : belge; s.durum = durum; s.baslatUnix = baslatUnix_; s.sonHeartbeatUnix = simdiUnix(); s.bellekKB = kendiBellekKB(); benVar = true; } yeni += satirYaz(s); } if (!benVar) { // kayıt sıfırlanmış olabilir → kendini geri ekle SurecBilgi ben; ben.pid = benimPid_; ben.belge = belge.empty() ? "Adsız" : belge; ben.baslatUnix = baslatUnix_; ben.durum = durum; ben.sonHeartbeatUnix = simdiUnix(); ben.bellekKB = kendiBellekKB(); yeni += satirYaz(ben); } kayitYaz(yeni); } void SurecKopru::belgeAdiAyarla(const std::string& ad) { belgeAdi_ = ad.empty() ? "Adsız" : ad; } void SurecKopru::durumAyarla(SurecDurum durum) { durumSelf_ = durum; } void SurecKopru::kalpAt() { if (!calisiyor_) return; const uint64_t simdi = simdiMs(); if (simdi - sonHeartbeatMs_ < HEARTBEAT_ARALIK_MS) return; sonHeartbeatMs_ = simdi; heartbeatYaz(belgeAdi_, durumSelf_); } void SurecKopru::olaylariForwardEt(OlayGunlugu& glog) { const uint32_t merkez = merkezPid_; if (!merkez || merkez == benimPid_) return; if (simdiMs() - merkezAboneMs_ > ABONE_TTL_MS) { merkezPid_ = 0; return; } const uint64_t simdi = simdiMs(); if (simdi - sonForwardMs_ < FORWARD_ARALIK_MS) return; sonForwardMs_ = simdi; std::vector batch; uint64_t enSonSira = sonForwardSira_; bool doldu = false; for (const OlayKaydi& k : glog.kayitlar()) { if (k.sira < sonForwardSira_) continue; enSonSira = k.sira + 1; if (!glog.kalici(k.tur)) continue; // yüksek-frekans süz (bedava) if (k.tur == OlayTuru::SurecOlayAlindi) continue; // DÖNGÜ GUARD (re-forward yasak) ForwardOlay fo; fo.kaynakPid = benimPid_; fo.tur = (uint16_t)k.tur; fo.varlik = k.varlik; fo.zaman = k.zaman; fo.sira = k.sira; std::memcpy(fo.ozet, k.ozet, sizeof(fo.ozet)); fo.ozet[sizeof(fo.ozet) - 1] = 0; batch.push_back(fo); if (batch.size() >= FORWARD_BATCH_MAX) { doldu = true; break; } } sonForwardSira_ = doldu ? enSonSira : glog.sonrakiSira(); if (batch.empty()) return; SurecCerceve c; c.tip = SurecMesajTip::Olay; c.kaynakPid = benimPid_; c.hedefPid = merkez; c.govde = olayBatchSerialize(batch); gonderCerceve(merkez, c); // best-effort } void SurecKopru::olayAboneligiYayinla(bool acik) { const uint64_t simdi = simdiMs(); if (acik) { if (simdi - sonAboneMs_ < ABONE_YAYIN_ARALIK_MS) return; sonAboneMs_ = simdi; } SurecCerceve c; c.tip = acik ? SurecMesajTip::OlayAbone : SurecMesajTip::OlayAboneIptal; c.kaynakPid = benimPid_; for (const SurecBilgi& s : surecler()) if (s.pid != benimPid_) { c.hedefPid = s.pid; gonderCerceve(s.pid, c); } } std::vector SurecKopru::surecler() const { KayitKilit kilit; return ayristirVeSuz(kayitOku()); } bool SurecKopru::gonderCerceve(uint32_t pid, const SurecCerceve& c) const { const std::wstring pn = pipeAdi(pid); HANDLE pipe = CreateFileW(pn.c_str(), GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); if (pipe == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_PIPE_BUSY && WaitNamedPipeW(pn.c_str(), 100)) pipe = CreateFileW(pn.c_str(), GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); if (pipe == INVALID_HANDLE_VALUE) return false; } const std::string wire = cerceveSerialize(c); DWORD yazildi = 0; const BOOL ok = WriteFile(pipe, wire.data(), (DWORD)wire.size(), &yazildi, nullptr); CloseHandle(pipe); return ok && yazildi == wire.size(); } bool SurecKopru::gonder(uint32_t pid, const std::string& komutAd) const { SurecCerceve c; c.tip = SurecMesajTip::Komut; c.kaynakPid = benimPid_; c.hedefPid = pid; c.govde = komutAd; return gonderCerceve(pid, c); } bool SurecKopru::gonder(uint32_t pid, const std::string& komutAd, const std::vector>& parametreler) const { SurecCerceve c; c.tip = SurecMesajTip::Parametre; c.kaynakPid = benimPid_; c.hedefPid = pid; c.govde = paramSerialize(komutAd, parametreler); return gonderCerceve(pid, c); } bool SurecKopru::gonderKorelasyonlu( uint32_t pid, const std::string& komutAd, const std::vector>& parametreler, uint64_t korelasyon) const { SurecCerceve c; c.tip = SurecMesajTip::Parametre; c.kaynakPid = benimPid_; c.hedefPid = pid; c.korelasyon = korelasyon; c.govde = paramSerialize(komutAd, parametreler); return gonderCerceve(pid, c); } bool SurecKopru::onayGonder(uint32_t hedefPid, uint64_t korelasyon, bool basarili, const std::string& sonuc) const { SurecCerceve c; c.tip = SurecMesajTip::Onay; c.kaynakPid = benimPid_; c.hedefPid = hedefPid; c.korelasyon = korelasyon; c.govde = onaySerialize(basarili, sonuc); return gonderCerceve(hedefPid, c); } int SurecKopru::hepsineGonder(const std::string& komutAd) const { int ulasan = 0; for (const SurecBilgi& s : surecler()) if (s.pid != benimPid_ && gonder(s.pid, komutAd)) ++ulasan; return ulasan; } int SurecKopru::hepsineGonder( const std::string& komutAd, const std::vector>& parametreler) const { int ulasan = 0; for (const SurecBilgi& s : surecler()) if (s.pid != benimPid_ && gonder(s.pid, komutAd, parametreler)) ++ulasan; return ulasan; } bool SurecKopru::sonlandir(uint32_t pid, bool zorla) const { if (pid == 0 || pid == benimPid_) return false; // kendini öldürme guard'ı // fikocad guard: hedef kayıtta LİSTELİ olmalı (rastgele sistem PID'i öldürülemez). bool listeli = false; for (const SurecBilgi& s : surecler()) if (s.pid == pid) { listeli = true; break; } if (!listeli) return false; // fikocad imza: hedefin gelen-kutusu pipe'ı var olmalı. if (!pipeVar(pid)) return false; if (!zorla) { // nazik: Kapat çerçevesi → süreç kaydet-sor → WM_CLOSE SurecCerceve c; c.tip = SurecMesajTip::Kapat; c.kaynakPid = benimPid_; c.hedefPid = pid; return gonderCerceve(pid, c); } // zorla: PID'den handle aç + sonlandır (kayıt bir sonraki taramada süzülür). HANDLE h = OpenProcess(PROCESS_TERMINATE, FALSE, pid); if (!h) return false; const BOOL ok = TerminateProcess(h, 1); CloseHandle(h); return ok != 0; } } // namespace fiko