#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace bp = boost::process; namespace asio = boost::asio; namespace po = boost::program_options; namespace fs = boost::filesystem; bool dry_run = false; templatestd::set::reverse_iterator handle_backups_after(std::string & snapshotbase, std::set & times, std::set::reverse_iterator ct, boost::posix_time::ptime now) { if(ct == times.rend()) return ct; boost::posix_time::time_duration dur; boost::posix_time::ptime last_kept_time; while( ct != times.rend() && (dur = now - *ct).hours() <= till_y_day * 24) { last_kept_time = *ct; std::cout << "Keeping Backup " << last_kept_time << " of this day." << std::endl << std::endl; ct++; while( ct != times.rend() && (dur = last_kept_time - *ct).hours() < keep_every_x_days * 24) { if(ct == --times.rend()) { std::cout << "Keeping last Backup " << *ct << " in this backup-set." << std::endl << std::endl; return ct++; } std::cout << "Removing Backup " << *ct << " because we have already one in the relevant set of keeping every " << keep_every_x_days << " till " << till_y_day << " days." << std::endl; std::string timestring = boost::posix_time::to_iso_extended_string(*ct); timestring[10]='_'; timestring[13]='-'; timestring[16]='-'; timestring=timestring.substr(0, 19); asio::io_context ctx; asio::readable_pipe out(ctx); std::vector args; args.push_back("destroy"); args.push_back("-v"); if(dry_run) args.push_back("-n"); args.push_back(snapshotbase + "@" + timestring); bp::process proc(ctx, bp::environment::find_executable("zfs"), args, bp::process_stdio{{/* in to default*/}, out, {/* err to default */}}); std::string output; boost::system::error_code ec; asio::read(out, asio::dynamic_buffer(output), ec); assert(!ec || (ec==asio::error::eof)); proc.wait(); std::cout << output << std::endl; ct++; } } return ct; } std::set::reverse_iterator handle_backups_after_one_day(std::string & snapshotbase, std::set & times, std::set::reverse_iterator ct, boost::posix_time::ptime now) { // after one day, we keep one backup per day return handle_backups_after<1,30>(snapshotbase, times, ct, now); /*if(ct == times.rend()) return ct; boost::posix_time::time_duration dur; boost::posix_time::ptime last_kept_time; while( ct != times.rend() && (dur = now - *ct).hours() <= 30 * 24) { last_kept_time = *ct; std::cout << "Keeping Backup " << last_kept_time << " of this day." << std::endl; ct++; while( ct != times.rend() && (dur = last_kept_time - *ct).hours() < 24) { std::cout << "Removing Backup " << *ct << " because we have already one in 24 hours." << std::endl; ct++; } } return ct;*/ } std::set::reverse_iterator handle_backups_after_30_days(std::string & snapshotbase, std::set & times, std::set::reverse_iterator ct, boost::posix_time::ptime now) { // after 30 days, we keep one backup every 7 days return handle_backups_after<7,365>(snapshotbase, times, ct, now); } std::set::reverse_iterator handle_backups_after_365_days(std::string & snapshotbase, std::set & times, std::set::reverse_iterator ct, boost::posix_time::ptime now) { // after 365 days, we keep one backup every 30 days return handle_backups_after<30, 365*100>(snapshotbase, times, ct, now); } int main(int argc, char** argv){ std::cout << "Hello, from zfsbackupcleaner!\n"; po::options_description options("Argumente"); options.add_options() ("help", "Hilfe anzeigen") ("dry-run,n", "Test-Modus; keine Backups entfernen") ("backupsystem", po::value(), "Computer, dessen Backups bereinigt werden sollen") ; po::positional_options_description p; p.add("backupsystem", 1); po::variables_map vm; try { po::store(po::command_line_parser(argc,argv).options(options).positional(p).run(), vm); po::notify(vm); } catch (const boost::program_options::invalid_command_line_syntax & error) { std::cout << "Error parsing commandline-arguments:" << std::endl; std::cout << error.what() << std::endl; return -1; } if(vm.count("help") || !vm.count("backupsystem")) { std::cout << "Benutzung: zfsbackupcleaner [Optionen] " << std::endl << std::endl; std::cout << options << std::endl; return 0; } if(vm.count("dry-run")) dry_run = true; std::string backupsystem = vm["backupsystem"].as(); if(backupsystem != "workstation" && ! fs::exists("/backup/"+backupsystem)) { std::cout << "Für das System >" << backupsystem << "< gibt es keine Backups." << std::endl; return -1; } asio::io_context ctx; asio::readable_pipe out(ctx); bp::process proc(ctx, bp::environment::find_executable("zfs"), {"list", "-t", "snapshot", "/backup/"+backupsystem}, bp::process_stdio{{/* in to default*/}, out, {/* err to default */}}); std::string output; boost::system::error_code ec; asio::read(out, asio::dynamic_buffer(output), ec); assert(!ec || (ec==asio::error::eof)); proc.wait(); //std::cout << "OUTPUT" << output << "END" << std::endl; std::stringstream lines(output); std::set times; std::string snapshotbase; for(std::string line; std::getline(lines, line);) { std::string::size_type pos = line.find(' '); line = line.substr(0,pos); //std::cout << "X: " << line << std::endl; if(line == "NAME") continue; pos = line.find('@'); snapshotbase = line.substr(0,pos); std::string timestring = line.substr(pos+1); timestring[10]=' '; timestring[13]=':'; timestring[16]=':'; boost::posix_time::ptime t(boost::posix_time::time_from_string(timestring)); times.insert(t); } if(times.empty()) { std::cout << "NO SNAPSHOTS FOUND — EXIT"; return 0; } std::set::reverse_iterator current_time = times.rbegin(); // handle fist 24 hours!!! boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); std::cout << "Gehe aus von aktueller Zeit: " << now << std::endl << std::endl; boost::posix_time::time_duration dur; while( (dur = now - *current_time).hours() <= 72) { std::cout << "Omitting Backup " << *current_time << " because it is too young to handle (" << dur.hours() << " hours old)." << std::endl; current_time++; } std::cout << std::endl; current_time = handle_backups_after_one_day(snapshotbase, times, current_time, now); current_time = handle_backups_after_30_days(snapshotbase, times, current_time, now); current_time = handle_backups_after_365_days(snapshotbase, times, current_time, now); return 0; }