Skip to content
API Documentation
BastionGuard™
BastionGuard-usbd-gdbus.cpp
Go to the documentation of this file.
1/*
2 * BastionGuard™
3 * Copyright (C) 2025–2026 Calogero Scarnà
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 *
17 * BastionGuard™ is a trademark of Calogero Scarnà.
18 * The BastionGuard™ name and branding are not licensed under the GPL.
19 */
20
21#include "Resource.hpp"
22#include <libudev.h>
23#include <filesystem>
24#include <fstream>
25#include <iostream>
26#include <thread>
27#include <set>
28#include <atomic>
29#include <mutex>
30#include <chrono>
31#include <csignal>
32#include <algorithm>
33#include <cstdlib>
34#include <unistd.h>
35#include <gio/gio.h>
36#include <glibmm/miscutils.h>
37#include <glibmm/i18n.h>
38
39namespace fs = std::filesystem;
40
41static std::atomic<bool> running{true};
42static std::mutex log_mutex;
43static GDBusConnection *bus = nullptr;
44
45void write_log(const std::string& msg)
46{
47 std::lock_guard<std::mutex> lock(log_mutex);
48
49 try {
50 std::string base_dir;
51
52 if (geteuid() == 0)
53 base_dir = "/var/log/BastionGuard";
54 else
55 base_dir = Glib::get_home_dir() + "/.local/share/BastionGuard/logs";
56
57 if (!fs::exists(base_dir))
58 fs::create_directories(base_dir);
59
60 auto now = std::time(nullptr);
61 std::tm tm{};
62 localtime_r(&now, &tm); // thread-safe
63
64 char datebuf[16];
65 std::strftime(datebuf, sizeof(datebuf), "%Y-%m-%d", &tm);
66
67 std::string log_path = base_dir + "/usbd_" + datebuf + ".log";
68 std::ofstream f(log_path, std::ios::app);
69 if (!f.is_open()) return;
70
71 char timebuf[16];
72 std::strftime(timebuf, sizeof(timebuf), "%H:%M:%S", &tm);
73
74 f << "[" << timebuf << "] " << msg << std::endl;
75
76 const auto sys_now = std::chrono::system_clock::now();
77 const auto limit = std::chrono::hours(24 * 7);
78
79 for (const auto& entry : fs::directory_iterator(base_dir)) {
80 if (!entry.is_regular_file()) continue;
81
82 auto ftime = entry.last_write_time();
83 auto sys_time = std::chrono::time_point_cast<std::chrono::system_clock::duration>(
84 ftime - fs::file_time_type::clock::now() + sys_now
85 );
86
87 if (sys_now - sys_time > limit)
88 fs::remove(entry.path());
89 }
90
91 } catch (...) {}
92}
93
94struct USBDevice {
95 std::string name;
96 std::string devnode;
97 std::string serial;
98};
99
101{
102 if (!bus) return;
103
104 GError* error = nullptr;
105 g_dbus_connection_emit_signal(
106 bus,
107 nullptr,
108 "/org/BastionGuard/USBD",
109 "org.BastionGuard.USBD",
110 "DeviceAdded",
111 g_variant_new("(sss)",
112 dev.name.c_str(),
113 dev.devnode.c_str(),
114 dev.serial.c_str()),
115 &error);
116
117 if (error) {
118 write_log(std::string(_("Errore invio segnale DeviceAdded: ")) + error->message);
119 g_error_free(error);
120 }
121}
122
123void send_device_removed(const std::string &devnode)
124{
125 if (!bus) return;
126
127 GError* error = nullptr;
128 g_dbus_connection_emit_signal(
129 bus,
130 nullptr,
131 "/org/BastionGuard/USBD",
132 "org.BastionGuard.USBD",
133 "DeviceRemoved",
134 g_variant_new("(s)", devnode.c_str()),
135 &error);
136
137 if (error) {
138 write_log(std::string(_("Errore invio segnale DeviceRemoved: ")) + error->message);
139 g_error_free(error);
140 }
141}
142
143std::vector<USBDevice> detect_usb_devices()
144{
145 std::vector<USBDevice> result;
146
147 struct udev *udev = udev_new();
148 if (!udev) return result;
149
150 struct udev_enumerate *enumerate = udev_enumerate_new(udev);
151 if (!enumerate) { udev_unref(udev); return result; }
152
153 udev_enumerate_add_match_subsystem(enumerate, "block");
154 udev_enumerate_scan_devices(enumerate);
155
156 struct udev_list_entry *entries = udev_enumerate_get_list_entry(enumerate);
157 struct udev_list_entry *entry;
158
159 udev_list_entry_foreach(entry, entries)
160 {
161 const char *path = udev_list_entry_get_name(entry);
162 struct udev_device *dev = udev_device_new_from_syspath(udev, path);
163 if (!dev) continue;
164 const char *devtype = udev_device_get_devtype(dev);
165 if (!devtype || std::string(devtype) != "disk") {
166 udev_device_unref(dev);
167 continue;
168 }
169
170
171 struct udev_device *usb =
172 udev_device_get_parent_with_subsystem_devtype(
173 dev, "usb", "usb_device");
174
175 if (!usb) {
176 udev_device_unref(dev);
177 continue;
178 }
179
180 const char *size = udev_device_get_sysattr_value(dev, "size");
181 if (!size || std::strtoull(size, nullptr, 10) == 0) {
182 udev_device_unref(dev);
183 continue;
184 }
185
186 const char *rem = udev_device_get_sysattr_value(dev, "removable");
187 if (!rem || std::string(rem) != "1") {
188 udev_device_unref(dev);
189 continue;
190 }
191
192 USBDevice info;
193
194 const char *devnode = udev_device_get_devnode(dev);
195 if (!devnode) {
196 udev_device_unref(dev);
197 continue;
198 }
199
200 info.devnode = devnode;
201
202 const char *vendor = udev_device_get_sysattr_value(usb, "manufacturer");
203 const char *product = udev_device_get_sysattr_value(usb, "product");
204 const char *serial = udev_device_get_sysattr_value(usb, "serial");
205
206 if (vendor) info.name += vendor;
207 if (product) {
208 if (!info.name.empty()) info.name += " ";
209 info.name += product;
210 }
211
212 if (info.name.empty())
213 info.name = info.devnode;
214
215 if (serial) info.serial = serial;
216
217 result.push_back(info);
218
219 udev_device_unref(dev);
220 }
221
222 udev_enumerate_unref(enumerate);
223 udev_unref(udev);
224
225 return result;
226}
227
228
230{
231 write_log(_("Monitor USB avviato."));
232
233 std::set<std::string> known;
234
235 while (running)
236 {
237 auto list = detect_usb_devices();
238
239 for (auto &d : list) {
240 if (!known.count(d.devnode)) {
241 known.insert(d.devnode);
242 write_log(_("Collegato USB: ") + d.name + " (" + d.devnode + ")");
244 }
245 }
246
247 for (auto it = known.begin(); it != known.end(); ) {
248 bool still = false;
249 for (auto &d : list)
250 if (d.devnode == *it) still = true;
251
252 if (!still) {
253 write_log(_("Rimosso USB: ") + *it);
255 it = known.erase(it);
256 } else {
257 ++it;
258 }
259 }
260
261 std::this_thread::sleep_for(std::chrono::seconds(2));
262 }
263
264 write_log(_("Monitor USB terminato."));
265}
266
267
268void handle_signal(int) { running = false; }
269
270static bool load_lang_conf()
271{
272 const std::string lang_conf = Glib::get_home_dir() + "/.config/BastionGuard/lang.conf";
273 std::ifstream f(lang_conf);
274 if (!f.is_open())
275 return false;
276
277 std::string line;
278 bool any = false;
279
280 while (std::getline(f, line)) {
281 if (line.empty() || line[0] == '#')
282 continue;
283
284 auto pos = line.find('=');
285 if (pos == std::string::npos)
286 continue;
287
288 std::string key = line.substr(0, pos);
289 std::string val = line.substr(pos + 1);
290
291 auto ltrim = [](std::string& s){
292 s.erase(0, s.find_first_not_of(" \t\r\n"));
293 };
294 auto rtrim = [](std::string& s){
295 s.erase(s.find_last_not_of(" \t\r\n") + 1);
296 };
297 ltrim(key); rtrim(key);
298 ltrim(val); rtrim(val);
299
300 if (!key.empty() && !val.empty()) {
301 setenv(key.c_str(), val.c_str(), 1);
302 any = true;
303 }
304 }
305
306 return any;
307}
308int main()
309{
310 bool loaded = load_lang_conf();
311
312
313 std::setlocale(LC_ALL, "");
314 bindtextdomain("BastionGuard", LOCALEDIR);
315 bind_textdomain_codeset("BastionGuard", "UTF-8");
316 textdomain("BastionGuard");
317
318 if (loaded) {
319 std::cerr << _("[Lang] Config caricata da ~/.config/BastionGuard/lang.conf") << "\n";
320 } else {
321 std::cerr << _("[Lang] Nessuna config trovata, uso locale di sistema") << "\n";
322 }
323
324 std::signal(SIGINT, handle_signal);
325 std::signal(SIGTERM, handle_signal);
326
327 // D-Bus init through GLib/GIO, independent from the init system.
328 GError* dbus_error = nullptr;
329 bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &dbus_error);
330 if (!bus) {
331 std::cerr << _("Errore apertura system bus: ")
332 << (dbus_error ? dbus_error->message : _("errore sconosciuto"))
333 << "\n";
334 if (dbus_error) g_error_free(dbus_error);
335 return 1;
336 }
337
338 GVariant* request_reply = g_dbus_connection_call_sync(
339 bus,
340 "org.freedesktop.DBus",
341 "/org/freedesktop/DBus",
342 "org.freedesktop.DBus",
343 "RequestName",
344 g_variant_new("(su)", "org.BastionGuard.USBD", 0u),
345 G_VARIANT_TYPE("(u)"),
346 G_DBUS_CALL_FLAGS_NONE,
347 -1,
348 nullptr,
349 &dbus_error);
350
351 if (!request_reply) {
352 std::cerr << _("Errore registrazione nome DBus: ")
353 << (dbus_error ? dbus_error->message : _("errore sconosciuto"))
354 << "\n";
355 if (dbus_error) g_error_free(dbus_error);
356 g_object_unref(bus);
357 bus = nullptr;
358 return 1;
359 }
360
361 guint32 request_result = 0;
362 g_variant_get(request_reply, "(u)", &request_result);
363 g_variant_unref(request_reply);
364 if (request_result != 1u && request_result != 4u) {
365 std::cerr << _("Nome DBus org.BastionGuard.USBD già occupato.\n");
366 g_object_unref(bus);
367 bus = nullptr;
368 return 1;
369 }
370
371 write_log(_("BastionGuard-usbd avviato."));
372
373 std::thread th(monitor_loop);
374
375 while (running)
376 std::this_thread::sleep_for(std::chrono::seconds(1));
377
378 if (th.joinable()) th.join();
379
380 g_object_unref(bus);
381 bus = nullptr;
382 write_log(_("BastionGuard-usbd terminato."));
383
384 return 0;
385}
#define _(s)
void handle_signal(int)
static std::mutex log_mutex
void monitor_loop()
void write_log(const std::string &msg)
static std::atomic< bool > running
int main(int argc, char *argv[])
static bool load_lang_conf()
std::vector< USBDevice > detect_usb_devices()
void send_device_removed(const std::string &devnode)
static GDBusConnection * bus
void send_device_added(const USBDevice &dev)
#define LOCALEDIR