Skip to content
API Documentation
BastionGuard™
BastionGuardImport.TimeshiftImporter Class Reference
Inheritance diagram for BastionGuardImport.TimeshiftImporter:

Public Member Functions

TimeshiftDetectResult detect ()
bool migrate (TimeshiftDetectResult detected, bool remove_timeshift_config=false, Gtk.Window? parent_window=null)

Private Member Functions

bool load_timeshift_config (string path, TimeshiftDetectResult result)
string find_device_by_uuid (string uuid)
bool mount_backup_device (TimeshiftDetectResult result)
void unmount_backup_device (string mount_path)
void scan_snapshots (TimeshiftDetectResult result)
ImportedSnapshot parse_snapshot (string path, string name, bool is_btrfs)
void update_bgbackup_config (TimeshiftDetectResult detected)

Static Private Attributes

const string TIMESHIFT_CONF = "/etc/bastionguard-backup/bastionguard-backup.json"
const string TIMESHIFT_CONF_LEGACY = "/etc/bastionguard-backup.json"
const string BGBACKUP_CONF = "/etc/bastionguard-backup/bastionguard-backup.json"
const string BGBACKUP_CONF_DIR = "/etc/bastionguard-backup"
const string SCAN_MOUNT_POINT = "/run/bastionguard-backup/import-scan"

Detailed Description

Definition at line 73 of file TimeshiftImport.vala.

Member Function Documentation

◆ detect()

TimeshiftDetectResult BastionGuardImport.TimeshiftImporter.detect ( )
inline

Definition at line 91 of file TimeshiftImport.vala.

91 {
92 var result = new TimeshiftDetectResult();
93
94 // 1. Cerca config BastionGuard Backup
95 string conf_path = "";
96 if (FileUtils.test(TIMESHIFT_CONF, FileTest.EXISTS))
97 conf_path = TIMESHIFT_CONF;
98 else if (FileUtils.test(TIMESHIFT_CONF_LEGACY, FileTest.EXISTS))
99 conf_path = TIMESHIFT_CONF_LEGACY;
100 else {
101 log_debug("BastionGuard BackupImport: nessuna config trovata");
102 return result;
103 }
104
105 result.found = true;
106 result.config_path = conf_path;
107 log_msg("BastionGuard BackupImport: config trovata in %s".printf(conf_path));
108
109 // 2. Leggi il JSON di config BastionGuard Backup
110 if (!load_timeshift_config(conf_path, result))
111 return result;
112
113 // 3. Trova il device dal UUID
114 result.backup_device = find_device_by_uuid(result.backup_uuid);
115 if (result.backup_device.length == 0) {
116 log_error("BastionGuard BackupImport: device con UUID %s non trovato".printf(result.backup_uuid));
117 return result;
118 }
119 log_msg("BastionGuard BackupImport: device backup = %s".printf(result.backup_device));
120
121 // 4. Monta la partizione (sola lettura)
122 if (!mount_backup_device(result))
123 return result;
124
125 // 5. Determina il path snapshot
126 if (result.is_btrfs)
127 result.snapshots_path = result.mount_path + "/bastionguard-backup-btrfs/snapshots";
128 else
129 result.snapshots_path = result.mount_path + "/bastionguard-backup/snapshots";
130
131 // 6. Scansiona gli snapshot
132 if (FileUtils.test(result.snapshots_path, FileTest.IS_DIR))
133 scan_snapshots(result);
134 else
135 log_msg("BastionGuard BackupImport: dir snapshot non trovata in %s".printf(result.snapshots_path));
136
137 log_msg("BastionGuard BackupImport: trovati %d snapshot".printf(result.snapshots.size));
138 return result;
139 }
void log_msg(const std::string &msg)
void log_debug(string message)
void log_error(string message, bool highlight=false, bool is_warning=false)

References find_device_by_uuid(), load_timeshift_config(), TeeJee.Logging.log_debug(), TeeJee.Logging.log_error(), log_msg(), mount_backup_device(), scan_snapshots(), TIMESHIFT_CONF, TIMESHIFT_CONF_LEGACY, and BastionGuardImport.TimeshiftDetectResult.TimeshiftDetectResult().

