fixed updater for windows

This commit is contained in:
WolverinDEV 2019-10-27 21:50:25 +01:00
parent 1864a19c63
commit 4ded40e7ba
6 changed files with 116 additions and 81 deletions

View File

@ -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<string, string>& 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<string, string>& 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;
}

View File

@ -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);
}

View File

@ -6,6 +6,9 @@
#include <iostream>
#include <fstream>
#include <memory>
#include <iomanip>
#include <sstream>
#ifndef WIN32
#include <cstdarg>
#endif
@ -16,9 +19,15 @@ thread_local std::unique_ptr<char, decltype(free)*> log_buffer{nullptr, nullptr}
std::mutex target_file_lock;
std::unique_ptr<std::ofstream> 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, decltype(free)*>((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";
}

View File

@ -1,4 +1,4 @@
#include <iostream>
#define _CRT_SECURE_NO_WARNINGS // Disable MSVC localtime warning
#include <string>
#include <chrono>
#include <thread>
@ -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::nanoseconds>(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();
}

View File

@ -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

View File

@ -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