diff --git a/native/updater/file.cpp b/native/updater/file.cpp index 07dd0f2..83a3663 100644 --- a/native/updater/file.cpp +++ b/native/updater/file.cpp @@ -104,7 +104,7 @@ bool file::move(std::string &error, const std::string &source, const std::string try { fs::create_directories(target_path.parent_path()); } catch(const fs::filesystem_error& ex) { - error = "failed to create target directories"; + error = "failed to create target directories (" + std::string{ex.what()} + ")"; return false; } } else { @@ -200,12 +200,12 @@ void file::rollback() { return; logger::info("Rollbacking %d moved files", move_back.size()); - for(const pair& key : move_back) { - logger::debug("Attempting to moveback %s to %s", key.first.c_str(), key.second.c_str()); + for(const auto& [backup, original] : move_back) { + logger::debug("Attempting to moveback %s to %s", backup.c_str(), original.c_str()); try { - fs::rename(fs::u8path(key.first), fs::u8path(key.second)); + fs::rename(fs::u8path(backup), fs::u8path(original)); } catch(const fs::filesystem_error& ex) { - logger::warn("Failed to moveback file from %s to %s (%s)", key.first.c_str(), key.second.c_str(), ex.what()); + logger::warn("Failed to moveback file from %s to %s (%s)", backup.c_str(), original.c_str(), ex.what()); continue; } logger::debug("=> success"); @@ -213,19 +213,19 @@ void file::rollback() { logger::info("Rollbacking %d deleted files", backup_mapping.size()); - for(const pair& key : backup_mapping) { - logger::debug("Attempting to restore %s to %s", key.first.c_str(), key.second.c_str()); + for(const auto& [backup, original] : backup_mapping) { + logger::debug("Attempting to restore %s to %s", backup.c_str(), original.c_str()); try { - auto source = fs::absolute(fs::u8path(config::backup_directory) / key.first); + auto source = fs::absolute(fs::u8path(config::backup_directory) / backup); if(!fs::exists(source)) { - logger::warn("Failed to restore file %s (Source file missing)", key.second.c_str()); + logger::warn("Failed to restore file %s (Source file missing)", original.c_str()); continue; } - auto target = fs::u8path(key.second); + auto target = fs::u8path(original); fs::rename(source, target); } catch(const fs::filesystem_error& ex) { - logger::warn("Failed to restore file %s (%s)", key.second.c_str(), ex.what()); + logger::warn("Failed to restore file %s (%s)", original.c_str(), ex.what()); continue; } logger::debug("=> success"); @@ -233,6 +233,20 @@ void file::rollback() { logger::info("Rollback done"); } +void file::commit() { + if(!config::backup) + return; + + try { + if(!fs::remove_all(config::backup_directory)) + throw fs::filesystem_error("invalid result", error_code()); + } catch(const fs::filesystem_error& ex) { + logger::warn("Failed to cleanup backup directory (" + string{ex.what()} + ")"); + } + + backup_mapping.clear(); +} + #ifdef WIN32 bool file::file_locked(const std::string &file) { auto file_handle = CreateFile( @@ -240,7 +254,7 @@ void file::rollback() { (DWORD) GENERIC_WRITE, (DWORD) 0, /* we dont want to share */ (LPSECURITY_ATTRIBUTES) nullptr, - (DWORD) OPEN_EXISTING, /* file should be availible */ + (DWORD) OPEN_EXISTING, /* file should be available */ FILE_ATTRIBUTE_NORMAL, nullptr ); @@ -249,9 +263,11 @@ void file::rollback() { return true; /* file is beeing used */ wchar_t buf[256]; - FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - buf, (sizeof(buf) / sizeof(wchar_t)), nullptr); + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(wchar_t)), nullptr); + buf[255] = L'\0'; /* even if FormatMessageW fails we've a 0 terminator */ + auto r = wcschr(buf, L'\r'); + if(r) *r = L'\0'; + logger::info("Failed to open file! (%S) (%d)", buf, GetLastError()); return true; } diff --git a/native/updater/file.h b/native/updater/file.h index b40bcc8..07fb085 100644 --- a/native/updater/file.h +++ b/native/updater/file.h @@ -14,6 +14,7 @@ namespace file { extern void drop_backup(const std::string& source); extern std::string register_backup(const std::string& source); extern void rollback(); + extern void commit(); extern bool file_locked(const std::string& file); } \ No newline at end of file diff --git a/native/updater/logger.cpp b/native/updater/logger.cpp index 657789f..b51fc8c 100644 --- a/native/updater/logger.cpp +++ b/native/updater/logger.cpp @@ -6,6 +6,9 @@ #include #include #include +#include +#include + #ifndef WIN32 #include #endif @@ -16,9 +19,15 @@ thread_local std::unique_ptr log_buffer{nullptr, nullptr} std::mutex target_file_lock; std::unique_ptr file_stream; +std::string logging_session; void logger::log_raw(logger::level::value level, const char* format, ...) { if(!log_buffer) log_buffer = std::unique_ptr((char*) malloc(LOG_BUFFER_SIZE), ::free); + if(logging_session.empty()) { + std::ostringstream os; + os << std::uppercase << std::setfill('0') << std::setw(4) << std::hex << (uint32_t) rand(); + logging_session = "[" + os.str() + "]"; + } va_list arg_lst; @@ -32,14 +41,14 @@ void logger::log_raw(logger::level::value level, const char* format, ...) { if(result < 0) { fprintf(stdout, "failed to format log message (%d)\n", result); if(file_stream) - *file_stream << "failed to format log message (" << result << ")\n"; + *file_stream << logging_session << "f ailed to format log message (" << result << ")\n"; } else { fprintf(stdout, "[%d] ", level); fwrite(log_buffer.get(), result, 1, stdout); fprintf(stdout, "\n"); if(file_stream) { - *file_stream << "[" << level << "] "; + *file_stream << logging_session << "[" << level << "] "; file_stream->write(log_buffer.get(), result); *file_stream << "\n"; } diff --git a/native/updater/main.cpp b/native/updater/main.cpp index 5d086fe..91f5b90 100644 --- a/native/updater/main.cpp +++ b/native/updater/main.cpp @@ -1,4 +1,4 @@ -#include +#define _CRT_SECURE_NO_WARNINGS // Disable MSVC localtime warning #include #include #include @@ -7,13 +7,12 @@ #include "config.h" #include "util.h" #include "file.h" - using namespace std; using namespace log_helper; -const std::string current_time() { - time_t now = time(0); - struct tm tstruct; +std::string current_time() { + time_t now = time(nullptr); + struct tm tstruct{}; char buf[80]; tstruct = *localtime(&now); strftime(buf, sizeof(buf), "%Y-%m-%d.%X", &tstruct); @@ -82,7 +81,7 @@ static bool daemonize() { std::string log_file_path; int main(int argc, char** argv) { - srand(time(nullptr)); + srand((unsigned int) floor(chrono::system_clock::now().time_since_epoch()).count()); log_file_path = argc > 2 ? argv[1] : "update_installer.log"; //logger::info("Starting log at %s", log_file_path.c_str()); @@ -97,6 +96,11 @@ int main(int argc, char** argv) { }); } + if(argc < 3) { + logger::fatal("Invalid argument count (%d). Exiting...", argc); + return 1; + } + #ifndef WIN32 logger::info("Deamonize process"); @@ -107,11 +111,20 @@ int main(int argc, char** argv) { logger::info("Deamonized process"); #endif - if(argc < 3) { - logger::fatal("Invalid argument count (%d). Exiting...", argc); - return 1; +#ifdef WIN32 + { + auto admin = is_administrator(); + logger::info("App executed as admin: %s", admin ? "yes" : "no"); + if(!admin) { + logger::info("Requesting administrator rights"); + if(!request_administrator(argc, argv)) { + execute_callback_fail_exit("permissions", "failed to get administrator permissions"); + } + logger::info("Admin right granted. New updater instance executes the update now."); + return 0; + } } - +#endif logger::info("loading config from file %s", argv[2]); { string error; @@ -121,19 +134,6 @@ int main(int argc, char** argv) { } } -#ifdef WIN32 - { - auto admin = is_administrator(); - logger::info("App executed as admin: %s", admin ? "yes" : "no"); - if(!admin) { - logger::info("Requesting administrator rights"); - if(!request_administrator()) { - execute_callback_fail_exit("permissions", "failed to get administrator permissions"); - } - } - } -#endif - { logger::info("Awaiting the unlocking of all files"); auto begin = chrono::system_clock::now(); @@ -170,6 +170,10 @@ int main(int argc, char** argv) { } } - logger::info("Update unpacking successfully!"); + if(config::backup) { + logger::info("Cleaning up backup directly"); + file::commit(); + } + logger::info("Update installing successfully!"); execute_callback_success_exit(); } \ No newline at end of file diff --git a/native/updater/util.cpp b/native/updater/util.cpp index 60224da..3367457 100644 --- a/native/updater/util.cpp +++ b/native/updater/util.cpp @@ -81,13 +81,14 @@ inline std::string build_callback_info(const std::string& error_id, const std::s } void execute_callback_fail_exit(const std::string& error, const std::string& error_message) { + file::rollback(); + if(!is_executable(config::callback_file)) { logger::fatal("callback file (%s) is not executable! Ignoring fail callback", config::callback_file.c_str()); logger::flush(); exit(1); } - file::rollback(); auto cmd_line = config::callback_file + " " + config::callback_argument_fail + build_callback_info(error, error_message); logger::info("executing callback file %s with fail command line %s", config::callback_file.c_str(), cmd_line.c_str()); logger::flush(); @@ -110,50 +111,54 @@ void execute_callback_success_exit() { } #ifdef WIN32 - bool is_administrator() { - BOOL result = false; - DWORD error = ERROR_SUCCESS; - PSID admin_groups = nullptr; - - SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; - if (!AllocateAndInitializeSid( - &NtAuthority, - 2, - SECURITY_BUILTIN_DOMAIN_RID, - DOMAIN_ALIAS_RID_ADMINS, - 0, 0, 0, 0, 0, 0, - &admin_groups)) { - error = GetLastError(); +bool is_administrator() { + bool result{false}; + HANDLE token_handle{nullptr}; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token_handle)) { + TOKEN_ELEVATION eval{}; + DWORD eval_size = sizeof(TOKEN_ELEVATION); + if (GetTokenInformation(token_handle, TokenElevation, &eval, eval_size, &eval_size)) { + result = eval.TokenIsElevated; } - - if (error == ERROR_SUCCESS && !CheckTokenMembership(nullptr, admin_groups, &result)) - error = GetLastError(); - - if (admin_groups) { - FreeSid(admin_groups); - admin_groups = nullptr; - } - - return ERROR_SUCCESS != error && result; } - extern bool request_administrator() { - if(!is_administrator()) { - char szPath[MAX_PATH]; - if (GetModuleFileName(nullptr, szPath, ARRAYSIZE(szPath))) { - SHELLEXECUTEINFO sei = { sizeof(sei) }; + if (token_handle) + CloseHandle(token_handle); - sei.lpVerb = "runas"; - sei.lpFile = szPath; - sei.hwnd = nullptr; - sei.nShow = SW_NORMAL; + return result; +} - if (!ShellExecuteEx(&sei)) { - if (GetLastError() == ERROR_CANCELLED) - return false; - } +extern bool request_administrator(int argc, char** argv) { + char szPath[MAX_PATH]; + if (GetModuleFileName(nullptr, szPath, ARRAYSIZE(szPath))) { + SHELLEXECUTEINFO sei = { sizeof(sei) }; + + sei.lpVerb = "runas"; + sei.lpFile = szPath; + sei.hwnd = nullptr; + sei.nShow = SW_NORMAL; + + if(argc > 1) { + size_t param_size = 0; + for(int i = 1; i < argc; i++) + param_size += strlen(argv[i]) + 1; + sei.lpParameters = (char*) malloc(param_size); + if(!sei.lpParameters) return false; + + auto buf = (char*) sei.lpParameters; + for(int i = 1; i < argc; i++) { + const auto length = strlen(argv[i]); + memcpy(buf, argv[i], length); + buf += length; + *buf++ = ' '; } + *(--buf) = 0; } - return true; + + bool success = ShellExecuteEx(&sei); + if(sei.lpParameters) ::free((void*) sei.lpParameters); + return success; } + return true; +} #endif \ No newline at end of file diff --git a/native/updater/util.h b/native/updater/util.h index cd9479c..77478cc 100644 --- a/native/updater/util.h +++ b/native/updater/util.h @@ -13,5 +13,5 @@ extern __no_return void execute_callback_success_exit(); #ifdef WIN32 extern bool is_administrator(); - extern bool request_administrator(); + extern bool request_administrator(int argc, char** argv); #endif \ No newline at end of file