204 lines
6.8 KiB
C++
204 lines
6.8 KiB
C++
#include "chrome_scanner.h"
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
#ifdef _WIN32
|
|
# define WIN32_LEAN_AND_MEAN
|
|
# include <windows.h>
|
|
#endif
|
|
|
|
namespace navegator {
|
|
|
|
namespace {
|
|
|
|
#ifdef _WIN32
|
|
// Ejecuta un comando con CreateProcess + pipe stdout, OCULTANDO la consola.
|
|
// _popen siempre muestra cmd.exe en GUI apps WIN32_EXECUTABLE — para auto-rescan
|
|
// cada 2s eso parpadea sin parar. CREATE_NO_WINDOW evita el flicker.
|
|
std::string run_capture(const std::string& cmd) {
|
|
HANDLE r_pipe = nullptr;
|
|
HANDLE w_pipe = nullptr;
|
|
SECURITY_ATTRIBUTES sa{};
|
|
sa.nLength = sizeof(sa);
|
|
sa.bInheritHandle = TRUE;
|
|
if (!CreatePipe(&r_pipe, &w_pipe, &sa, 0)) return "";
|
|
SetHandleInformation(r_pipe, HANDLE_FLAG_INHERIT, 0);
|
|
|
|
STARTUPINFOA si{};
|
|
si.cb = sizeof(si);
|
|
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
|
si.wShowWindow = SW_HIDE;
|
|
si.hStdOutput = w_pipe;
|
|
si.hStdError = w_pipe;
|
|
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
PROCESS_INFORMATION pi{};
|
|
std::string mutable_cmd = cmd;
|
|
BOOL ok = CreateProcessA(
|
|
nullptr,
|
|
mutable_cmd.data(),
|
|
nullptr, nullptr, TRUE,
|
|
CREATE_NO_WINDOW,
|
|
nullptr, nullptr, &si, &pi);
|
|
|
|
CloseHandle(w_pipe); // padre no escribe
|
|
|
|
if (!ok) {
|
|
CloseHandle(r_pipe);
|
|
return "";
|
|
}
|
|
|
|
std::string out;
|
|
char buf[4096];
|
|
DWORD nread = 0;
|
|
while (ReadFile(r_pipe, buf, sizeof(buf), &nread, nullptr) && nread > 0) {
|
|
out.append(buf, nread);
|
|
}
|
|
CloseHandle(r_pipe);
|
|
|
|
WaitForSingleObject(pi.hProcess, 30000);
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
return out;
|
|
}
|
|
#else
|
|
std::string run_capture(const std::string& cmd) {
|
|
FILE* pipe = popen(cmd.c_str(), "r");
|
|
if (!pipe) return "";
|
|
std::string out;
|
|
char buf[4096];
|
|
while (fgets(buf, sizeof(buf), pipe)) out.append(buf);
|
|
pclose(pipe);
|
|
return out;
|
|
}
|
|
#endif
|
|
|
|
// Extrae el valor de --flag=VALUE o --flag VALUE de una commandline.
|
|
// Devuelve cadena vacia si no se encuentra.
|
|
std::string extract_flag(const std::string& cmd, const std::string& flag) {
|
|
// Busca "--flag=" primero.
|
|
std::string needle_eq = flag + "=";
|
|
auto p = cmd.find(needle_eq);
|
|
if (p != std::string::npos) {
|
|
size_t start = p + needle_eq.size();
|
|
// Si empieza con comilla, leer hasta la siguiente.
|
|
char quote = 0;
|
|
if (start < cmd.size() && (cmd[start] == '"' || cmd[start] == '\'')) {
|
|
quote = cmd[start];
|
|
++start;
|
|
}
|
|
size_t end = start;
|
|
while (end < cmd.size()) {
|
|
if (quote) {
|
|
if (cmd[end] == quote) break;
|
|
} else {
|
|
if (cmd[end] == ' ') break;
|
|
}
|
|
++end;
|
|
}
|
|
return cmd.substr(start, end - start);
|
|
}
|
|
// Fallback: "--flag VALUE".
|
|
auto p2 = cmd.find(flag + " ");
|
|
if (p2 != std::string::npos) {
|
|
size_t start = p2 + flag.size() + 1;
|
|
size_t end = cmd.find(' ', start);
|
|
if (end == std::string::npos) end = cmd.size();
|
|
return cmd.substr(start, end - start);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
std::string basename_of_path(const std::string& path) {
|
|
size_t s1 = path.find_last_of('\\');
|
|
size_t s2 = path.find_last_of('/');
|
|
size_t s = std::string::npos;
|
|
if (s1 != std::string::npos) s = s1;
|
|
if (s2 != std::string::npos && (s == std::string::npos || s2 > s)) s = s2;
|
|
if (s == std::string::npos) return path;
|
|
return path.substr(s + 1);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
std::vector<ChromeInstance> scan_chrome_instances() {
|
|
#ifndef _WIN32
|
|
return {};
|
|
#else
|
|
// Format: por cada chrome.exe que tenga --remote-debugging-port, escribimos
|
|
// dos lineas: "PID:<n>" y "CMD:<commandline>", separadas por "---".
|
|
// Quoting: el CommandLine puede tener caracteres raros, pero lo escribimos
|
|
// tal cual y parseamos por prefijo. Sin JSON => sin dependencias.
|
|
// Filtrar SOLO el master process: tiene --remote-debugging-port y NO tiene
|
|
// --type= (los renderers/gpu-process/utility heredan el cmdline del padre
|
|
// pero llevan ademas --type=renderer / --type=gpu-process / etc).
|
|
const char* ps_script =
|
|
"powershell.exe -NoProfile -Command \""
|
|
"Get-CimInstance Win32_Process -Filter \\\"Name='chrome.exe'\\\" "
|
|
"| Where-Object { $_.CommandLine -like '*--remote-debugging-port=*' "
|
|
" -and $_.CommandLine -notlike '*--type=*' } "
|
|
"| ForEach-Object { "
|
|
" Write-Output '---'; "
|
|
" Write-Output ('PID:' + $_.ProcessId); "
|
|
" Write-Output ('CMD:' + $_.CommandLine) "
|
|
"}\"";
|
|
std::string out = run_capture(ps_script);
|
|
|
|
std::vector<ChromeInstance> result;
|
|
std::istringstream ss(out);
|
|
std::string line;
|
|
ChromeInstance cur;
|
|
bool have_cur = false;
|
|
while (std::getline(ss, line)) {
|
|
// Trim CR si viene con \r\n.
|
|
while (!line.empty() && (line.back() == '\r' || line.back() == '\n')) line.pop_back();
|
|
if (line == "---") {
|
|
if (have_cur && cur.port > 0) result.push_back(cur);
|
|
cur = ChromeInstance{};
|
|
have_cur = true;
|
|
} else if (line.rfind("PID:", 0) == 0) {
|
|
try { cur.pid = (uint32_t)std::stoul(line.substr(4)); } catch (...) { cur.pid = 0; }
|
|
} else if (line.rfind("CMD:", 0) == 0) {
|
|
cur.command_line = line.substr(4);
|
|
std::string port_str = extract_flag(cur.command_line, "--remote-debugging-port");
|
|
try { cur.port = std::stoi(port_str); } catch (...) { cur.port = 0; }
|
|
cur.user_data_dir = extract_flag(cur.command_line, "--user-data-dir");
|
|
cur.profile_name = basename_of_path(cur.user_data_dir);
|
|
cur.headless =
|
|
cur.command_line.find("--headless") != std::string::npos;
|
|
}
|
|
}
|
|
if (have_cur && cur.port > 0) result.push_back(cur);
|
|
return result;
|
|
#endif
|
|
}
|
|
|
|
int kill_chromes_by_userdata(const std::string& user_data_dir_substr) {
|
|
#ifndef _WIN32
|
|
(void)user_data_dir_substr;
|
|
return -1;
|
|
#else
|
|
if (user_data_dir_substr.empty()) return -1;
|
|
// Escapamos comillas simples duplicandolas (PowerShell single-quote rule).
|
|
std::string esc;
|
|
esc.reserve(user_data_dir_substr.size() * 2);
|
|
for (char c : user_data_dir_substr) {
|
|
if (c == '\'') esc += "''";
|
|
else esc += c;
|
|
}
|
|
std::string cmd =
|
|
"powershell.exe -NoProfile -Command \""
|
|
"$n = (Get-CimInstance Win32_Process -Filter \\\"Name='chrome.exe'\\\" "
|
|
"| Where-Object { $_.CommandLine -like '*" + esc + "*' } "
|
|
"| ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue; 1 } "
|
|
"| Measure-Object).Count; Write-Output $n\"";
|
|
std::string out = run_capture(cmd);
|
|
try { return std::stoi(out); } catch (...) { return -1; }
|
|
#endif
|
|
}
|
|
|
|
} // namespace navegator
|