From: Arvin Schnell Date: Tue, 5 Nov 2024 14:31:17 +0000 (+0100) Subject: - provide backup program for btrfs snapshots X-Git-Tag: v0.12.0~17^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F951%2Fhead;p=thirdparty%2Fsnapper.git - provide backup program for btrfs snapshots --- diff --git a/client/Makefile.am b/client/Makefile.am index 59de7e6b..414ac83d 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -2,4 +2,4 @@ # Makefile.am for snapper/client # -SUBDIRS = utils proxy snapper mksubvolume installation-helper systemd-helper +SUBDIRS = utils proxy snapper mksubvolume installation-helper systemd-helper snbk diff --git a/client/snbk/.gitignore b/client/snbk/.gitignore new file mode 100644 index 00000000..5561e1aa --- /dev/null +++ b/client/snbk/.gitignore @@ -0,0 +1,4 @@ +*.o +*.lo +*.la +snbk diff --git a/client/snbk/BackupConfig.cc b/client/snbk/BackupConfig.cc new file mode 100644 index 00000000..9d31a85b --- /dev/null +++ b/client/snbk/BackupConfig.cc @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2024 SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + + +#include + +#include +#include + +#include "BackupConfig.h" +#include "JsonFile.h" + + +namespace snapper +{ + + using namespace std; + + + const vector EnumInfo::names({ "local", "ssh-push" }); + + + BackupConfig::BackupConfig(const string& name) + : name(name) + { + JsonFile json_file(BACKUP_CONFIGS_DIR "/" + name + ".json"); + + if (!get_child_value(json_file.get_root(), "config", config)) + SN_THROW(Exception("config entry not found in file")); + + string tmp1; + if (!get_child_value(json_file.get_root(), "target-mode", tmp1)) + SN_THROW(Exception("target-mode entry not found in file")); + if (!toValue(tmp1, target_mode, false)) + SN_THROW(Exception("unknown target-mode")); + + if (!get_child_value(json_file.get_root(), "source-path", source_path)) + SN_THROW(Exception("source-path entry not found in file")); + + if (!get_child_value(json_file.get_root(), "target-path", target_path)) + SN_THROW(Exception("target-path entry not found in file")); + + get_child_value(json_file.get_root(), "automatic", automatic); + + if (target_mode == TargetMode::SSH_PUSH) + { + if (!get_child_value(json_file.get_root(), "ssh-host", ssh_host)) + SN_THROW(Exception("ssh-host entry not found in file")); + + get_child_value(json_file.get_root(), "ssh-port", ssh_port); + get_child_value(json_file.get_root(), "ssh-user", ssh_user); + get_child_value(json_file.get_root(), "ssh-identity", ssh_identity); + } + } + + + Shell + BackupConfig::get_source_shell() const + { + Shell source_shell; + + return source_shell; + } + + + Shell + BackupConfig::get_target_shell() const + { + Shell target_shell; + + if (target_mode == TargetMode::SSH_PUSH) + { + target_shell.mode = Shell::Mode::SSH; + target_shell.ssh_options = ssh_options(); + } + + return target_shell; + } + + + vector + BackupConfig::ssh_options() const + { + vector options = { ssh_host }; + + if (ssh_port != 0) + options.insert(options.end(), { "-p", to_string(ssh_port) }); + + if (!ssh_user.empty()) + options.insert(options.end(), { "-l", ssh_user }); + + if (!ssh_identity.empty()) + options.insert(options.end(), { "-i", ssh_identity }); + + return options; + } + + + vector + read_backup_config_names() + { + const vector filenames = glob(BACKUP_CONFIGS_DIR "/" "*.json", 0); + const size_t l1 = strlen(BACKUP_CONFIGS_DIR "/"); + const size_t l2 = strlen(".json"); + + vector names; + + for (const string& filename : filenames) + names.push_back(filename.substr(l1, filename.size() - l1 - l2)); + + return names; + } + +} diff --git a/client/snbk/BackupConfig.h b/client/snbk/BackupConfig.h new file mode 100644 index 00000000..254bc843 --- /dev/null +++ b/client/snbk/BackupConfig.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2024 SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + +#ifndef SNAPPER_BACKUP_CONFIG_H +#define SNAPPER_BACKUP_CONFIG_H + + +#include +#include + +#include + +#include "Shell.h" + + +namespace snapper +{ + + using std::string; + using std::vector; + + + class BackupConfig + { + public: + + enum class TargetMode + { + LOCAL, SSH_PUSH + }; + + BackupConfig(const string& name); + + const string name; + + string config; + + TargetMode target_mode = TargetMode::LOCAL; + + string source_path; + string target_path; + + bool automatic = false; + + string ssh_host; + unsigned int ssh_port = 0; + string ssh_user; + string ssh_identity; + + Shell get_source_shell() const; + Shell get_target_shell() const; + + private: + + vector ssh_options() const; + + }; + + + using BackupConfigs = vector; + + + template <> struct EnumInfo { static const vector names; }; + + + vector + read_backup_config_names(); + +} + + +#endif diff --git a/client/snbk/CmdBtrfs.cc b/client/snbk/CmdBtrfs.cc new file mode 100644 index 00000000..ac98b504 --- /dev/null +++ b/client/snbk/CmdBtrfs.cc @@ -0,0 +1,262 @@ +/* + * Copyright (c) [2004-2015] Novell, Inc. + * Copyright (c) [2017-2024] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + + +#include +#include +#include + +#include "snapper/SnapperTmpl.h" +#include "snapper/SystemCmd.h" +#include "snapper/SnapperDefines.h" +#include "snapper/Exception.h" +#include "snapper/Log.h" +#include "CmdBtrfs.h" + + +namespace snapper +{ + + using namespace std; + + + CmdBtrfsSubvolumeList::CmdBtrfsSubvolumeList(const Shell& shell, const string& mount_point) + { + SystemCmd::Args cmd_args = { BTRFS_BIN, "subvolume", "list", "-a", "-puqR", mount_point }; + SystemCmd cmd(shellify(shell, cmd_args)); + + if (cmd.retcode() != 0) + { + y2err("command '" << cmd.cmd() << "' failed: " << cmd.retcode()); + for (const string& tmp : cmd.get_stdout()) + y2err(tmp); + for (const string& tmp : cmd.get_stderr()) + y2err(tmp); + + SN_THROW(Exception("'btrfs subvolume list' failed")); + } + + parse(cmd.get_stdout()); + } + + + void + CmdBtrfsSubvolumeList::parse(const vector& lines) + { + for (const string& line : lines) + { + Entry entry; + + string::size_type pos1 = line.find("ID "); + if (pos1 == string::npos) + SN_THROW(Exception("could not find 'id' in 'btrfs subvolume list' output")); + line.substr(pos1 + strlen("ID ")) >> entry.id; + + string::size_type pos2 = line.find(" parent "); + if (pos2 == string::npos) + SN_THROW(Exception("could not find 'parent' in 'btrfs subvolume list' output")); + line.substr(pos2 + strlen(" parent ")) >> entry.parent_id; + + // Subvolume can already be deleted, in which case parent is "0" + // (and path "DELETED"). That is a temporary state. + if (entry.parent_id == 0) + continue; + + string::size_type pos3 = line.find(" path "); + if (pos3 == string::npos) + SN_THROW(Exception("could not find 'path' in 'btrfs subvolume list' output")); + entry.path = line.substr(pos3 + strlen(" path ")); + if (boost::starts_with(entry.path, "/")) + entry.path.erase(0, strlen("/")); + + string::size_type pos4 = line.find(" uuid "); + if (pos4 == string::npos) + SN_THROW(Exception("could not find 'uuid' in 'btrfs subvolume list' output")); + line.substr(pos4 + strlen(" uuid ")) >> entry.uuid; + + string::size_type pos5 = line.find(" parent_uuid "); + if (pos5 == string::npos) + SN_THROW(Exception("could not find 'parent_uuid' in 'btrfs subvolume list' output")); + line.substr(pos5 + strlen(" parent_uuid ")) >> entry.parent_uuid; + if (entry.parent_uuid == "-") + entry.parent_uuid = ""; + + string::size_type pos6 = line.find(" received_uuid "); + if (pos6 == string::npos) + SN_THROW(Exception("could not find 'received_uuid' in 'btrfs subvolume list' output")); + line.substr(pos6 + strlen(" received_uuid ")) >> entry.parent_uuid; + if (entry.received_uuid == "-") + entry.received_uuid = ""; + + data.push_back(entry); + } + + // No way to get read-only flag. Only showing read-only snapshots does not help. + + y2mil(*this); + } + + + CmdBtrfsSubvolumeList::const_iterator + CmdBtrfsSubvolumeList::find_entry_by_path(const string& path) const + { + for (const_iterator it = data.begin(); it != data.end(); ++it) + { + if (it->path == path) + return it; + } + + return data.end(); + } + + + std::ostream& + operator<<(std::ostream& s, const CmdBtrfsSubvolumeList& cmd_btrfs_subvolume_list) + { + for (const CmdBtrfsSubvolumeList::Entry& entry : cmd_btrfs_subvolume_list) + s << entry; + + return s; + } + + + std::ostream& + operator<<(std::ostream& s, const CmdBtrfsSubvolumeList::Entry& entry) + { + s << "id:" << entry.id << " parent-id:" << entry.parent_id + << " path:" << entry.path << " uuid:" << entry.uuid; + + if (!entry.parent_uuid.empty()) + s << " parent-uuid:" << entry.parent_uuid; + + if (!entry.received_uuid.empty()) + s << " received-uuid:" << entry.received_uuid; + + s << '\n'; + + return s; + } + + + CmdBtrfsSubvolumeShow::CmdBtrfsSubvolumeShow(const Shell& shell, const string& mount_point) + { + SystemCmd::Args cmd_args = { BTRFS_BIN, "subvolume", "show", mount_point }; + SystemCmd cmd(shellify(shell, cmd_args)); + + if (cmd.retcode() != 0) + { + y2err("command '" << cmd.cmd() << "' failed: " << cmd.retcode()); + for (const string& tmp : cmd.get_stdout()) + y2err(tmp); + for (const string& tmp : cmd.get_stderr()) + y2err(tmp); + + SN_THROW(Exception("'btrfs subvolume show' failed")); + } + + parse(cmd.get_stdout()); + } + + + void + CmdBtrfsSubvolumeShow::parse(const vector& lines) + { + static const regex uuid_regex("[ \t]*UUID:[ \t]*(" UUID_REGEX "|-)[ \t]*", regex::extended); + static const regex parent_uuid_regex("[ \t]*Parent UUID:[ \t]*(" UUID_REGEX "|-)[ \t]*", regex::extended); + static const regex received_uuid_regex("[ \t]*Received UUID:[ \t]*(" UUID_REGEX "|-)[ \t]*", regex::extended); + static const regex creation_time_regex("[ \t]*Creation time:[ \t]*([-+0-9: ]+)[ \t]*", regex::extended); + static const regex flags_regex("[ \t]*Flags:[ \t]*(" "readonly" "|-)[ \t]*", regex::extended); + + smatch match; + + for (const string& line : lines) + { + if (regex_match(line, match, uuid_regex)) + uuid = match[1]; + + if (regex_match(line, match, parent_uuid_regex)) + { + if (match[1] != "-") + parent_uuid = match[1]; + } + + if (regex_match(line, match, received_uuid_regex)) + { + if (match[1] != "-") + received_uuid = match[1]; + } + + if (regex_match(line, match, creation_time_regex)) + creation_time = match[1]; + + // so far readonly is the only flag so unclear whether several flags will be + // separated by space or comma + + if (regex_match(line, match, flags_regex)) + read_only = match[1] == "readonly"; + } + + if (uuid.empty()) + SN_THROW(Exception("could not find 'uuid' in 'btrfs subvolume show' output")); + + if (uuid == "-") + { + // If the btrfs was created with older kernels (whatever that means) (tested + // with SLES 11 SP3), the top-level subvolume does not have a UUID. Other + // subvolumes do have a UUID. In that case also all subvolumes are listed + // wrongly as snapshots of the top-level subvolume (by 'btrfs subvolume + // show'). The relationship between other subvolumes/snapshots seems to be + // fine. + + y2mil("could not find 'uuid' in 'btrfs subvolume show' output - happens if " + "btrfs was created with an old kernel"); + + uuid = ""; + } + + y2mil(*this); + } + + + std::ostream& + operator<<(std::ostream& s, const CmdBtrfsSubvolumeShow& cmd_btrfs_subvolume_show) + { + s << "uuid:" << cmd_btrfs_subvolume_show.uuid; + + if (!cmd_btrfs_subvolume_show.parent_uuid.empty()) + s << " parent-uuid:" << cmd_btrfs_subvolume_show.parent_uuid; + + if (!cmd_btrfs_subvolume_show.received_uuid.empty()) + s << " received-uuid:" << cmd_btrfs_subvolume_show.received_uuid; + + if (!cmd_btrfs_subvolume_show.creation_time.empty()) + s << " creation-time:" << cmd_btrfs_subvolume_show.creation_time; + + if (cmd_btrfs_subvolume_show.read_only) + s << " read-only"; + + s << '\n'; + + return s; + } + +} diff --git a/client/snbk/CmdBtrfs.h b/client/snbk/CmdBtrfs.h new file mode 100644 index 00000000..db10c504 --- /dev/null +++ b/client/snbk/CmdBtrfs.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) [2004-2015] Novell, Inc. + * Copyright (c) [2017-2024] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + + +#ifndef SNAPPER_CMD_BTRFS_H +#define SNAPPER_CMD_BTRFS_H + + +#include "Shell.h" + + +namespace snapper +{ + + using std::string; + using std::vector; + + + /** + * Class to probe for btrfs subvolumes: Call "btrfs subvolume list + * ". + */ + class CmdBtrfsSubvolumeList + { + public: + + static const long top_level_id = 5; + static const long unknown_id = -1; + + CmdBtrfsSubvolumeList(const Shell& shell, const string& mount_point); + + /** + * Entry for every subvolume (unfortunately except the top-level). + * + * Caution: parent_id and parent_uuid are something completely + * different - not just different ways to specify the + * "parent". + */ + struct Entry + { + long id = unknown_id; + long parent_id = unknown_id; + string path; + string uuid; + string parent_uuid; + string received_uuid; + }; + + typedef vector::value_type value_type; + typedef vector::const_iterator const_iterator; + + const_iterator begin() const { return data.begin(); } + const_iterator end() const { return data.end(); } + + const_iterator find_entry_by_path(const string& path) const; + + friend std::ostream& operator<<(std::ostream& s, const CmdBtrfsSubvolumeList& cmd_btrfs_subvolume_list); + friend std::ostream& operator<<(std::ostream& s, const Entry& entry); + + private: + + void parse(const vector& lines); + + vector data; + + }; + + + /** + * Class to probe for btrfs subvolume information: Call "btrfs subvolume + * show ". + */ + class CmdBtrfsSubvolumeShow + { + public: + + CmdBtrfsSubvolumeShow(const Shell& shell, const string& mount_point); + + const string& get_uuid() const { return uuid; } + const string& get_parent_uuid() const { return parent_uuid; } + const string& get_received_uuid() const { return received_uuid; } + const string& get_creation_time() const { return creation_time; } + bool is_read_only() const { return read_only; } + + friend std::ostream& operator<<(std::ostream& s, const CmdBtrfsSubvolumeShow& + cmd_btrfs_subvolume_show); + + private: + + void parse(const vector& lines); + + string uuid; + string parent_uuid; + string received_uuid; + string creation_time; // TODO should be time_t + bool read_only = false; + + }; + +} + +#endif diff --git a/client/snbk/CmdFindmnt.cc b/client/snbk/CmdFindmnt.cc new file mode 100644 index 00000000..4844c8b6 --- /dev/null +++ b/client/snbk/CmdFindmnt.cc @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024 SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + + +#include "CmdFindmnt.h" +#include "JsonFile.h" + +#include "snapper/SystemCmd.h" +#include "snapper/SnapperDefines.h" +#include "snapper/Exception.h" +#include "snapper/Log.h" + + +namespace snapper +{ + + CmdFindmnt::CmdFindmnt(const Shell& shell, const string& path) + : path(path) + { + SystemCmd::Args cmd_args = { FINDMNT_BIN, "--json", "--target", path }; + SystemCmd cmd(shellify(shell, cmd_args)); + + if (cmd.retcode() != 0) + { + y2err("command '" << cmd.cmd() << "' failed: " << cmd.retcode()); + for (const string& tmp : cmd.get_stdout()) + y2err(tmp); + for (const string& tmp : cmd.get_stderr()) + y2err(tmp); + + SN_THROW(Exception("'findmnt' failed")); + } + + parse_json(cmd.get_stdout()); + } + + + void + CmdFindmnt::parse_json(const vector& lines) + { + JsonFile json_file(lines); + + vector tmp1; + + if (!get_child_nodes(json_file.get_root(), "filesystems", tmp1)) + SN_THROW(Exception("\"filesystems\" not found in json output of 'findmnt'")); + + for (json_object* tmp2 : tmp1) + { + if (!get_child_value(tmp2, "source", source)) + SN_THROW(Exception("\"source\" not found or invalid")); + + if (!get_child_value(tmp2, "target", target)) + SN_THROW(Exception("\"target\" not found or invalid")); + } + + y2mil(*this); + } + + + std::ostream& + operator<<(std::ostream& s, const CmdFindmnt& cmd_findmnt) + { + s << "path:" << cmd_findmnt.path << " source:" << cmd_findmnt.source + << " target:" << cmd_findmnt.target; + + return s; + } + +} diff --git a/client/snbk/CmdFindmnt.h b/client/snbk/CmdFindmnt.h new file mode 100644 index 00000000..09cbea96 --- /dev/null +++ b/client/snbk/CmdFindmnt.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024 SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + + +#ifndef SNAPPER_CMD_FINDMNT_H +#define SNAPPER_CMD_FINDMNT_H + + +#include "Shell.h" + + +namespace snapper +{ + + using std::string; + using std::vector; + + + /** + * Class to probe for mount points: Call "findmnt --target ". + */ + class CmdFindmnt + { + public: + + CmdFindmnt(const Shell& shell, const string& path); + + const string& get_source() const { return source; } + const string& get_target() const { return target; } + + friend std::ostream& operator<<(std::ostream& s, const CmdFindmnt& cmd_findmnt); + + private: + + void parse_json(const vector& lines); + + const string path; + + string source; + string target; + + }; + +} + +#endif diff --git a/client/snbk/CmdRealpath.cc b/client/snbk/CmdRealpath.cc new file mode 100644 index 00000000..9600fdcb --- /dev/null +++ b/client/snbk/CmdRealpath.cc @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024 SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + + +#include "CmdRealpath.h" + +#include "snapper/SystemCmd.h" +#include "snapper/SnapperDefines.h" +#include "snapper/Exception.h" +#include "snapper/Log.h" + + +namespace snapper +{ + + CmdRealpath::CmdRealpath(const Shell& shell, const string& path) + : path(path) + { + SystemCmd::Args cmd_args = { REALPATH_BIN, path }; + SystemCmd cmd(shellify(shell, cmd_args)); + + if (cmd.retcode() != 0) + { + y2err("command '" << cmd.cmd() << "' failed: " << cmd.retcode()); + for (const string& tmp : cmd.get_stdout()) + y2err(tmp); + for (const string& tmp : cmd.get_stderr()) + y2err(tmp); + + SN_THROW(Exception("'realpath' failed")); + } + + parse(cmd.get_stdout()); + } + + + void + CmdRealpath::parse(const vector& lines) + { + if (lines.size() != 1) + SN_THROW(Exception("failed to parse output of 'realpath'")); + + realpath = lines[0]; + + y2mil(*this); + } + + + std::ostream& + operator<<(std::ostream& s, const CmdRealpath& cmd_realpath) + { + s << "path:" << cmd_realpath.path << " realpath:" << cmd_realpath.realpath; + + return s; + } + +} diff --git a/client/snbk/CmdRealpath.h b/client/snbk/CmdRealpath.h new file mode 100644 index 00000000..9668713d --- /dev/null +++ b/client/snbk/CmdRealpath.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + + +#ifndef SNAPPER_CMD_REALPATH_H +#define SNAPPER_CMD_REALPATH_H + + +#include "Shell.h" + + +namespace snapper +{ + + using std::string; + using std::vector; + + + /** + * Class to probe realpath: Call "realpath ". + */ + class CmdRealpath + { + public: + + CmdRealpath(const Shell& shell, const string& path); + + const string& get_realpath() const { return realpath; } + + friend std::ostream& operator<<(std::ostream& s, const CmdRealpath& cmd_realpath); + + private: + + void parse(const vector& lines); + + const string path; + + string realpath; + + }; + +} + +#endif diff --git a/client/snbk/GlobalOptions.cc b/client/snbk/GlobalOptions.cc new file mode 100644 index 00000000..4f9deab4 --- /dev/null +++ b/client/snbk/GlobalOptions.cc @@ -0,0 +1,211 @@ +/* + * Copyright (c) [2019-2024] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + + +#include "../utils/help.h" +#include "../misc.h" +#include "client/utils/text.h" +#include "client/utils/TableFormatter.h" +#include "client/utils/CsvFormatter.h" + +#include "GlobalOptions.h" + + +namespace snapper +{ + + using namespace std; + + + void + GlobalOptions::help_global_options() + { + cout << " " << _("Global options:") << '\n'; + + print_options({ + { _("--quiet, -q"), _("Suppress normal output.") }, + { _("--verbose, -v"), _("Increase verbosity.") }, + { _("--debug"), _("Turn on debugging.") }, + { _("--target-mode "), _("Only use backup-config with specified target-mode.") }, + { _("--automatic"), _("Only use backup-config with automatic set.") }, + { _("--utc"), _("Display dates and times in UTC.") }, + { _("--iso"), _("Display dates and times in ISO format.") }, + { _("--table-style, -t