◆ find_device_by_uuid()

string BastionGuardImport.TimeshiftImporter.find_device_by_uuid ( string uuid)
inlineprivate

Definition at line 292 of file TimeshiftImport.vala.

292 {
293 if (uuid.length == 0) return "";
294 string path = "/dev/disk/by-uuid/" + uuid;
295 if (FileUtils.test(path, FileTest.EXISTS)) {
296 // Risolvi il symlink
297 string out_str, err_str;
298 int status;
299 try {
300 GLib.Process.spawn_command_line_sync(
301 "readlink -f " + path,
302 out out_str, out err_str, out status);
303 } catch (GLib.SpawnError e) { log_error(e.message); }
304 if (status == 0)
305 return out_str.strip();
306 }
307 // Fallback: cerca con blkid
308 string out_str, err_str;
309 int status;
310 try {
311 GLib.Process.spawn_command_line_sync(
312 "blkid -U " + uuid,
313 out out_str, out err_str, out status);
314 } catch (GLib.SpawnError e) { log_error(e.message); }
315 if (status == 0 && out_str.strip().length > 0)
316 return out_str.strip();
317
318 return "";
319 }

References TeeJee.Logging.log_error().

Referenced by detect().

◆ load_timeshift_config()

bool BastionGuardImport.TimeshiftImporter.load_timeshift_config ( string path,
TimeshiftDetectResult result )
inlineprivate

Definition at line 265 of file TimeshiftImport.vala.

265 {
266 string raw;
267 try {
268 FileUtils.get_contents(path, out raw);
269 } catch (Error e) {
270 log_error("BastionGuard BackupImport: impossibile leggere %s: %s".printf(path, e.message));
271 return false;
272 }
273 try {
274 var parser = new Json.Parser();
275 parser.load_from_data(raw);
276 var config = parser.get_root().get_object();
277
278 result.backup_uuid = json_get_string(config, "backup_device_uuid", "");
279 result.is_btrfs = json_get_bool(config, "btrfs_mode", false);
280
281 if (result.backup_uuid.length == 0) {
282 log_error("BastionGuard BackupImport: backup_device_uuid non trovato nella config");
283 return false;
284 }
285 } catch (Error e) {
286 log_error("BastionGuard BackupImport: errore parsing config: " + e.message);
287 return false;
288 }
289 return true;
290 }
string json_get_string(Json.Object jobj, string member, string def_value)
bool json_get_bool(Json.Object jobj, string member, bool def_value)

References BastionGuardImport.TimeshiftDetectResult.backup_uuid, Error, TeeJee.JsonHelper.json_get_bool(), TeeJee.JsonHelper.json_get_string(), and TeeJee.Logging.log_error().

Referenced by detect().

◆ migrate()

bool BastionGuardImport.TimeshiftImporter.migrate ( TimeshiftDetectResult detected,
bool remove_timeshift_config = false,
Gtk.Window? parent_window = null )
inline

Definition at line 147 of file TimeshiftImport.vala.

