From effb1c08f7eb5c1deb743fe4b2fd9b145be374b7 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Tue, 2 Jun 2026 21:01:17 +0200 Subject: [PATCH] feat: portar panel Sistema del widget previo + servicio de captura MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pestaña Sistema: - Reproduce las 9 gráficas del widget anterior dibujadas con Cairo sobre históricos en memoria: CPU, RAM, CPU temp, GPU, GPU temp, VRAM, red (down/up superpuestos) y disk I/O, más las barras de uso de los discos /, /mnt/1tb, /mnt/2tb y /mnt/16tb. - metric.sh portado (nvidia-smi + coretemp hwmon) para temperaturas y métricas de GPU. - Paleta Nord, igual que el panel original. Widget redimensionado a 290x545 para acomodar el panel Sistema. Servicio de captura (service/): - packet-capture.service: dumpcap en ring buffer (~10 min, 10 archivos de hasta 60s/50MB, tope ~500MB) escribiendo en /var/log/pktcap. - install-capture.sh: crea el directorio, instala y activa el unit. El botón Wireshark abre ahora el .pcapng más reciente del buffer. Co-Authored-By: Claude Opus 4.8 (1M context) --- conky.conf | 6 +- lua/widget.lua | 299 ++++++++++++++++++++------------- metric.sh | 16 ++ service/install-capture.sh | 48 ++++++ service/packet-capture.service | 26 +++ 5 files changed, 276 insertions(+), 119 deletions(-) create mode 100755 metric.sh create mode 100755 service/install-capture.sh create mode 100644 service/packet-capture.service diff --git a/conky.conf b/conky.conf index 4cfe015..d56debb 100644 --- a/conky.conf +++ b/conky.conf @@ -14,9 +14,9 @@ conky.config = { xinerama_head = 1, -- 1 = DP-1 (pantalla izquierda). 0 = HDMI-0 derecha. gap_x = 30, -- separacion desde el borde derecho gap_y = 50, -- separa del panel superior de XFCE - minimum_width = 300, - maximum_width = 300, - minimum_height = 360, + minimum_width = 290, + maximum_width = 290, + minimum_height = 545, -- Ventana (tipo 'normal' = recibe clicks; 'desktop' los ignora) ---------- own_window = true, diff --git a/lua/widget.lua b/lua/widget.lua index 7ced9e1..d3b379b 100644 --- a/lua/widget.lua +++ b/lua/widget.lua @@ -3,72 +3,99 @@ conky_widget — render Cairo + pestañas clickeables. Estructura: - Estado: current_tab (1=Red, 2=Sistema, 3=Docker). - - conky_draw (lua_draw_hook_post): dibuja la barra de pestañas y el panel activo. + - conky_draw (lua_draw_hook_post): mantiene los historicos y dibuja la barra + de pestañas y el panel activo. - conky_mouse (lua_mouse_hook): cambia de pestaña y lanza apps al clicar botones. Los datos del sistema se obtienen llamando a conky_parse("${...}") desde Lua, de modo que se pueden colocar con libertad mediante Cairo. -Geometria fija (la ventana mide 300x360 segun conky.conf): - - Barra de pestañas: y 4..28, tres pestañas de ancho W/3. - - Botones de la pestaña Red: fila inferior en BTN_Y. +La pestaña Sistema reproduce el panel del widget previo: nueve graficas de +linea (CPU, RAM, CPU temp, GPU, GPU temp, VRAM, red, disk I/O) mas las barras +de uso de los cuatro discos. Los valores de temperatura/GPU se obtienen de +metric.sh (nvidia-smi + coretemp hwmon). + +Geometria fija (la ventana mide W x H segun conky.conf). ]] require 'cairo' pcall(require, 'cairo_xlib') -- conky >= 1.12 separa el modulo cairo_xlib -- Geometria compartida entre dibujo y eventos de raton ----------------------- -local W = 300 -local H = 360 +local W = 290 +local H = 545 local TAB_TOP = 4 local TAB_H = 24 -local BTN_H = 28 -local BTN_Y = H - 40 -- 320 +local BTN_H = 26 +local BTN_Y = 242 -- fila de botones de la pestaña Red --- Interfaz de red a monitorizar (detectada: enp5s0 es la fisica activa) ------ +-- Interfaz de red a monitorizar (enp5s0 es la fisica activa) ------------------ local NIF = "enp5s0" +-- Helper de metricas (temps + GPU), portado del widget previo ----------------- +local MET = os.getenv("HOME") .. "/.config/conky/conky_widget/metric.sh" + -- Pestañas ------------------------------------------------------------------- local TABS = { "Red", "Sistema", "Docker" } local current_tab = 1 --- Botones de la pestaña Red. cmd es un comando shell; bin se comprueba antes --- de lanzar y, si falta, launch.sh avisa con notify-send. +-- Botones de la pestaña Red. bin se comprueba antes de lanzar; si falta, +-- launch.sh avisa con notify-send. local BTNS = { { label = "Wireshark", bin = "wireshark", pkg = "wireshark", - cmd = "wireshark /var/log/pktcap/cap.pcapng 2>/dev/null || wireshark" }, + cmd = "f=$(ls -t /var/log/pktcap/*.pcapng 2>/dev/null | head -1); " .. + "if [ -n \"$f\" ]; then wireshark -r \"$f\"; else wireshark; fi" }, { label = "ntopng", bin = "ntopng", pkg = "ntopng", cmd = "xdg-open http://localhost:3000" }, { label = "nethogs", bin = "nethogs", pkg = "nethogs", - cmd = "xfce4-terminal --title='nethogs ' -e 'sudo nethogs " .. NIF .. "'" }, + cmd = "xfce4-terminal --title='nethogs' -e 'sudo nethogs " .. NIF .. "'" }, } local LAUNCH = os.getenv("HOME") .. "/.config/conky/conky_widget/lua/launch.sh" --- Historico de velocidad de red para el grafico en vivo ---------------------- -local HIST = 60 -local down_hist, up_hist = {}, {} -for i = 1, HIST do down_hist[i] = 0; up_hist[i] = 0 end +-- Historicos para los graficos en vivo --------------------------------------- +local GH = 58 +local KEYS = { "cpu", "ram", "cputemp", "gputil", "gputemp", "vram", "diskio", "down", "up" } +local hist = {} +for _, k in ipairs(KEYS) do + hist[k] = {} + for i = 1, GH do hist[k][i] = 0 end +end local function push(t, v) table.remove(t, 1) t[#t + 1] = v end --- Paleta --------------------------------------------------------------------- +-- Paleta (Nord, como el widget previo) --------------------------------------- +local function hex(s) + return { + tonumber(s:sub(1, 2), 16) / 255, + tonumber(s:sub(3, 4), 16) / 255, + tonumber(s:sub(5, 6), 16) / 255, + } +end + local COL = { - bg = { 0.08, 0.09, 0.11 }, - panel = { 0.14, 0.15, 0.18 }, - tab_active = { 0.18, 0.55, 0.85 }, - tab_inactive = { 0.18, 0.19, 0.23 }, - text = { 0.86, 0.87, 0.90 }, - white = { 1.00, 1.00, 1.00 }, - dim = { 0.55, 0.57, 0.62 }, - green = { 0.30, 0.80, 0.45 }, - orange = { 0.95, 0.60, 0.20 }, - red = { 0.90, 0.30, 0.35 }, - down = { 0.30, 0.70, 0.95 }, - up = { 0.95, 0.55, 0.30 }, + bg = hex("0e0f12"), + panel = hex("1b1d23"), + tab_active = hex("5e81ac"), + tab_inactive = hex("2e3440"), + text = hex("d8dee9"), + snow = hex("eceff4"), + white = { 1, 1, 1 }, + dim = hex("7b8394"), + teal = hex("8fbcbb"), + green = hex("a3be8c"), + cyan = hex("88c0d0"), + blue = hex("81a1c1"), + frost = hex("5e81ac"), + yellow = hex("ebcb8b"), + orange = hex("d08770"), + purple = hex("b48ead"), + red = hex("bf616a"), + down = hex("88c0d0"), + up = hex("d08770"), } -- Helpers de lectura de datos ------------------------------------------------ @@ -82,6 +109,17 @@ local function num(expr) return tonumber(str(expr)) or 0 end +-- Convierte un texto de tasa de conky ("1.2MiB", "300KiB", "5B") a KiB/s ------ +local function rate_kib(s) + local n, unit = s:match("([%d%.]+)%s*([KMGTPi]*)B?") + n = tonumber(n) or 0 + unit = unit or "" + if unit:find("M") then return n * 1024 + elseif unit:find("G") then return n * 1024 * 1024 + elseif unit:find("K") then return n + else return n / 1024 end +end + -- Helpers de dibujo ---------------------------------------------------------- local function setcol(cr, c, a) cairo_set_source_rgba(cr, c[1], c[2], c[3], a or 1.0) @@ -105,10 +143,9 @@ local function text(cr, x, y, s, c, size, bold) cairo_show_text(cr, s) end --- Centrado aproximado para fuente monoespaciada (avance ~0.6*tamaño) ---------- -local function ctext(cr, cx, y, s, c, size, bold) - local tw = #s * (size or 12) * 0.6 - text(cr, cx - tw / 2, y, s, c, size, bold) +-- Texto alineado a la derecha (avance mono ~0.6*tamaño) ---------------------- +local function rtext(cr, xr, y, s, c, size, bold) + text(cr, xr - #s * (size or 12) * 0.6, y, s, c, size, bold) end local function bar(cr, x, y, w, h, frac, c) @@ -117,6 +154,30 @@ local function bar(cr, x, y, w, h, frac, c) setcol(cr, c); rrect(cr, x, y, math.max(2, w * frac), h, 3); cairo_fill(cr) end +-- Grafico de linea sobre un panel redondeado --------------------------------- +local function graph(cr, x, y, w, h, series) + setcol(cr, COL.panel); rrect(cr, x, y, w, h, 4); cairo_fill(cr) + -- Maximo comun a todas las series (fijo si se indica) + local maxv = 1 + for _, s in ipairs(series) do + if s.max then + if s.max > maxv then maxv = s.max end + else + for i = 1, #s.data do if s.data[i] > maxv then maxv = s.data[i] end end + end + end + for _, s in ipairs(series) do + cairo_set_line_width(cr, 1.3); setcol(cr, s.c) + local n = #s.data + for i = 1, n do + local px = x + (i - 1) / (n - 1) * w + local py = y + h - (s.data[i] / maxv) * (h - 4) - 2 + if i == 1 then cairo_move_to(cr, px, py) else cairo_line_to(cr, px, py) end + end + cairo_stroke(cr) + end +end + -- Barra de pestañas ---------------------------------------------------------- local function draw_tabs(cr) local tw = W / 3 @@ -125,33 +186,12 @@ local function draw_tabs(cr) local active = (i == current_tab) setcol(cr, active and COL.tab_active or COL.tab_inactive) rrect(cr, x + 3, TAB_TOP, tw - 6, TAB_H, 5); cairo_fill(cr) - ctext(cr, x + tw / 2, TAB_TOP + 17, TABS[i], + local lbl = TABS[i] + text(cr, x + tw / 2 - #lbl * 12 * 0.6 / 2, TAB_TOP + 17, lbl, active and COL.white or COL.dim, 12, active) end end --- Grafico de red en vivo ----------------------------------------------------- -local function draw_netgraph(cr, x, y, w, h) - setcol(cr, COL.panel); rrect(cr, x, y, w, h, 6); cairo_fill(cr) - local maxv = 1 - for i = 1, HIST do - if down_hist[i] > maxv then maxv = down_hist[i] end - if up_hist[i] > maxv then maxv = up_hist[i] end - end - local function plot(hist, c) - cairo_set_line_width(cr, 1.5); setcol(cr, c) - for i = 1, HIST do - local px = x + (i - 1) / (HIST - 1) * w - local py = y + h - (hist[i] / maxv) * (h - 6) - 3 - if i == 1 then cairo_move_to(cr, px, py) else cairo_line_to(cr, px, py) end - end - cairo_stroke(cr) - end - plot(down_hist, COL.down) - plot(up_hist, COL.up) - text(cr, x + 5, y + 12, string.format("max %.0f KiB/s", maxv), COL.dim, 8) -end - -- Botones de la pestaña Red -------------------------------------------------- local function draw_buttons(cr) local bw = (W - 24) / 3 @@ -160,77 +200,98 @@ local function draw_buttons(cr) setcol(cr, COL.tab_inactive); rrect(cr, bx, BTN_Y, bw, BTN_H, 6); cairo_fill(cr) setcol(cr, COL.tab_active, 0.9); cairo_set_line_width(cr, 1) rrect(cr, bx, BTN_Y, bw, BTN_H, 6); cairo_stroke(cr) - ctext(cr, bx + bw / 2, BTN_Y + 18, BTNS[i].label, COL.text, 10) + local lbl = BTNS[i].label + text(cr, bx + bw / 2 - #lbl * 10 * 0.6 / 2, BTN_Y + 17, lbl, COL.text, 10) end end -- Panel: Red ----------------------------------------------------------------- local function draw_red(cr) local y = 50 - text(cr, 14, y, "Interfaz " .. NIF, COL.dim, 11); y = y + 22 + text(cr, 12, y, "Interfaz " .. NIF, COL.dim, 11); y = y + 22 - text(cr, 14, y, "Down", COL.down, 12) + text(cr, 12, y, "Down", COL.down, 12) text(cr, 90, y, str("${downspeed " .. NIF .. "}"), COL.text, 13, true); y = y + 20 - text(cr, 14, y, "Up", COL.up, 12) + text(cr, 12, y, "Up", COL.up, 12) text(cr, 90, y, str("${upspeed " .. NIF .. "}"), COL.text, 13, true); y = y + 24 - draw_netgraph(cr, 14, y, W - 28, 78); y = y + 90 + graph(cr, 12, y, W - 24, 70, { + { data = hist.down, c = COL.down }, + { data = hist.up, c = COL.up }, + }) + y = y + 78 local conns = str("${execi 2 ss -tun state established 2>/dev/null | tail -n +2 | wc -l}") - text(cr, 14, y, "Conexiones activas: " .. conns, COL.text, 11); y = y + 18 - text(cr, 14, y, "Total ↓ " .. str("${totaldown " .. NIF .. "}") .. + text(cr, 12, y, "Conexiones activas: " .. conns, COL.text, 11); y = y + 16 + text(cr, 12, y, "Total ↓ " .. str("${totaldown " .. NIF .. "}") .. " ↑ " .. str("${totalup " .. NIF .. "}"), COL.dim, 10) draw_buttons(cr) + + text(cr, 12, BTN_Y + BTN_H + 18, + "Wireshark abre el buffer de captura mas reciente.", COL.dim, 9) end --- Panel: Sistema ------------------------------------------------------------- +-- Panel: Sistema (portado del widget previo) --------------------------------- local function draw_sys(cr) - local y = 50 + local x = 10 + local gw = W - 20 + local y = 38 - local cpu = num("${cpu cpu0}") - text(cr, 14, y, "CPU", COL.text, 12, true) - text(cr, W - 52, y, string.format("%d%%", cpu), COL.text, 12) - y = y + 6 - bar(cr, 14, y, W - 28, 8, cpu / 100, cpu > 80 and COL.red or COL.green) - y = y + 18 - - local nproc = math.floor(num("${exec nproc}")) - if nproc < 1 then nproc = 1 elseif nproc > 16 then nproc = 16 end - local cw = (W - 28) / nproc - for i = 1, nproc do - local cu = num("${cpu cpu" .. i .. "}") - bar(cr, 14 + (i - 1) * cw, y, cw - 2, 6, cu / 100, - cu > 80 and COL.red or COL.green) + local function row(label, value, lc, gh) + text(cr, x, y + 11, label, lc, 11, true) + if value and value ~= "" then rtext(cr, W - 10, y + 11, value, COL.snow, 10) end + y = y + 15 + return gh end - y = y + 20 - local memp = num("${memperc}") - text(cr, 14, y, "RAM", COL.text, 12, true) - text(cr, W - 150, y, str("${mem}") .. " / " .. str("${memmax}"), COL.dim, 9) - y = y + 6 - bar(cr, 14, y, W - 28, 8, memp / 100, memp > 85 and COL.red or COL.orange) - y = y + 18 + local cputemp = str("${execi 3 " .. MET .. " cpu_temp}") + local gputemp = str("${execi 2 " .. MET .. " gpu_temp}") - local swapp = num("${swapperc}") - text(cr, 14, y, "Swap", COL.text, 12, true) - text(cr, W - 150, y, str("${swap}") .. " / " .. str("${swapmax}"), COL.dim, 9) - y = y + 6 - bar(cr, 14, y, W - 28, 8, swapp / 100, COL.orange) - y = y + 18 + -- CPU + local gh = row("CPU " .. math.floor(num("${cpu cpu0}")) .. "%", + str("${freq_g}") .. "GHz " .. cputemp .. "°C", COL.teal, 26) + graph(cr, x, y, gw, gh, { { data = hist.cpu, c = COL.green, max = 100 } }); y = y + gh + 4 - local diskp = num("${fs_used_perc /}") - text(cr, 14, y, "Disco /", COL.text, 12, true) - text(cr, W - 150, y, str("${fs_used /}") .. " / " .. str("${fs_size /}"), COL.dim, 9) - y = y + 6 - bar(cr, 14, y, W - 28, 8, diskp / 100, diskp > 90 and COL.red or COL.green) - y = y + 24 + -- RAM + gh = row("RAM " .. math.floor(num("${memperc}")) .. "%", + str("${mem}") .. " / " .. str("${memmax}"), COL.green, 26) + graph(cr, x, y, gw, gh, { { data = hist.ram, c = COL.green, max = 100 } }); y = y + gh + 4 - local traw = tonumber(str("${execi 5 cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null}")) - local tstr = traw and string.format("%.0f°C", traw / 1000) or "n/a" - text(cr, 14, y, "Temp " .. tstr, COL.dim, 10) - text(cr, W - 150, y, "Load " .. str("${loadavg 1}"), COL.dim, 10); y = y + 16 - text(cr, 14, y, "Uptime " .. str("${uptime_short}"), COL.dim, 10) + -- CPU TEMP + gh = row("CPU TEMP " .. cputemp .. "°C", "", COL.yellow, 20) + graph(cr, x, y, gw, gh, { { data = hist.cputemp, c = COL.yellow, max = 100 } }); y = y + gh + 4 + + -- GPU + gh = row("GPU " .. str("${execi 2 " .. MET .. " gpu_util}") .. "%", + gputemp .. "°C", COL.cyan, 26) + graph(cr, x, y, gw, gh, { { data = hist.gputil, c = COL.cyan, max = 100 } }); y = y + gh + 4 + + -- GPU TEMP + gh = row("GPU TEMP " .. gputemp .. "°C", "", COL.yellow, 20) + graph(cr, x, y, gw, gh, { { data = hist.gputemp, c = COL.yellow, max = 100 } }); y = y + gh + 4 + + -- VRAM + gh = row("VRAM " .. str("${execi 2 " .. MET .. " gpu_memp}") .. "%", + str("${execi 2 " .. MET .. " gpu_memi}"), COL.purple, 26) + graph(cr, x, y, gw, gh, { { data = hist.vram, c = COL.purple, max = 100 } }); y = y + gh + 4 + + -- DISK I/O + gh = row("DISK I/O", str("${diskio}"), COL.orange, 26) + graph(cr, x, y, gw, gh, { { data = hist.diskio, c = COL.purple } }); y = y + gh + 6 + + -- Uso de discos + text(cr, x, y + 10, "USO DE DISCOS", COL.snow, 10, true); y = y + 16 + local disks = { "/", "/mnt/1tb", "/mnt/2tb", "/mnt/16tb" } + for _, m in ipairs(disks) do + local p = num("${fs_used_perc " .. m .. "}") + text(cr, x, y + 9, m, COL.green, 10) + rtext(cr, W - 10, y + 9, + str("${fs_used " .. m .. "}") .. "/" .. str("${fs_size " .. m .. "}") .. + " " .. math.floor(p) .. "%", COL.dim, 9) + y = y + 12 + bar(cr, x, y, gw, 6, p / 100, p > 90 and COL.red or COL.green); y = y + 11 + end end -- Panel: Docker -------------------------------------------------------------- @@ -238,19 +299,19 @@ local function draw_docker(cr) local y = 50 local running = str("${execi 3 docker ps -q 2>/dev/null | wc -l}") local total = str("${execi 10 docker ps -aq 2>/dev/null | wc -l}") - text(cr, 14, y, "Contenedores", COL.text, 12, true) - text(cr, W - 110, y, running .. " up / " .. total .. " total", COL.dim, 10) + text(cr, 12, y, "Contenedores", COL.text, 12, true) + rtext(cr, W - 10, y, running .. " up / " .. total .. " total", COL.dim, 10) y = y + 22 - local names = str("${execi 3 docker ps --format '{{.Names}}' 2>/dev/null | head -16}") + local names = str("${execi 3 docker ps --format '{{.Names}}' 2>/dev/null | head -28}") if names == "" then - text(cr, 18, y, "ninguno en marcha", COL.dim, 10) + text(cr, 16, y, "ninguno en marcha", COL.dim, 10) return end for line in names:gmatch("[^\n]+") do - text(cr, 18, y, "● " .. line, COL.green, 10) + text(cr, 16, y, "● " .. line, COL.green, 10) y = y + 15 - if y > H - 16 then break end + if y > H - 14 then break end end end @@ -262,9 +323,16 @@ function conky_draw() conky_window.width, conky_window.height) local cr = cairo_create(cs) - -- Mantener el historico de red vivo en todas las pestañas. - push(down_hist, num("${downspeedf " .. NIF .. "}")) - push(up_hist, num("${upspeedf " .. NIF .. "}")) + -- Mantener todos los historicos vivos en cualquier pestaña. + push(hist.cpu, num("${cpu cpu0}")) + push(hist.ram, num("${memperc}")) + push(hist.cputemp, num("${execi 3 " .. MET .. " cpu_temp}")) + push(hist.gputil, num("${execi 2 " .. MET .. " gpu_util}")) + push(hist.gputemp, num("${execi 2 " .. MET .. " gpu_temp}")) + push(hist.vram, num("${execi 2 " .. MET .. " gpu_memp}")) + push(hist.diskio, rate_kib(str("${diskio}"))) + push(hist.down, num("${downspeedf " .. NIF .. "}")) + push(hist.up, num("${upspeedf " .. NIF .. "}")) setcol(cr, COL.bg, 0.86); rrect(cr, 0, 0, W, H, 10); cairo_fill(cr) @@ -287,9 +355,8 @@ local function shell_quote(s) end local function launch(b) - local cmd = string.format("%s %s %s %s &", - shell_quote(LAUNCH), shell_quote(b.bin), shell_quote(b.pkg), shell_quote(b.cmd)) - os.execute(cmd) + os.execute(string.format("%s %s %s %s &", + shell_quote(LAUNCH), shell_quote(b.bin), shell_quote(b.pkg), shell_quote(b.cmd))) end -- Hook de raton: cambia de pestaña y lanza apps ------------------------------ diff --git a/metric.sh b/metric.sh new file mode 100755 index 0000000..c43607b --- /dev/null +++ b/metric.sh @@ -0,0 +1,16 @@ +#!/bin/sh +# Helper de metricas para conky_widget — imprime UN valor "pelado" (sin etiquetas). +# Portado del widget previo. Toda la logica con comillas/awk vive aqui para no +# romper el parser de conky ni el de widget.lua. +case "$1" in + gpu_util) nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits ;; + gpu_temp) nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits ;; + gpu_memp) nvidia-smi --query-gpu=memory.used,memory.total --format=csv,noheader,nounits \ + | awk -F', ' '{printf "%d", $1/$2*100}' ;; + gpu_memi) nvidia-smi --query-gpu=memory.used,memory.total --format=csv,noheader,nounits \ + | awk -F', ' '{printf "%d/%d MB", $1, $2}' ;; + cpu_temp) for h in /sys/class/hwmon/hwmon*; do + [ "$(cat "$h/name" 2>/dev/null)" = coretemp ] && { cat "$h/temp1_input"; break; } + done | awk '{printf "%d", $1/1000}' ;; + *) echo 0 ;; +esac diff --git a/service/install-capture.sh b/service/install-capture.sh new file mode 100755 index 0000000..135bece --- /dev/null +++ b/service/install-capture.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Instala el servicio systemd packet-capture (dumpcap ring buffer ~10 min). +# Crea el directorio de captura propiedad del usuario, copia el unit, lo activa +# y muestra su estado. Requiere privilegios sudo (los pedirá si no están en caché). +# +# Uso: install-capture.sh [interfaz] (por defecto enp5s0) +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +UNIT_SRC="$SCRIPT_DIR/packet-capture.service" +UNIT_DST="/etc/systemd/system/packet-capture.service" +CAP_DIR="/var/log/pktcap" +USER_NAME="${SUDO_USER:-$USER}" +IFACE="${1:-enp5s0}" + +echo "==> packet-capture install (interfaz: $IFACE, usuario: $USER_NAME)" + +if ! command -v dumpcap >/dev/null 2>&1; then + echo "ERROR: dumpcap no instalado. Instalar: sudo apt install wireshark" >&2 + exit 1 +fi + +# 1. Directorio de captura, propiedad del usuario que ejecuta dumpcap +sudo mkdir -p "$CAP_DIR" +sudo chown "$USER_NAME:$USER_NAME" "$CAP_DIR" +sudo chmod 750 "$CAP_DIR" +echo " dir: $CAP_DIR (owner $USER_NAME)" + +# 2. Instalar el unit, ajustando interfaz y usuario +sudo cp "$UNIT_SRC" "$UNIT_DST" +sudo sed -i \ + -e "s|-i enp5s0|-i $IFACE|" \ + -e "s|^User=.*|User=$USER_NAME|" \ + -e "s|^Group=.*|Group=$USER_NAME|" \ + "$UNIT_DST" +echo " unit: $UNIT_DST" + +# 3. Activar y arrancar +sudo systemctl daemon-reload +sudo systemctl enable --now packet-capture.service +sleep 2 + +echo "==> Estado:" +sudo systemctl --no-pager --full status packet-capture.service | head -12 || true +echo "==> Capturas en $CAP_DIR:" +ls -lh "$CAP_DIR" 2>/dev/null | tail -3 || true +echo "==> Parar: sudo systemctl stop packet-capture.service" +echo "==> Logs: journalctl -u packet-capture.service -f" diff --git a/service/packet-capture.service b/service/packet-capture.service new file mode 100644 index 0000000..f447345 --- /dev/null +++ b/service/packet-capture.service @@ -0,0 +1,26 @@ +[Unit] +Description=Packet capture ring buffer (~10 min, dumpcap) for conky_widget +Documentation=https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/dataforge/conky_widget +After=network-online.target +Wants=network-online.target + +[Service] +# dumpcap escribe un ring buffer rotativo: 10 archivos de hasta 60s o 50MB cada +# uno, lo que ocurra antes. Resultado: siempre los ultimos ~10 minutos de +# trafico en disco (tope ~500MB para no llenar el disco con descargas grandes). +# +# Variantes utiles (editar la linea ExecStart): +# - Solo cabeceras (mucho mas ligero): añadir -s 96 +# - Ver dominios (SNI/DNS) sin payload: añadir -s 320 +# - Filtrar una IP/puerto: añadir -f "host 1.2.3.4" +Type=exec +User=enmanuel +Group=enmanuel +SupplementaryGroups=wireshark +ExecStart=/usr/bin/dumpcap -i enp5s0 -b duration:60 -b files:10 -b filesize:51200 -w /var/log/pktcap/cap.pcapng +Restart=always +RestartSec=3 +Nice=10 + +[Install] +WantedBy=multi-user.target