Basic AppImage Updater
This commit is contained in:
parent
b055980103
commit
00fee9c410
@ -76,6 +76,9 @@ if(BUILD_ARM_COMPONENTS)
|
|||||||
endif()
|
endif()
|
||||||
cmake_language(EVAL CODE "${COMPILE_FLAGS_SETUP}")
|
cmake_language(EVAL CODE "${COMPILE_FLAGS_SETUP}")
|
||||||
|
|
||||||
|
# Build Dependencies
|
||||||
|
add_subdirectory(dependencies)
|
||||||
|
|
||||||
# Fast Math
|
# Fast Math
|
||||||
add_compile_options(-ffast-math)
|
add_compile_options(-ffast-math)
|
||||||
|
|
||||||
@ -91,9 +94,6 @@ if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Build Dependencies
|
|
||||||
add_subdirectory(dependencies)
|
|
||||||
|
|
||||||
# Build libreborn
|
# Build libreborn
|
||||||
add_subdirectory(libreborn)
|
add_subdirectory(libreborn)
|
||||||
|
|
||||||
|
@ -291,10 +291,12 @@ void ConfigurationUI::draw_about() {
|
|||||||
Updater *updater = Updater::instance;
|
Updater *updater = Updater::instance;
|
||||||
if (updater) {
|
if (updater) {
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
ImGui::BeginDisabled(!updater->can_start());
|
||||||
draw_right_aligned_buttons({updater->get_status().c_str()}, [&updater](__attribute__((unused)) int id, const bool was_clicked) {
|
draw_right_aligned_buttons({updater->get_status().c_str()}, [&updater](__attribute__((unused)) int id, const bool was_clicked) {
|
||||||
if (was_clicked) {
|
if (was_clicked) {
|
||||||
updater->start();
|
updater->start();
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
ImGui::EndDisabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "logger/logger.h"
|
#include "logger/logger.h"
|
||||||
#include "util/util.h"
|
#include "util/util.h"
|
||||||
#include "client/configuration.h"
|
#include "client/configuration.h"
|
||||||
|
#include "updater/updater.h"
|
||||||
|
|
||||||
// Bind Options To Environmental Variable
|
// Bind Options To Environmental Variable
|
||||||
static void bind_to_env(const char *env, const bool value) {
|
static void bind_to_env(const char *env, const bool value) {
|
||||||
@ -39,12 +40,34 @@ static void setup_environment(const options_t &options) {
|
|||||||
|
|
||||||
// Non-Launch Commands
|
// Non-Launch Commands
|
||||||
static void handle_non_launch_commands(const options_t &options) {
|
static void handle_non_launch_commands(const options_t &options) {
|
||||||
|
// SDK
|
||||||
if (options.copy_sdk) {
|
if (options.copy_sdk) {
|
||||||
const std::string binary_directory = get_binary_directory();
|
const std::string binary_directory = get_binary_directory();
|
||||||
copy_sdk(binary_directory, true);
|
copy_sdk(binary_directory, true);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
// Updater
|
||||||
|
if (options.run_update) {
|
||||||
|
Updater *updater = Updater::instance;
|
||||||
|
if (updater) {
|
||||||
|
updater->update();
|
||||||
|
if (updater->status == ERROR) {
|
||||||
|
ERR("Unable To Update");
|
||||||
|
} else if (updater->status == UP_TO_DATE) {
|
||||||
|
INFO("Already Up-To-Date");
|
||||||
|
} else {
|
||||||
|
if (updater->status != RESTART_NEEDED) {
|
||||||
|
IMPOSSIBLE();
|
||||||
|
}
|
||||||
|
INFO("Update Completed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ERR("Built-In Updater Unavailable, Use System Package Manager");
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start The Game
|
// Start The Game
|
||||||
|
@ -10,4 +10,5 @@ OPTION(only_generate, "only-generate", -8, "Generate World And Exit (Server-Mode
|
|||||||
OPTION(force_headless, "force-headless", -9, "Force Disable Game Rendering")
|
OPTION(force_headless, "force-headless", -9, "Force Disable Game Rendering")
|
||||||
OPTION(force_non_headless, "force-non-headless", -10, "Force Enable Game Rendering")
|
OPTION(force_non_headless, "force-non-headless", -10, "Force Enable Game Rendering")
|
||||||
OPTION(server_mode, "server", -11, "Run In Server-Mode")
|
OPTION(server_mode, "server", -11, "Run In Server-Mode")
|
||||||
OPTION(skip_pagesize_check, "skip-pagesize-check", -12, "Skip Page-Size Check (Not Recommended)")
|
OPTION(skip_pagesize_check, "skip-pagesize-check", -12, "Skip Page-Size Check (Not Recommended)")
|
||||||
|
OPTION(run_update, "update", -13, "Run Updater (If Available)")
|
@ -0,0 +1,122 @@
|
|||||||
|
#include <cstdlib>
|
||||||
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <libreborn/log.h>
|
||||||
|
#include <libreborn/util/exec.h>
|
||||||
|
#include <libreborn/config.h>
|
||||||
|
|
||||||
|
#include "updater.h"
|
||||||
|
|
||||||
|
// Implement
|
||||||
|
struct AppImageUpdater final : Updater {
|
||||||
|
void update() override;
|
||||||
|
void restart() override;
|
||||||
|
static AppImageUpdater instance;
|
||||||
|
};
|
||||||
|
AppImageUpdater AppImageUpdater::instance;
|
||||||
|
|
||||||
|
// Update
|
||||||
|
template <typename... Args>
|
||||||
|
static std::optional<std::string> run_wget(Args... args) {
|
||||||
|
int status = 0;
|
||||||
|
const char *const command[] = {"wget", "-O", std::forward<Args>(args)..., nullptr};
|
||||||
|
const std::vector<unsigned char> *output = run_command(command, &status);
|
||||||
|
std::string output_str = (const char *) output->data();
|
||||||
|
delete output;
|
||||||
|
if (!is_exit_status_success(status)) {
|
||||||
|
return std::nullopt;
|
||||||
|
} else {
|
||||||
|
return output_str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static std::string extract_from_json(const std::string &json_str, const std::string &key) {
|
||||||
|
std::string::size_type pos = json_str.find(key);
|
||||||
|
std::array<std::string::size_type, 3> indices = {};
|
||||||
|
unsigned int i = 0;
|
||||||
|
while (true) {
|
||||||
|
if (pos == std::string::npos) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (i >= indices.size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos = json_str.find('"', pos) + 1;
|
||||||
|
indices[i++] = pos;
|
||||||
|
}
|
||||||
|
const std::string::size_type start = indices[1];
|
||||||
|
const std::string::size_type end = indices[2];
|
||||||
|
return json_str.substr(start, end - start - 1);
|
||||||
|
}
|
||||||
|
static const char *get_appimage_path() {
|
||||||
|
const char *path = getenv("APPIMAGE");
|
||||||
|
if (path == nullptr) {
|
||||||
|
IMPOSSIBLE();
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
void AppImageUpdater::update() {
|
||||||
|
// Check
|
||||||
|
if (status != CHECKING) {
|
||||||
|
IMPOSSIBLE();
|
||||||
|
}
|
||||||
|
const std::optional<std::string> json = run_wget("-", MCPI_APPIMAGE_JSON_URL);
|
||||||
|
if (!json.has_value()) {
|
||||||
|
status = ERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const std::string tag_name = extract_from_json(json.value(), "tag_name");
|
||||||
|
|
||||||
|
// Check Version
|
||||||
|
if (tag_name == MCPI_VERSION) {
|
||||||
|
status = UP_TO_DATE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get URL
|
||||||
|
std::string url = MCPI_APPIMAGE_DOWNLOAD_URL;
|
||||||
|
while (true) {
|
||||||
|
const std::string placeholder = MCPI_APPIMAGE_VERSION_PLACEHOLDER;
|
||||||
|
const std::string::size_type pos = url.find(placeholder);
|
||||||
|
if (pos == std::string::npos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
url.replace(pos, placeholder.size(), tag_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Path
|
||||||
|
const char *appimage_path = get_appimage_path();
|
||||||
|
const std::string new_appimage_path_base = std::string(appimage_path) + ".new";
|
||||||
|
std::string new_appimage_path = new_appimage_path_base;
|
||||||
|
int num = 1;
|
||||||
|
while (access(new_appimage_path.c_str(), F_OK) != -1) {
|
||||||
|
new_appimage_path = new_appimage_path_base + '.' + std::to_string(num++);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download
|
||||||
|
status = DOWNLOADING;
|
||||||
|
const std::optional<std::string> out = run_wget(new_appimage_path.c_str(), url.c_str());
|
||||||
|
bool ret = out.has_value();
|
||||||
|
if (ret) {
|
||||||
|
ret = chmod(new_appimage_path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0;
|
||||||
|
}
|
||||||
|
if (ret) {
|
||||||
|
ret = rename(new_appimage_path.c_str(), appimage_path) == 0;
|
||||||
|
}
|
||||||
|
if (!ret) {
|
||||||
|
unlink(new_appimage_path.c_str());
|
||||||
|
status = ERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done
|
||||||
|
status = RESTART_NEEDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart
|
||||||
|
void AppImageUpdater::restart() {
|
||||||
|
const char *const command[] = {get_appimage_path(), nullptr};
|
||||||
|
safe_execvpe(command, environ);
|
||||||
|
}
|
@ -20,6 +20,7 @@ std::string Updater::get_status() const {
|
|||||||
case CHECKING: return "Checking...";
|
case CHECKING: return "Checking...";
|
||||||
case UP_TO_DATE: return "Up-To-Date";
|
case UP_TO_DATE: return "Up-To-Date";
|
||||||
case DOWNLOADING: return "Downloading...";
|
case DOWNLOADING: return "Downloading...";
|
||||||
|
case ERROR: return "Error";
|
||||||
default: return "";
|
default: return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,6 +34,7 @@ static void *update_thread(void *data) {
|
|||||||
void Updater::start() {
|
void Updater::start() {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case NOT_STARTED: {
|
case NOT_STARTED: {
|
||||||
|
status = CHECKING;
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
pthread_create(&thread, nullptr, update_thread, this);
|
pthread_create(&thread, nullptr, update_thread, this);
|
||||||
break;
|
break;
|
||||||
|
@ -8,7 +8,8 @@ enum UpdateStatus {
|
|||||||
CHECKING,
|
CHECKING,
|
||||||
UP_TO_DATE,
|
UP_TO_DATE,
|
||||||
DOWNLOADING,
|
DOWNLOADING,
|
||||||
RESTART_NEEDED
|
RESTART_NEEDED,
|
||||||
|
ERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
// Updater
|
// Updater
|
||||||
|
@ -142,7 +142,7 @@ std::vector<unsigned char> *run_command(const char *const command[], int *exit_s
|
|||||||
// Child Process
|
// Child Process
|
||||||
reborn_debug_tag = CHILD_PROCESS_TAG;
|
reborn_debug_tag = CHILD_PROCESS_TAG;
|
||||||
// Run
|
// Run
|
||||||
safe_execvpe(command, (const char *const *) environ);
|
safe_execvpe(command, environ);
|
||||||
} else {
|
} else {
|
||||||
// Read stdout
|
// Read stdout
|
||||||
std::vector<unsigned char> *output = new std::vector<unsigned char>;
|
std::vector<unsigned char> *output = new std::vector<unsigned char>;
|
||||||
|
@ -49,7 +49,15 @@ export function getDebianVersion() {
|
|||||||
|
|
||||||
// Make File Executable
|
// Make File Executable
|
||||||
export function makeExecutable(path) {
|
export function makeExecutable(path) {
|
||||||
fs.chmodSync(path, 0o755);
|
fs.chmodSync(path,
|
||||||
|
fs.constants.S_IRUSR |
|
||||||
|
fs.constants.S_IWUSR |
|
||||||
|
fs.constants.S_IXUSR |
|
||||||
|
fs.constants.S_IRGRP |
|
||||||
|
fs.constants.S_IXGRP |
|
||||||
|
fs.constants.S_IROTH |
|
||||||
|
fs.constants.S_IXOTH
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Scripts Directory
|
// Get Scripts Directory
|
||||||
|
Loading…
x
Reference in New Issue
Block a user