149 {
150
151 if (!detected.found || detected.snapshots.size == 0) {
152 log_error("BastionGuard BackupImport: nulla da migrare");
153 return false;
154 }
155
156 log_msg("==> BastionGuard BackupImport: avvio migrazione...");
157
158 // Rimonta in lettura/scrittura per la migrazione
159 string out_str, err_str;
160 int status;
161 try {
162 try { GLib.Process.spawn_command_line_sync(
163 "mount -o remount,rw %s %s".printf(
164 detected.backup_device, detected.mount_path),
165 out out_str, out err_str, out status);
166 } catch (GLib.SpawnError e) { log_error(e.message); }
167 } catch (GLib.SpawnError e) { log_error("mount remount: " + e.message); return false; }
168 if (status != 0) {
169 log_error("BastionGuard BackupImport: impossibile rimontare in rw: " + err_str);
170 return false;
171 }
172
173 bool ok = true;
174
175 // Passo 1: rinomina directory sul device
176 string old_base, new_base;
177 if (detected.is_btrfs) {
178 old_base = detected.mount_path + "/bastionguard-backup-btrfs";
179 new_base = detected.mount_path + "/bastionguard-backup-btrfs";
180 } else {
181 old_base = detected.mount_path + "/bastionguard-backup";
182 new_base = detected.mount_path + "/bastionguard-backup";
183 }
184
185 if (FileUtils.test(old_base, FileTest.IS_DIR)) {
186 if (FileUtils.rename(old_base, new_base) != 0) {
187 log_error("BastionGuard BackupImport: impossibile rinominare %s → %s".printf(
188 old_base, new_base));
189 ok = false;
190 } else {
191 log_msg("BastionGuard BackupImport: rinominato %s → %s".printf(old_base, new_base));
192 // Aggiorna il path snapshot nel result
193 if (detected.is_btrfs)
194 detected.snapshots_path = new_base + "/snapshots";
195 else
196 detected.snapshots_path = new_base + "/snapshots";
197 }
198 }
199
200 // Passo 2: aggiorna info.json di ogni snapshot
201 foreach (var snap in detected.snapshots) {
202 // Aggiorna il path con il nuovo nome directory
203 snap.path = snap.path.replace(old_base, new_base);
204 string info_path = snap.path + "/info.json";
205
206 if (!FileUtils.test(info_path, FileTest.EXISTS)) continue;
207
208 string raw;
209 try {
210 FileUtils.get_contents(info_path, out raw);
211 } catch { continue; }
212
213 try {
214 var parser = new Json.Parser();
215 parser.load_from_data(raw);
216 var config = parser.get_root().get_object();
217
218 // Aggiorna il campo app-version con BastionGuard
219 config.set_string_member("app-version",
220 "bastionguard-backup-%s".printf(Constants.VERSION));
221 // Mantieni il campo originale per tracciabilità
222 config.set_string_member("imported-from", "bastionguard-backup");
223
224 // Serializza e riscrivi
225 var gen = new Json.Generator();
226 gen.set_root(new Json.Node.alloc().init_object(config));
227 gen.set_pretty(true);
228 string new_json = gen.to_data(null);
229 FileUtils.set_contents(info_path, new_json);
230
231 } catch (Error e) {
232 log_error("BastionGuard BackupImport: errore aggiornamento %s: %s".printf(
233 info_path, e.message));
234 }
235 }
236
237 // Passo 3: aggiorna/crea config BastionGuard Backup
238 update_bgbackup_config(detected);
239
240 // Passo 4: rimuovi config BastionGuard Backup (opzionale) ─
241 if (remove_timeshift_config) {
242 try {
243 if (FileUtils.test(TIMESHIFT_CONF, FileTest.EXISTS))
244 FileUtils.remove(TIMESHIFT_CONF);
245 if (FileUtils.test(TIMESHIFT_CONF_LEGACY, FileTest.EXISTS))
246 FileUtils.remove(TIMESHIFT_CONF_LEGACY);
247 log_msg("BastionGuard BackupImport: config BastionGuard Backup rimossa");
248 } catch (Error e) {
249 log_error("BastionGuard BackupImport: impossibile rimuovere config: " + e.message);
250 }
251 }
252
253 // Passo 5: smonta
254 unmount_backup_device(detected.mount_path);
255
256 log_msg("==> BastionGuard BackupImport: migrazione completata. " +
257 "%d snapshot importati.".printf(detected.snapshots.size));
258 return ok;
259 }

References BastionGuardImport.TimeshiftDetectResult.backup_device, Error, BastionGuardImport.TimeshiftDetectResult.found, BastionGuardImport.TimeshiftDetectResult.is_btrfs, TeeJee.Logging.log_error(), log_msg(), BastionGuardImport.TimeshiftDetectResult.mount_path, BastionGuardImport.TimeshiftDetectResult.snapshots, TIMESHIFT_CONF, TIMESHIFT_CONF_LEGACY, unmount_backup_device(), and update_bgbackup_config().

◆ mount_backup_device()

bool BastionGuardImport.TimeshiftImporter.mount_backup_device ( TimeshiftDetectResult result)
inlineprivate

Definition at line 321 of file TimeshiftImport.vala.

