Skip to content
API Documentation
BastionGuard™
BastionGuard-usbd.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 <systemd/sd-bus.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 sd_bus *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 sd_bus_message *m = nullptr;
105 sd_bus_message_new_signal(bus, &m,
106 "/org/BastionGuard/USBD",
107 "org.BastionGuard.USBD",
108 "DeviceAdded");
109
110 sd_bus_message_append(m, "sss",
111 dev.name.c_str(),
112 dev.devnode.c_str(),
113 dev.serial.c_str());
114
115 sd_bus_send(bus, m, nullptr);
116 sd_bus_message_unref(m);
117}
118
119void send_device_removed(const std::string &devnode)
120{
121 if (!bus) return;
122
123 sd_bus_message *m = nullptr;
124 sd_bus_message_new_signal(bus, &m,
125 "/org/BastionGuard/USBD",
126 "org.BastionGuard.USBD",
127 "DeviceRemoved");
128
129 sd_bus_message_append(m, "s", devnode.c_str());
130
131 sd_bus_send(bus, m, nullptr);
132 sd_bus_message_unref(m);
133}
134
135std::vector<USBDevice> detect_usb_devices()
136{
137 std::vector<USBDevice> result;
138
139 struct udev *udev = udev_new();
140 if (!udev) return result;
141
142 struct udev_enumerate *enumerate = udev_enumerate_new(udev);
143 if (!enumerate) { udev_unref(udev); return result; }
144
145 udev_enumerate_add_match_subsystem(enumerate, "block");
146 udev_enumerate_scan_devices(enumerate);
147
148 struct udev_list_entry *entries = udev_enumerate_get_list_entry(enumerate);
149 struct udev_list_entry *entry;
150
151 udev_list_entry_foreach(entry, entries)
152 {
153 const char *path = udev_list_entry_get_name(entry);
154 struct udev_device *dev = udev_device_new_from_syspath(udev, path);
155 if (!dev) continue;
156 const char *devtype = udev_device_get_devtype(dev);
157 if (!devtype || std::string(devtype) != "disk") {
158 udev_device_unref(dev);
159 continue;
160 }
161
162
163 struct udev_device *usb =
164 udev_device_get_parent_with_subsystem_devtype(
165 dev, "usb", "usb_device");
166
167 if (!usb) {
168 udev_device_unref(dev);
169 continue;
170 }
171
172 const char *size = udev_device_get_sysattr_value(dev, "size");
173 if (!size || std::strtoull(size, nullptr, 10) == 0) {
174 udev_device_unref(dev);
175 continue;
176 }
177
178 const char *rem = udev_device_get_sysattr_value(dev, "removable");
179 if (!rem || std::string(rem) != "1") {
180 udev_device_unref(dev);
181 continue;
182 }
183
184 USBDevice info;
185
186 const char *devnode = udev_device_get_devnode(dev);
187 if (!devnode) {
188 udev_device_unref(dev);
189 continue;
190 }
191
192 info.devnode = devnode;
193
194 const char *vendor = udev_device_get_sysattr_value(usb, "manufacturer");
195 const char *product = udev_device_get_sysattr_value(usb, "product");
196 const char *serial = udev_device_get_sysattr_value(usb, "serial");
197
198 if (vendor) info.name += vendor;
199 if (product) {
200 if (!info.name.empty()) info.name += " ";
201 info.name += product;
202 }
203
204 if (info.name.empty())
205 info.name = info.devnode;
206
207 if (serial) info.serial = serial;
208
209 result.push_back(info);
210
211 udev_device_unref(dev);
212 }
213
214 udev_enumerate_unref(enumerate);
215 udev_unref(udev);
216
217 return result;
218}
219
220
222{
223 write_log(_("Monitor USB avviato."));
224
225 std::set<std::string> known;
226
227 while (running)
228 {
229 auto list = detect_usb_devices();
230
231 for (auto &d : list) {
232 if (!known.count(d.devnode)) {
233 known.insert(d.devnode);
234 write_log(_("Collegato USB: ") + d.name + " (" + d.devnode + ")");
236 }
237 }
238
239 for (auto it = known.begin(); it != known.end(); ) {
240 bool still = false;
241 for (auto &d : list)
242 if (d.devnode == *it) still = true;
243
244 if (!still) {
245 write_log(_("Rimosso USB: ") + *it);
247 it = known.erase(it);
248 } else {
249 ++it;
250 }
251 }
252
253 std::this_thread::sleep_for(std::chrono::seconds(2));
254 }
255
256 write_log(_("Monitor USB terminato."));
257}
258
259
260void handle_signal(int) { running = false; }
261
262static bool load_lang_conf()
263{
264 const std::string lang_conf = Glib::get_home_dir() + "/.config/BastionGuard/lang.conf";
265 std::ifstream f(lang_conf);
266 if (!f.is_open())
267 return false;
268
269 std::string line;
270 bool any = false;
271
272 while (std::getline(f, line)) {
273 if (line.empty() || line[0] == '#')
274 continue;
275
276 auto pos = line.find('=');
277 if (pos == std::string::npos)
278 continue;
279
280 std::string key = line.substr(0, pos);
281 std::string val = line.substr(pos + 1);
282
283 auto ltrim = [](std::string& s){
284 s.erase(0, s.find_first_not_of(" \t\r\n"));
285 };
286 auto rtrim = [](std::string& s){
287 s.erase(s.find_last_not_of(" \t\r\n") + 1);
288 };
289 ltrim(key); rtrim(key);
290 ltrim(val); rtrim(val);
291
292 if (!key.empty() && !val.empty()) {
293 setenv(key.c_str(), val.c_str(), 1);
294 any = true;
295 }
296 }
297
298 return any;
299}
300int main()
301{
302 bool loaded = load_lang_conf();
303
304
305 std::setlocale(LC_ALL, "");
306 bindtextdomain("BastionGuard", LOCALEDIR);
307 bind_textdomain_codeset("BastionGuard", "UTF-8");
308 textdomain("BastionGuard");
309
310 if (loaded) {
311 std::cerr << _("[Lang] Config caricata da ~/.config/BastionGuard/lang.conf") << "\n";
312 } else {
313 std::cerr << _("[Lang] Nessuna config trovata, uso locale di sistema") << "\n";
314 }
315
316 std::signal(SIGINT, handle_signal);
317 std::signal(SIGTERM, handle_signal);
318
319 // DBus init
320 if (sd_bus_open_system(&bus) < 0) {
321 std::cerr << _("Errore apertura system bus.\n");
322 return 1;
323 }
324
325 if (sd_bus_request_name(bus, "org.BastionGuard.USBD", 0) < 0) {
326 std::cerr << _("Errore registrazione nome DBus.\n");
327 return 1;
328 }
329
330 write_log(_("BastionGuard-usbd avviato."));
331
332 std::thread th(monitor_loop);
333
334 while (running)
335 std::this_thread::sleep_for(std::chrono::seconds(1));
336
337 if (th.joinable()) th.join();
338
339 sd_bus_unref(bus);
340 write_log(_("BastionGuard-usbd terminato."));
341
342 return 0;
343}
#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)
void send_device_added(const USBDevice &dev)
static sd_bus * bus
#define LOCALEDIR