This repository has been archived on 2023-09-05. You can view files and clone it, but cannot push or open issues or pull requests.
backup-manager/BackupManager/Program.cs

219 lines
7.4 KiB
C#
Raw Permalink Normal View History

using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
2018-10-13 23:03:50 +00:00
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
2019-01-15 15:27:00 +00:00
using System.Text;
2018-10-13 23:03:50 +00:00
using System.Xml;
using System.Xml.Serialization;
2023-09-04 12:55:27 +00:00
namespace BackupManager
{
2019-11-06 17:04:53 +00:00
public static class Program {
2023-04-12 02:26:00 +00:00
public readonly static Stopwatch sw = new();
2018-10-13 23:03:50 +00:00
private const string CONFIG_NAME = @"FlashiiBackupManager.v1.xml";
public readonly static DateTimeOffset Startup = DateTimeOffset.UtcNow;
public static string Basename
=> $@"{Environment.MachineName} {Startup.Year:0000}-{Startup.Month:00}-{Startup.Day:00} {Startup.Hour:00}{Startup.Minute:00}{Startup.Second:00}";
2019-11-06 17:04:53 +00:00
public static string BackupName
2018-10-13 23:03:50 +00:00
=> $@"{Basename}.zip";
private static Config Config;
private readonly static string ConfigPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
CONFIG_NAME
);
2019-11-06 17:04:53 +00:00
public static string BackupStore => string.IsNullOrWhiteSpace(Config.FileSystemPathV2)
? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), @"Backups")
: Config.FileSystemPathV2;
2023-04-12 02:26:00 +00:00
private static bool Headless;
2018-10-13 23:03:50 +00:00
2019-11-06 17:04:53 +00:00
public static Stream ToXml(this object obj, bool pretty = false) {
2023-04-12 02:26:00 +00:00
MemoryStream ms = new();
XmlSerializer xs = new(obj.GetType());
2018-10-13 23:03:50 +00:00
2023-04-12 02:26:00 +00:00
using(XmlWriter xw = XmlWriter.Create(ms, new XmlWriterSettings { Indent = pretty }))
2018-10-13 23:03:50 +00:00
xs.Serialize(xw, obj);
ms.Seek(0, SeekOrigin.Begin);
return ms;
}
2019-11-06 17:04:53 +00:00
public static T FromXml<T>(Stream xml) {
2023-04-12 02:26:00 +00:00
if(xml.CanSeek)
2018-10-13 23:03:50 +00:00
xml.Seek(0, SeekOrigin.Begin);
2023-04-12 02:26:00 +00:00
XmlSerializer xs = new(typeof(T));
2018-10-13 23:03:50 +00:00
return (T)xs.Deserialize(xml);
}
2019-11-06 17:04:53 +00:00
public static void SaveConfig() {
2018-10-13 23:03:50 +00:00
Log(@"Saving configuration...");
2023-04-12 02:26:00 +00:00
using FileStream fs = new(ConfigPath, FileMode.Create, FileAccess.Write);
using Stream cs = Config.ToXml(true);
cs.CopyTo(fs);
2018-10-13 23:03:50 +00:00
}
2019-11-06 17:04:53 +00:00
public static void LoadConfig() {
2018-10-13 23:03:50 +00:00
Log(@"Loading configuration...");
2023-04-12 02:26:00 +00:00
using FileStream fs = File.OpenRead(ConfigPath);
Config = FromXml<Config>(fs);
2018-10-13 23:03:50 +00:00
}
2019-11-06 17:04:53 +00:00
public static void Main(string[] args) {
2018-10-13 23:03:50 +00:00
Headless = args.Contains(@"-cron") || args.Contains(@"-headless");
Log(@"Flashii Backup Manager");
sw.Start();
2023-04-12 02:26:00 +00:00
if(!File.Exists(ConfigPath)) {
2018-10-13 23:03:50 +00:00
Config = new Config();
SaveConfig();
Error(@"No configuration file exists, created a blank one. Be sure to fill it out properly.");
}
LoadConfig();
2023-04-12 02:26:00 +00:00
if(!Directory.Exists(BackupStore))
2019-11-06 17:04:53 +00:00
Directory.CreateDirectory(BackupStore);
2018-10-13 23:03:50 +00:00
Log(@"Fetching database list...");
2023-04-12 02:26:00 +00:00
MySqlConnectionStringBuilder connStr = new() {
Server = Config.MySqlHost,
UserID = Config.MySqlUser,
Password = Config.MySqlPass,
CharacterSet = @"utf8mb4",
2023-09-04 12:58:34 +00:00
SslMode = MySqlSslMode.Disabled,
};
2023-04-12 02:26:00 +00:00
List<string> databases = new();
string[] exclude = Config.MySqlExcludeDatabases.Split(' ');
2023-04-12 02:26:00 +00:00
using(MySqlConnection conn = new(connStr.ToString())) {
conn.Open();
2021-07-30 02:23:48 +00:00
using(MySqlCommand comm = new(@"SET NAMES 'utf8mb4';", conn))
2021-07-30 02:23:00 +00:00
comm.ExecuteNonQuery();
2023-04-12 02:26:00 +00:00
using(MySqlCommand comm = new(@"SHOW DATABASES;", conn))
2021-07-30 02:23:00 +00:00
using(MySqlDataReader read = comm.ExecuteReader()) {
while(read.Read()) {
string database = read.GetString(0);
2021-07-30 02:23:00 +00:00
if(string.IsNullOrEmpty(database) || exclude.Contains(database))
continue;
databases.Add(database);
}
}
}
2019-11-06 17:04:53 +00:00
Log(@"Creating backup archive...");
2019-08-16 03:09:17 +00:00
2019-11-06 17:04:53 +00:00
string archivePath = Path.GetTempFileName();
2018-10-13 23:03:50 +00:00
2023-04-12 02:26:00 +00:00
using(FileStream fs = File.OpenWrite(archivePath))
using(ZipArchive archive = new(fs, ZipArchiveMode.Create)) {
2019-11-06 17:04:53 +00:00
Log(@"Database backup...");
string sqldefaults = Path.GetTempFileName();
2019-08-16 03:09:17 +00:00
2023-04-12 02:26:00 +00:00
using(FileStream sqlConfFs = File.Open(sqldefaults, FileMode.Open, FileAccess.ReadWrite))
using(StreamWriter sw = new(sqlConfFs)) {
2019-11-06 17:04:53 +00:00
sw.WriteLine(@"[client]");
sw.WriteLine($@"user={Config.MySqlUser}");
sw.WriteLine($@"password={Config.MySqlPass}");
sw.WriteLine(@"default-character-set=utf8mb4");
}
2018-10-13 23:03:50 +00:00
2023-04-12 02:26:00 +00:00
foreach(string database in databases)
2019-11-06 17:04:53 +00:00
CreateDbDump(archive, sqldefaults, database);
2018-10-13 23:03:50 +00:00
2019-11-06 17:04:53 +00:00
Log($@"MariaDB dump done.");
File.Delete(sqldefaults);
2018-10-13 23:03:50 +00:00
}
2019-11-06 17:04:53 +00:00
string targetPath = Path.Combine(BackupStore, BackupName);
File.Move(archivePath, targetPath);
Log($@"Moved backup archive to {targetPath}");
2018-10-13 23:03:50 +00:00
SaveConfig();
sw.Stop();
2023-09-04 12:55:27 +00:00
Log($@"Done! Took {sw.Elapsed}.");
2018-10-13 23:03:50 +00:00
#if DEBUG
Console.ReadLine();
#endif
}
2023-09-04 12:55:27 +00:00
public static void Log(object line) {
2023-04-12 02:26:00 +00:00
if(!Headless) {
if(sw?.IsRunning == true) {
2019-02-11 15:29:50 +00:00
ConsoleColor fg = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.Write(sw.ElapsedMilliseconds.ToString().PadRight(10));
Console.ForegroundColor = fg;
}
2018-10-13 23:03:50 +00:00
2019-02-11 15:29:50 +00:00
Console.WriteLine(line);
}
2018-10-13 23:03:50 +00:00
}
2019-11-06 17:04:53 +00:00
public static void Error(object line, int exit = 0x00DEAD00) {
2023-04-12 02:26:00 +00:00
if(!Headless) {
2018-10-13 23:03:50 +00:00
Console.ForegroundColor = ConsoleColor.Red;
Log(line);
Console.ResetColor();
}
2019-01-15 14:55:35 +00:00
#if DEBUG
Console.ReadLine();
#endif
2018-10-13 23:03:50 +00:00
Environment.Exit(exit);
}
2019-11-06 17:04:53 +00:00
public static void CreateDbDump(ZipArchive archive, string defaults, string database) {
Log($@"Dumping {database}...");
2018-10-13 23:03:50 +00:00
2019-08-16 03:09:17 +00:00
string sqldump = Path.GetTempFileName();
2023-04-12 02:26:00 +00:00
StringBuilder mysqldumpArgs = new();
2019-11-06 17:04:53 +00:00
mysqldumpArgs.AppendFormat(@"--defaults-file={0} ", defaults);
mysqldumpArgs.Append(@"--single-transaction ");
mysqldumpArgs.Append(@"--tz-utc --triggers ");
mysqldumpArgs.Append(@"--routines --hex-blob ");
mysqldumpArgs.Append(@"--add-locks --order-by-primary ");
2021-01-31 00:26:38 +00:00
mysqldumpArgs.Append(@"--skip-lock-tables "); // might regret this, we'll find out someday
2019-08-16 03:09:17 +00:00
mysqldumpArgs.AppendFormat(@"--result-file={0} ", sqldump);
2021-01-31 00:26:38 +00:00
mysqldumpArgs.Append(@"-Q -q -B "); // lock, quote names, quick, database list
2019-11-06 17:04:53 +00:00
mysqldumpArgs.Append(database);
2019-08-16 03:09:17 +00:00
#if DEBUG
Log($@"mysqldump args: {mysqldumpArgs}");
#endif
2019-11-06 17:04:53 +00:00
Process p = Process.Start(new ProcessStartInfo {
2023-09-04 12:56:53 +00:00
FileName = Config.MySqlDumpPath,
Arguments = mysqldumpArgs.ToString(),
2018-10-13 23:03:50 +00:00
UseShellExecute = false,
CreateNoWindow = true,
2019-01-15 14:55:35 +00:00
});
2018-10-13 23:03:50 +00:00
p.WaitForExit();
2019-11-06 17:04:53 +00:00
archive.CreateEntryFromFile(sqldump, $@"mariadb/{database}.sql", CompressionLevel.Optimal);
2019-08-16 03:09:17 +00:00
2019-11-06 17:04:53 +00:00
File.Delete(sqldump);
2018-10-13 23:03:50 +00:00
}
}
}