321 {
322 dir_create(SCAN_MOUNT_POINT);
323 result.mount_path = SCAN_MOUNT_POINT;
324
325 string out_str, err_str;
326 int status;
327 try {
328 try { GLib.Process.spawn_command_line_sync(
329 "mount -o ro %s %s".printf(result.backup_device, SCAN_MOUNT_POINT),
330 out out_str, out err_str, out status);
331 } catch (GLib.SpawnError e) { log_error(e.message); }
332 } catch (GLib.SpawnError e) { log_error("mount: " + e.message); return false; }
333
334 if (status != 0) {
335 log_error("BastionGuard BackupImport: mount fallito: " + err_str);
336 return false;
337 }
338 return true;
339 }
bool dir_create(string dir_path, bool show_message=false)

References BastionGuardImport.TimeshiftDetectResult.backup_device, TeeJee.FileSystem.dir_create(), TeeJee.Logging.log_error(), and SCAN_MOUNT_POINT.

Referenced by detect().

◆ parse_snapshot()

ImportedSnapshot BastionGuardImport.TimeshiftImporter.parse_snapshot ( string path,
string name,
bool is_btrfs )
inlineprivate

Definition at line 375 of file TimeshiftImport.vala.

375 {
376 var snap = new ImportedSnapshot();
377 snap.name = name;
378 snap.path = path;
379 snap.is_btrfs = is_btrfs;
380
381 string info_path = path + "/info.json";
382 if (!FileUtils.test(info_path, FileTest.EXISTS)) {
383 log_debug("BastionGuard BackupImport: info.json non trovato in " + path);
384 return snap;
385 }
386
387 string raw;
388 try {
389 FileUtils.get_contents(info_path, out raw);
390 } catch { return snap; }
391
392 try {
393 var parser = new Json.Parser();
394 parser.load_from_data(raw);
395 var config = parser.get_root().get_object();
396
397 snap.sys_uuid = json_get_string(config, "sys-uuid", "");
398 snap.sys_distro = json_get_string(config, "sys-distro", "");
399 snap.tags = json_get_string(config, "tags", "");
400 snap.description = json_get_string(config, "comments", "");
401
402 string created_str = json_get_string(config, "created", "0");
403 snap.date_unix = int64.parse(created_str);
404 var dt = new DateTime.from_unix_utc(snap.date_unix).to_local();
405 snap.date = dt.format("%Y-%m-%d %H:%M:%S");
406
407 snap.valid = true;
408 } catch (Error e) {
409 log_error("BastionGuard BackupImport: errore parsing info.json in %s: %s".printf(
410 path, e.message));
411 }
412 return snap;
413 }

References Error, TeeJee.JsonHelper.json_get_string(), TeeJee.Logging.log_debug(), and TeeJee.Logging.log_error().

Referenced by scan_snapshots().

◆ scan_snapshots()

void BastionGuardImport.TimeshiftImporter.scan_snapshots ( TimeshiftDetectResult result)
inlineprivate

Definition at line 354 of file TimeshiftImport.vala.

354 {
355 try {
356 var dir = Dir.open(result.snapshots_path);
357 string? name;
358 while ((name = dir.read_name()) != null) {
359 string snap_path = result.snapshots_path + "/" + name;
360 if (!FileUtils.test(snap_path, FileTest.IS_DIR)) continue;
361
362 var snap = parse_snapshot(snap_path, name, result.is_btrfs);
363 if (snap.valid)
364 result.snapshots.add(snap);
365 }
366 // Ordina per data (più recente prima)
367 result.snapshots.sort((a, b) => {
368 return (int)(b.date_unix - a.date_unix);
369 });
370 } catch (Error e) {
371 log_error("BastionGuard BackupImport: errore scansione snapshot: " + e.message);
372 }
373 }

References Error, BastionGuardImport.TimeshiftDetectResult.is_btrfs, TeeJee.Logging.log_error(), parse_snapshot(), BastionGuardImport.TimeshiftDetectResult.snapshots, and BastionGuardImport.TimeshiftDetectResult.snapshots_path.

Referenced by detect().

◆ unmount_backup_device()

void BastionGuardImport.TimeshiftImporter.unmount_backup_device ( string mount_path)
inlineprivate

