fixed updater for windows
This commit is contained in:
parent
1864a19c63
commit
4ded40e7ba
@ -104,7 +104,7 @@ bool file::move(std::string &error, const std::string &source, const std::string
|
|||||||
try {
|
try {
|
||||||
fs::create_directories(target_path.parent_path());
|
fs::create_directories(target_path.parent_path());
|
||||||
} catch(const fs::filesystem_error& ex) {
|
} catch(const fs::filesystem_error& ex) {
|
||||||
error = "failed to create target directories";
|
error = "failed to create target directories (" + std::string{ex.what()} + ")";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -200,12 +200,12 @@ void file::rollback() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
logger::info("Rollbacking %d moved files", move_back.size());
|
logger::info("Rollbacking %d moved files", move_back.size());
|
||||||
for(const pair<string, string>& key : move_back) {
|
for(const auto& [backup, original] : move_back) {
|
||||||
logger::debug("Attempting to moveback %s to %s", key.first.c_str(), key.second.c_str());
|
logger::debug("Attempting to moveback %s to %s", backup.c_str(), original.c_str());
|
||||||
try {
|
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) {
|
} 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;
|
continue;
|
||||||
}
|
}
|
||||||
logger::debug("=> success");
|
logger::debug("=> success");
|
||||||
@ -213,19 +213,19 @@ void file::rollback() {
|
|||||||
|
|
||||||
logger::info("Rollbacking %d deleted files", backup_mapping.size());
|
logger::info("Rollbacking %d deleted files", backup_mapping.size());
|
||||||
|
|
||||||
for(const pair<string, string>& key : backup_mapping) {
|
for(const auto& [backup, original] : backup_mapping) {
|
||||||
logger::debug("Attempting to restore %s to %s", key.first.c_str(), key.second.c_str());
|
logger::debug("Attempting to restore %s to %s", backup.c_str(), original.c_str());
|
||||||
try {
|
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)) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto target = fs::u8path(key.second);
|
auto target = fs::u8path(original);
|
||||||
fs::rename(source, target);
|
fs::rename(source, target);
|
||||||
} catch(const fs::filesystem_error& ex) {
|
} 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;
|
continue;
|
||||||
}
|
}
|
||||||
logger::debug("=> success");
|
logger::debug("=> success");
|
||||||
@ -233,6 +233,20 @@ void file::rollback() {
|
|||||||
logger::info("Rollback done");
|
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
|
#ifdef WIN32
|
||||||
bool file::file_locked(const std::string &file) {
|
bool file::file_locked(const std::string &file) {
|
||||||
auto file_handle = CreateFile(
|
auto file_handle = CreateFile(
|
||||||
@ -240,7 +254,7 @@ void file::rollback() {
|
|||||||
(DWORD) GENERIC_WRITE,
|
(DWORD) GENERIC_WRITE,
|
||||||
(DWORD) 0, /* we dont want to share */
|
(DWORD) 0, /* we dont want to share */
|
||||||
(LPSECURITY_ATTRIBUTES) nullptr,
|
(LPSECURITY_ATTRIBUTES) nullptr,
|
||||||
(DWORD) OPEN_EXISTING, /* file should be availible */
|
(DWORD) OPEN_EXISTING, /* file should be available */
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
nullptr
|
nullptr
|
||||||
);
|
);
|
||||||
@ -249,9 +263,11 @@ void file::rollback() {
|
|||||||
return true; /* file is beeing used */
|
return true; /* file is beeing used */
|
||||||
|
|
||||||
wchar_t buf[256];
|
wchar_t buf[256];
|
||||||
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(wchar_t)), nullptr);
|
||||||
nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
buf[255] = L'\0'; /* even if FormatMessageW fails we've a 0 terminator */
|
||||||
buf, (sizeof(buf) / sizeof(wchar_t)), nullptr);
|
auto r = wcschr(buf, L'\r');
|
||||||
|
if(r) *r = L'\0';
|
||||||
|
|
||||||
logger::info("Failed to open file! (%S) (%d)", buf, GetLastError());
|
logger::info("Failed to open file! (%S) (%d)", buf, GetLastError());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ namespace file {
|
|||||||
extern void drop_backup(const std::string& source);
|
extern void drop_backup(const std::string& source);
|
||||||
extern std::string register_backup(const std::string& source);
|
extern std::string register_backup(const std::string& source);
|
||||||
extern void rollback();
|
extern void rollback();
|
||||||
|
extern void commit();
|
||||||
|
|
||||||
extern bool file_locked(const std::string& file);
|
extern bool file_locked(const std::string& file);
|
||||||
}
|
}
|
@ -6,6 +6,9 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#endif
|
#endif
|
||||||
@ -16,9 +19,15 @@ thread_local std::unique_ptr<char, decltype(free)*> log_buffer{nullptr, nullptr}
|
|||||||
std::mutex target_file_lock;
|
std::mutex target_file_lock;
|
||||||
std::unique_ptr<std::ofstream> file_stream;
|
std::unique_ptr<std::ofstream> file_stream;
|
||||||
|
|
||||||
|
std::string logging_session;
|
||||||
void logger::log_raw(logger::level::value level, const char* format, ...) {
|
void logger::log_raw(logger::level::value level, const char* format, ...) {
|
||||||
if(!log_buffer)
|
if(!log_buffer)
|
||||||
log_buffer = std::unique_ptr<char, decltype(free)*>((char*) malloc(LOG_BUFFER_SIZE), ::free);
|
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;
|
va_list arg_lst;
|
||||||
@ -32,14 +41,14 @@ void logger::log_raw(logger::level::value level, const char* format, ...) {
|
|||||||
if(result < 0) {
|
if(result < 0) {
|
||||||
fprintf(stdout, "failed to format log message (%d)\n", result);
|
fprintf(stdout, "failed to format log message (%d)\n", result);
|
||||||
if(file_stream)
|
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 {
|
} else {
|
||||||
fprintf(stdout, "[%d] ", level);
|
fprintf(stdout, "[%d] ", level);
|
||||||
fwrite(log_buffer.get(), result, 1, stdout);
|
fwrite(log_buffer.get(), result, 1, stdout);
|
||||||
fprintf(stdout, "\n");
|
fprintf(stdout, "\n");
|
||||||
|
|
||||||
if(file_stream) {
|
if(file_stream) {
|
||||||
*file_stream << "[" << level << "] ";
|
*file_stream << logging_session << "[" << level << "] ";
|
||||||
file_stream->write(log_buffer.get(), result);
|
file_stream->write(log_buffer.get(), result);
|
||||||
*file_stream << "\n";
|
*file_stream << "\n";
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include <iostream>
|
#define _CRT_SECURE_NO_WARNINGS // Disable MSVC localtime warning
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -7,13 +7,12 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace log_helper;
|
using namespace log_helper;
|
||||||
|
|
||||||
const std::string current_time() {
|
std::string current_time() {
|
||||||
time_t now = time(0);
|
time_t now = time(nullptr);
|
||||||
struct tm tstruct;
|
struct tm tstruct{};
|
||||||
char buf[80];
|
char buf[80];
|
||||||
tstruct = *localtime(&now);
|
tstruct = *localtime(&now);
|
||||||
strftime(buf, sizeof(buf), "%Y-%m-%d.%X", &tstruct);
|
strftime(buf, sizeof(buf), "%Y-%m-%d.%X", &tstruct);
|
||||||
@ -82,7 +81,7 @@ static bool daemonize() {
|
|||||||
|
|
||||||
std::string log_file_path;
|
std::string log_file_path;
|
||||||
int main(int argc, char** argv) {
|
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";
|
log_file_path = argc > 2 ? argv[1] : "update_installer.log";
|
||||||
//logger::info("Starting log at %s", log_file_path.c_str());
|
//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
|
#ifndef WIN32
|
||||||
logger::info("Deamonize process");
|
logger::info("Deamonize process");
|
||||||
@ -107,11 +111,20 @@ int main(int argc, char** argv) {
|
|||||||
logger::info("Deamonized process");
|
logger::info("Deamonized process");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(argc < 3) {
|
#ifdef WIN32
|
||||||
logger::fatal("Invalid argument count (%d). Exiting...", argc);
|
{
|
||||||
return 1;
|
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]);
|
logger::info("loading config from file %s", argv[2]);
|
||||||
{
|
{
|
||||||
string error;
|
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");
|
logger::info("Awaiting the unlocking of all files");
|
||||||
auto begin = chrono::system_clock::now();
|
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();
|
execute_callback_success_exit();
|
||||||
}
|
}
|
@ -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) {
|
void execute_callback_fail_exit(const std::string& error, const std::string& error_message) {
|
||||||
|
file::rollback();
|
||||||
|
|
||||||
if(!is_executable(config::callback_file)) {
|
if(!is_executable(config::callback_file)) {
|
||||||
logger::fatal("callback file (%s) is not executable! Ignoring fail callback", config::callback_file.c_str());
|
logger::fatal("callback file (%s) is not executable! Ignoring fail callback", config::callback_file.c_str());
|
||||||
logger::flush();
|
logger::flush();
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
file::rollback();
|
|
||||||
auto cmd_line = config::callback_file + " " + config::callback_argument_fail + build_callback_info(error, error_message);
|
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::info("executing callback file %s with fail command line %s", config::callback_file.c_str(), cmd_line.c_str());
|
||||||
logger::flush();
|
logger::flush();
|
||||||
@ -110,35 +111,24 @@ void execute_callback_success_exit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
bool is_administrator() {
|
bool is_administrator() {
|
||||||
BOOL result = false;
|
bool result{false};
|
||||||
DWORD error = ERROR_SUCCESS;
|
HANDLE token_handle{nullptr};
|
||||||
PSID admin_groups = nullptr;
|
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token_handle)) {
|
||||||
|
TOKEN_ELEVATION eval{};
|
||||||
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
DWORD eval_size = sizeof(TOKEN_ELEVATION);
|
||||||
if (!AllocateAndInitializeSid(
|
if (GetTokenInformation(token_handle, TokenElevation, &eval, eval_size, &eval_size)) {
|
||||||
&NtAuthority,
|
result = eval.TokenIsElevated;
|
||||||
2,
|
}
|
||||||
SECURITY_BUILTIN_DOMAIN_RID,
|
|
||||||
DOMAIN_ALIAS_RID_ADMINS,
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
&admin_groups)) {
|
|
||||||
error = GetLastError();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error == ERROR_SUCCESS && !CheckTokenMembership(nullptr, admin_groups, &result))
|
if (token_handle)
|
||||||
error = GetLastError();
|
CloseHandle(token_handle);
|
||||||
|
|
||||||
if (admin_groups) {
|
return result;
|
||||||
FreeSid(admin_groups);
|
}
|
||||||
admin_groups = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ERROR_SUCCESS != error && result;
|
extern bool request_administrator(int argc, char** argv) {
|
||||||
}
|
|
||||||
|
|
||||||
extern bool request_administrator() {
|
|
||||||
if(!is_administrator()) {
|
|
||||||
char szPath[MAX_PATH];
|
char szPath[MAX_PATH];
|
||||||
if (GetModuleFileName(nullptr, szPath, ARRAYSIZE(szPath))) {
|
if (GetModuleFileName(nullptr, szPath, ARRAYSIZE(szPath))) {
|
||||||
SHELLEXECUTEINFO sei = { sizeof(sei) };
|
SHELLEXECUTEINFO sei = { sizeof(sei) };
|
||||||
@ -148,12 +138,27 @@ void execute_callback_success_exit() {
|
|||||||
sei.hwnd = nullptr;
|
sei.hwnd = nullptr;
|
||||||
sei.nShow = SW_NORMAL;
|
sei.nShow = SW_NORMAL;
|
||||||
|
|
||||||
if (!ShellExecuteEx(&sei)) {
|
if(argc > 1) {
|
||||||
if (GetLastError() == ERROR_CANCELLED)
|
size_t param_size = 0;
|
||||||
return false;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool success = ShellExecuteEx(&sei);
|
||||||
|
if(sei.lpParameters) ::free((void*) sei.lpParameters);
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
@ -13,5 +13,5 @@ extern __no_return void execute_callback_success_exit();
|
|||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
extern bool is_administrator();
|
extern bool is_administrator();
|
||||||
extern bool request_administrator();
|
extern bool request_administrator(int argc, char** argv);
|
||||||
#endif
|
#endif
|
Loading…
x
Reference in New Issue
Block a user