39 std::ostringstream ss;
41 if (bytes >= 1024ULL * 1024 * 1024) {
42 ss << std::fixed << std::setprecision(1)
43 << (double)bytes / (1024.0 * 1024 * 1024);
44 return Glib::ustring::compose(
_(
"%1 GB"), ss.str());
47 if (bytes >= 1024ULL * 1024) {
48 ss << std::fixed << std::setprecision(0)
49 << (double)bytes / (1024.0 * 1024);
50 return Glib::ustring::compose(
_(
"%1 MB"), ss.str());
54 return Glib::ustring::compose(
_(
"%1 KB"), ss.str());
62: Gtk::Box(Gtk::Orientation::VERTICAL, 0)
70 auto* columns = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 0);
72 auto* leftColumn = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 0);
73 leftColumn->set_hexpand(
true);
77 auto* rightColumn = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 0);
78 rightColumn->set_size_request(340, -1);
81 columns->append(*leftColumn);
82 columns->append(*rightColumn);
86 columns->set_vexpand(
true);
104 auto box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 16);
105 box->set_margin_top(20);
106 box->set_margin_bottom(20);
107 box->set_margin_start(20);
108 box->set_margin_end(20);
110 auto label = Gtk::make_managed<Gtk::Label>();
112 _(
"<b>Gestione snapshot BastionGuard</b>\n\n"
113 "Per creare, eliminare e ripristinare snapshot "
114 "aprire l'interfaccia grafica dedicata.\n"));
115 label->set_wrap(
true);
116 label->set_halign(Gtk::Align::START);
119 auto launchButton = Gtk::make_managed<Gtk::Button>();
120 launchButton->set_label(
_(
"🛡 Apri Gestione Backup"));
121 launchButton->get_style_context()->add_class(
"suggested-action");
122 launchButton->signal_clicked().connect([
this]() {
125 logSafe(Glib::ustring(
_(
"Avvio gestore backup BastionGuard...\n")).raw());
127 logSafe(Glib::ustring(
_(
"Errore durante l'avvio del gestore backup BastionGuard.\n")).raw());
130 box->append(*launchButton);
141 auto box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 14);
142 box->set_margin_top(16);
143 box->set_margin_bottom(16);
144 box->set_margin_start(16);
145 box->set_margin_end(16);
148 auto desc = Gtk::make_managed<Gtk::Label>();
150 _(
"<b>BastionGuard Recovery Environment</b>\n\n"
151 "Installa una voce GRUB per avviare un sistema Linux "
152 "minimale isolato e ripristinare snapshot in sicurezza.\n\n"
153 "<b>⚠ Requisiti:</b>\n"
154 " • Almeno <b>2 GB</b> di spazio libero sulla partizione <tt>/boot</tt>\n"
155 " • Connessione internet per il download dei pacchetti Debian\n"
156 " • La build può richiedere 10–20 minuti"));
157 desc->set_wrap(
true);
158 desc->set_halign(Gtk::Align::START);
162 auto card = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 6);
163 card->get_style_context()->add_class(
"card");
164 card->set_margin_top(4);
189 auto btnBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
208 box->append(*btnBox);
219 auto frame = Gtk::make_managed<Gtk::Frame>(
_(
"Log operazioni"));
224 logView.set_wrap_mode(Gtk::WrapMode::WORD_CHAR);
226 auto scroll = Gtk::make_managed<Gtk::ScrolledWindow>();
230 scroll->set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
231 scroll->set_vexpand(
true);
233 frame->set_child(*scroll);
234 frame->set_vexpand(
true);
235 frame->set_size_request(340, -1);
236 frame->set_margin_top(10);
237 frame->set_margin_bottom(10);
238 frame->set_margin_end(12);
239 frame->set_margin_start(6);
250 bool kernel_ok = Glib::file_test(
RECOVERY_KERNEL, Glib::FileTest::EXISTS);
251 bool grub_ok = Glib::file_test(
GRUB_ENTRY, Glib::FileTest::EXISTS);
253 std::string status_markup;
255 if (squashfs_ok && kernel_ok) {
256 std::string size_str =
"?";
259 ->query_info(
"standard::size");
260 size_str =
fmtSize((guint64)info->get_size());
263 status_markup = Glib::ustring::compose(
264 _(
"<span foreground='#22c55e'>✅ <b>Immagine recovery presente</b></span>\n"
265 "<small><tt>%1</tt>\n"
266 "filesystem.squashfs: %2\n"
267 "Voce GRUB: %3</small>"),
270 grub_ok ?
_(
"✅ installata") :
_(
"⚠ non installata")
274 _(
"<span foreground='#f59e0b'>⚠ <b>Immagine recovery non trovata</b></span>\n"
275 "<small>Usare \"Build filesystem squashfs\" per crearla.</small>");
282 std::string space_markup;
284 const char* check_path = Glib::file_test(
"/boot", Glib::FileTest::IS_DIR)
287 if (statvfs(check_path, &vfs) == 0) {
288 guint64 free_bytes = (guint64)vfs.f_bavail * vfs.f_frsize;
289 std::string free_str =
fmtSize(free_bytes);
292 space_markup = Glib::ustring::compose(
293 _(
"Spazio libero su <tt>%1</tt>: <b>%2</b> %3"),
297 ?
_(
"<span foreground='#22c55e'>✅ sufficiente</span>")
298 :
_(
"<span foreground='#ef4444'>❌ insufficiente (minimo 2 GB)</span>")
301 space_markup =
_(
"Spazio disponibile: <i>non rilevato</i>");
376 logSafe(Glib::ustring(
_(
"⚠ Operazione già in corso.\n")).raw());
389 pulseTimer = Glib::signal_timeout().connect([
this]() ->
bool {
396 std::vector<std::string> argv = {
"/bin/bash",
"-c", cmd };
402 Glib::spawn_async_with_pipes(
405 Glib::SpawnFlags::SEARCH_PATH | Glib::SpawnFlags::DO_NOT_REAP_CHILD,
406 sigc::slot<
void()>(),
412 }
catch (
const Glib::Error& e) {
414 Glib::ustring::compose(
_(
"Errore avvio processo: %1\n"), e.what()).raw()
426 auto stdout_ch = Glib::IOChannel::create_from_fd(stdout_fd);
427 auto stderr_ch = Glib::IOChannel::create_from_fd(stderr_fd);
428 stdout_ch->set_encoding(
"");
429 stderr_ch->set_encoding(
"");
430 stdout_ch->set_flags(Glib::IOFlags::NONBLOCK);
431 stderr_ch->set_flags(Glib::IOFlags::NONBLOCK);
440 auto stdout_conn = std::make_shared<sigc::connection>();
441 auto stderr_conn = std::make_shared<sigc::connection>();
446 auto drain_channel = [
this, alive](Glib::RefPtr<Glib::IOChannel> ch,
bool is_stderr) {
451 auto st = ch->read_line(line);
452 if (st != Glib::IOStatus::NORMAL || line.empty())
break;
455 logSafe(Glib::ustring::compose(
_(
"⚠ %1"), line.raw()).raw());
465 *stdout_conn = Glib::signal_io().connect(
466 [
this, alive, stdout_ch, stdout_conn, drain_channel](Glib::IOCondition cond) ->
bool {
468 if ((cond & Glib::IOCondition::IO_IN) != Glib::IOCondition()) {
471 auto status = stdout_ch->read_line(line);
472 if (status == Glib::IOStatus::NORMAL && !line.empty()) {
482 if ((cond & (Glib::IOCondition::IO_HUP | Glib::IOCondition::IO_ERR))
483 != Glib::IOCondition()) {
484 drain_channel(stdout_ch,
false);
489 stdout_fd, Glib::IOCondition::IO_IN | Glib::IOCondition::IO_HUP | Glib::IOCondition::IO_ERR
493 *stderr_conn = Glib::signal_io().connect(
494 [
this, alive, stderr_ch, stderr_conn, drain_channel](Glib::IOCondition cond) ->
bool {
495 if ((cond & Glib::IOCondition::IO_IN) != Glib::IOCondition()) {
498 auto status = stderr_ch->read_line(line);
499 if (status == Glib::IOStatus::NORMAL && !line.empty()) {
501 logSafe(Glib::ustring::compose(
_(
"⚠ %1"), line.raw()).raw());
506 if ((cond & (Glib::IOCondition::IO_HUP | Glib::IOCondition::IO_ERR))
507 != Glib::IOCondition()) {
508 drain_channel(stderr_ch,
true);
513 stderr_fd, Glib::IOCondition::IO_IN | Glib::IOCondition::IO_HUP | Glib::IOCondition::IO_ERR
517 Glib::signal_child_watch().connect(
518 [
this, alive, show_success, stdout_ch, stderr_ch, stdout_conn, stderr_conn, drain_channel]
519 (Glib::Pid p,
int status) {
523 if (stdout_conn && stdout_conn->connected()) stdout_conn->disconnect();
524 if (stderr_conn && stderr_conn->connected()) stderr_conn->disconnect();
527 drain_channel(stdout_ch,
false);
528 drain_channel(stderr_ch,
true);
530 try { stdout_ch->close(); }
catch (...) {}
531 try { stderr_ch->close(); }
catch (...) {}
532 Glib::spawn_close_pid(p);
542 bool ok = (WIFEXITED(status) && WEXITSTATUS(status) == 0);
545 logSafe(Glib::ustring(
_(
"==> Operazione completata con successo.\n")).raw());
547 setResult(
true,
_(
"Completato — disponibile al prossimo avvio"));
549 int code = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
551 Glib::ustring::compose(
552 _(
"==> Operazione terminata con errore (codice: %1)\n"),
558 Glib::ustring::compose(
559 _(
"Operazione fallita (codice %1)"),
static std::string fmtSize(guint64 bytes)
static const char * GRUB_ENTRY
static const char * RECOVERY_KERNEL
static const char * RECOVERY_SQUASHFS
static constexpr guint64 MIN_SPACE_BYTES
static const char * RECOVERY_DIR
static const char * INSTALL_GRUB_SCRIPT
static const char * BUILD_SCRIPT
static const char * BG_BACKUP_LAUNCHER
sigc::connection pulseTimer
void logSafe(const std::string &msg)
void setResult(bool success, const std::string &msg)
Glib::RefPtr< Gtk::TextBuffer > logBuffer
Gtk::Label recoveryResultLabel
void updateRecoveryStatus()
Gtk::Button removeGrubButton
Gtk::Widget * buildGrubTab()
Gtk::Button installGrubButton
Gtk::Widget * buildBackupTab()
BastionGuard::AliveFlag alive_
Gtk::Label recoveryStatusLabel
void runAsyncStreaming(const std::string &cmd, bool show_success=false)
Gtk::Label recoverySpaceLabel
Gtk::Button buildRecoveryButton
Gtk::ProgressBar recoveryProgress
Gtk::Widget * buildLogSection()