Definition at line 341 of file TimeshiftImport.vala.

341 {
342 string out_str, err_str;
343 int status;
344 try {
345 try { GLib.Process.spawn_command_line_sync(
346 "umount %s".printf(mount_path),
347 out out_str, out err_str, out status);
348 } catch (GLib.SpawnError e) { log_error(e.message); }
349 } catch (GLib.SpawnError e) { log_error("umount: " + e.message); }
350 if (status != 0)
351 log_error("BastionGuard BackupImport: umount fallito: " + err_str);
352 }

References TeeJee.Logging.log_error().

Referenced by migrate().

◆ update_bgbackup_config()

void BastionGuardImport.TimeshiftImporter.update_bgbackup_config ( TimeshiftDetectResult detected)
inlineprivate

Definition at line 415 of file TimeshiftImport.vala.

415 {
416 // Crea dir config se non esiste
417 dir_create(BGBACKUP_CONF_DIR);
418
419 // Leggi config esistente o crea vuota
420 var config = new Json.Object();
421 if (FileUtils.test(BGBACKUP_CONF, FileTest.EXISTS)) {
422 string raw;
423 try {
424 FileUtils.get_contents(BGBACKUP_CONF, out raw);
425 var parser = new Json.Parser();
426 parser.load_from_data(raw);
427 config = parser.get_root().get_object();
428 } catch {}
429 }
430
431 // Aggiorna con i dati importati da BastionGuard Backup
432 config.set_string_member("backup_device_uuid", detected.backup_uuid);
433 config.set_string_member("btrfs_mode", detected.is_btrfs.to_string());
434 config.set_string_member("imported_from_timeshift", "true");
435
436 // Scrivi
437 try {
438 var gen = new Json.Generator();
439 gen.set_root(new Json.Node.alloc().init_object(config));
440 gen.set_pretty(true);
441 string json_str = gen.to_data(null);
442 FileUtils.set_contents(BGBACKUP_CONF, json_str);
443 log_msg("BastionGuard BackupImport: config BastionGuard aggiornata in " + BGBACKUP_CONF);
444 } catch (Error e) {
445 log_error("BastionGuard BackupImport: impossibile scrivere config: " + e.message);
446 }
447 }

References BastionGuardImport.TimeshiftDetectResult.backup_uuid, BGBACKUP_CONF, BGBACKUP_CONF_DIR, TeeJee.FileSystem.dir_create(), Error, BastionGuardImport.TimeshiftDetectResult.is_btrfs, TeeJee.Logging.log_error(), and log_msg().

Referenced by migrate().

Member Data Documentation

◆ BGBACKUP_CONF

const string BastionGuardImport.TimeshiftImporter.BGBACKUP_CONF = "/etc/bastionguard-backup/bastionguard-backup.json"
staticprivate

Definition at line 80 of file TimeshiftImport.vala.

Referenced by update_bgbackup_config().

◆ BGBACKUP_CONF_DIR

const string BastionGuardImport.TimeshiftImporter.BGBACKUP_CONF_DIR = "/etc/bastionguard-backup"
staticprivate

Definition at line 81 of file TimeshiftImport.vala.

Referenced by update_bgbackup_config().

◆ SCAN_MOUNT_POINT

const string BastionGuardImport.TimeshiftImporter.SCAN_MOUNT_POINT = "/run/bastionguard-backup/import-scan"
staticprivate

Definition at line 84 of file TimeshiftImport.vala.

Referenced by mount_backup_device().

◆ TIMESHIFT_CONF

const string BastionGuardImport.TimeshiftImporter.TIMESHIFT_CONF = "/etc/bastionguard-backup/bastionguard-backup.json"
staticprivate

Definition at line 76 of file TimeshiftImport.vala.

Referenced by detect(), and migrate().

◆ TIMESHIFT_CONF_LEGACY

const string BastionGuardImport.TimeshiftImporter.TIMESHIFT_CONF_LEGACY = "/etc/bastionguard-backup.json"
staticprivate

Definition at line 77 of file TimeshiftImport.vala.

Referenced by detect(), and migrate().


The documentation for this class was generated from the following file: