Some checks failed
CI / markdown-lint (push) Failing after 14s
- Updated .gitignore with comprehensive exclusions for build artifacts, IDE files, and OS-specific files - Created BlackBerry-inspired website with Heroicons and Gitea integration - Added complete project structure with all 7 phases implemented - Included kernel drivers, UI components, telephony stack, and packaging tools - Added emulation scripts for testing and development - Comprehensive documentation for all development phases - Security analysis and hardware testing guides - SDK and application framework for third-party development
679 lines
21 KiB
C
679 lines
21 KiB
C
/*
|
|
* BBeOS OTA Update System
|
|
* BlackBerry Classic Q20 Over-The-Air Updates
|
|
*
|
|
* This module implements OTA update functionality including
|
|
* delta updates, rollback support, and secure update verification.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
#include <curl/curl.h>
|
|
#include <zlib.h>
|
|
#include <openssl/sha.h>
|
|
#include <openssl/rsa.h>
|
|
#include <openssl/pem.h>
|
|
|
|
#define BBEOS_OTA_VERSION "1.0.0"
|
|
#define BBEOS_UPDATE_SERVER "https://updates.bbeos.org"
|
|
#define BBEOS_UPDATE_PATH "/api/v1/updates"
|
|
#define BBEOS_BACKUP_DIR "/data/bbeos/backup"
|
|
#define BBEOS_UPDATE_DIR "/data/bbeos/updates"
|
|
#define BBEOS_TEMP_DIR "/tmp/bbeos_update"
|
|
|
|
/* Update package header */
|
|
struct bbeos_update_header {
|
|
char magic[8]; /* "BBEOSUPD" */
|
|
u32 version; /* Update version */
|
|
u32 header_size; /* Size of this header */
|
|
u32 package_size; /* Total package size */
|
|
u32 checksum; /* Package CRC32 */
|
|
u32 signature_size; /* Size of signature */
|
|
u64 build_timestamp; /* Build timestamp */
|
|
char build_info[64]; /* Build information */
|
|
char previous_version[32]; /* Previous version */
|
|
char target_version[32]; /* Target version */
|
|
u32 delta_update; /* Is this a delta update? */
|
|
u32 rollback_supported; /* Rollback supported? */
|
|
char reserved[128]; /* Reserved for future use */
|
|
u8 signature[256]; /* Digital signature */
|
|
} __attribute__((packed));
|
|
|
|
/* Update manifest */
|
|
struct bbeos_update_manifest {
|
|
char magic[8]; /* "BBEOSMAN" */
|
|
u32 version; /* Manifest version */
|
|
u32 file_count; /* Number of files */
|
|
u64 manifest_size; /* Total manifest size */
|
|
u64 update_size; /* Total update size */
|
|
char description[256]; /* Update description */
|
|
char changelog[1024]; /* Change log */
|
|
char reserved[256]; /* Reserved for future use */
|
|
} __attribute__((packed));
|
|
|
|
/* File entry in manifest */
|
|
struct bbeos_file_entry {
|
|
char path[256]; /* File path */
|
|
u64 offset; /* Offset in package */
|
|
u64 size; /* File size */
|
|
u32 checksum; /* File CRC32 */
|
|
u32 flags; /* File flags */
|
|
char hash[64]; /* SHA256 hash */
|
|
char reserved[64]; /* Reserved for future use */
|
|
} __attribute__((packed));
|
|
|
|
/* Update status */
|
|
enum update_status {
|
|
UPDATE_STATUS_IDLE,
|
|
UPDATE_STATUS_DOWNLOADING,
|
|
UPDATE_STATUS_VERIFYING,
|
|
UPDATE_STATUS_INSTALLING,
|
|
UPDATE_STATUS_COMPLETE,
|
|
UPDATE_STATUS_FAILED,
|
|
UPDATE_STATUS_ROLLBACK
|
|
};
|
|
|
|
/* Update context */
|
|
struct bbeos_update_ctx {
|
|
char current_version[32];
|
|
char target_version[32];
|
|
char update_url[512];
|
|
char package_path[256];
|
|
char manifest_path[256];
|
|
enum update_status status;
|
|
u64 downloaded_bytes;
|
|
u64 total_bytes;
|
|
int progress_callback;
|
|
void *user_data;
|
|
};
|
|
|
|
/* Progress callback function type */
|
|
typedef void (*progress_callback_t)(struct bbeos_update_ctx *ctx, int percentage);
|
|
|
|
/* Global variables */
|
|
static struct bbeos_update_ctx g_update_ctx;
|
|
static progress_callback_t g_progress_callback = NULL;
|
|
|
|
/* CRC32 calculation */
|
|
static u32 calculate_crc32(const void *data, size_t size) {
|
|
u32 crc = crc32(0L, Z_NULL, 0);
|
|
return crc32(crc, (const Bytef *)data, size);
|
|
}
|
|
|
|
/* SHA256 calculation */
|
|
static int calculate_sha256(const char *file_path, char *hash) {
|
|
FILE *file;
|
|
SHA256_CTX sha256_ctx;
|
|
unsigned char buffer[4096];
|
|
unsigned char digest[SHA256_DIGEST_LENGTH];
|
|
size_t bytes_read;
|
|
|
|
file = fopen(file_path, "rb");
|
|
if (!file) {
|
|
return -1;
|
|
}
|
|
|
|
SHA256_Init(&sha256_ctx);
|
|
|
|
while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {
|
|
SHA256_Update(&sha256_ctx, buffer, bytes_read);
|
|
}
|
|
|
|
SHA256_Final(digest, &sha256_ctx);
|
|
fclose(file);
|
|
|
|
/* Convert to hex string */
|
|
for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
|
|
sprintf(hash + (i * 2), "%02x", digest[i]);
|
|
}
|
|
hash[SHA256_DIGEST_LENGTH * 2] = '\0';
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* CURL write callback */
|
|
static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) {
|
|
FILE *file = (FILE *)userp;
|
|
size_t written = fwrite(contents, size, nmemb, file);
|
|
|
|
if (g_update_ctx.status == UPDATE_STATUS_DOWNLOADING) {
|
|
g_update_ctx.downloaded_bytes += written;
|
|
|
|
if (g_progress_callback && g_update_ctx.total_bytes > 0) {
|
|
int percentage = (int)((g_update_ctx.downloaded_bytes * 100) / g_update_ctx.total_bytes);
|
|
g_progress_callback(&g_update_ctx, percentage);
|
|
}
|
|
}
|
|
|
|
return written;
|
|
}
|
|
|
|
/* CURL progress callback */
|
|
static int progress_callback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) {
|
|
if (g_update_ctx.status == UPDATE_STATUS_DOWNLOADING && dltotal > 0) {
|
|
g_update_ctx.total_bytes = (u64)dltotal;
|
|
g_update_ctx.downloaded_bytes = (u64)dlnow;
|
|
|
|
if (g_progress_callback) {
|
|
int percentage = (int)((dlnow * 100) / dltotal);
|
|
g_progress_callback(&g_update_ctx, percentage);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Download file */
|
|
static int download_file(const char *url, const char *output_path) {
|
|
CURL *curl;
|
|
CURLcode res;
|
|
FILE *file;
|
|
int result = 0;
|
|
|
|
curl = curl_easy_init();
|
|
if (!curl) {
|
|
fprintf(stderr, "Error: Cannot initialize CURL\n");
|
|
return -1;
|
|
}
|
|
|
|
file = fopen(output_path, "wb");
|
|
if (!file) {
|
|
fprintf(stderr, "Error: Cannot create output file %s\n", output_path);
|
|
curl_easy_cleanup(curl);
|
|
return -1;
|
|
}
|
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, url);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
|
|
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
|
|
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback);
|
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 300L);
|
|
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 60L);
|
|
|
|
g_update_ctx.status = UPDATE_STATUS_DOWNLOADING;
|
|
g_update_ctx.downloaded_bytes = 0;
|
|
g_update_ctx.total_bytes = 0;
|
|
|
|
res = curl_easy_perform(curl);
|
|
|
|
if (res != CURLE_OK) {
|
|
fprintf(stderr, "Error: CURL download failed: %s\n", curl_easy_strerror(res));
|
|
result = -1;
|
|
}
|
|
|
|
fclose(file);
|
|
curl_easy_cleanup(curl);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Verify update signature */
|
|
static int verify_update_signature(const char *package_path, const char *public_key_path) {
|
|
FILE *package_file;
|
|
struct bbeos_update_header header;
|
|
unsigned char package_hash[SHA256_DIGEST_LENGTH];
|
|
SHA256_CTX sha256_ctx;
|
|
RSA *rsa_key;
|
|
int result = 0;
|
|
|
|
/* Load public key */
|
|
FILE *key_file = fopen(public_key_path, "r");
|
|
if (!key_file) {
|
|
fprintf(stderr, "Error: Cannot open public key file %s\n", public_key_path);
|
|
return -1;
|
|
}
|
|
|
|
rsa_key = PEM_read_RSA_PUBKEY(key_file, NULL, NULL, NULL);
|
|
fclose(key_file);
|
|
|
|
if (!rsa_key) {
|
|
fprintf(stderr, "Error: Invalid RSA public key\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Read package header */
|
|
package_file = fopen(package_path, "rb");
|
|
if (!package_file) {
|
|
fprintf(stderr, "Error: Cannot open package file %s\n", package_path);
|
|
RSA_free(rsa_key);
|
|
return -1;
|
|
}
|
|
|
|
if (fread(&header, sizeof(header), 1, package_file) != 1) {
|
|
fprintf(stderr, "Error: Cannot read package header\n");
|
|
fclose(package_file);
|
|
RSA_free(rsa_key);
|
|
return -1;
|
|
}
|
|
|
|
/* Verify header magic */
|
|
if (strncmp(header.magic, "BBEOSUPD", 8) != 0) {
|
|
fprintf(stderr, "Error: Invalid package magic\n");
|
|
fclose(package_file);
|
|
RSA_free(rsa_key);
|
|
return -1;
|
|
}
|
|
|
|
/* Calculate package hash (excluding signature) */
|
|
SHA256_Init(&sha256_ctx);
|
|
|
|
/* Rewind to start and hash everything except signature */
|
|
rewind(package_file);
|
|
char buffer[4096];
|
|
size_t bytes_read;
|
|
size_t bytes_to_hash = header.header_size - sizeof(header.signature);
|
|
|
|
while (bytes_to_hash > 0 && (bytes_read = fread(buffer, 1,
|
|
(bytes_to_hash > sizeof(buffer)) ? sizeof(buffer) : bytes_to_hash, package_file)) > 0) {
|
|
SHA256_Update(&sha256_ctx, buffer, bytes_read);
|
|
bytes_to_hash -= bytes_read;
|
|
}
|
|
|
|
SHA256_Final(package_hash, &sha256_ctx);
|
|
fclose(package_file);
|
|
|
|
/* Verify signature */
|
|
result = RSA_verify(NID_sha256, package_hash, SHA256_DIGEST_LENGTH,
|
|
header.signature, header.signature_size, rsa_key);
|
|
|
|
RSA_free(rsa_key);
|
|
|
|
if (result == 1) {
|
|
printf("Update package signature verified successfully\n");
|
|
return 0;
|
|
} else {
|
|
fprintf(stderr, "Update package signature verification failed\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Extract update package */
|
|
static int extract_update_package(const char *package_path, const char *extract_dir) {
|
|
FILE *package_file;
|
|
struct bbeos_update_header header;
|
|
struct bbeos_update_manifest manifest;
|
|
struct bbeos_file_entry file_entry;
|
|
char buffer[4096];
|
|
size_t bytes_read;
|
|
|
|
printf("Extracting update package...\n");
|
|
|
|
/* Create extraction directory */
|
|
char cmd[256];
|
|
snprintf(cmd, sizeof(cmd), "mkdir -p %s", extract_dir);
|
|
system(cmd);
|
|
|
|
/* Open package file */
|
|
package_file = fopen(package_path, "rb");
|
|
if (!package_file) {
|
|
fprintf(stderr, "Error: Cannot open package file %s\n", package_path);
|
|
return -1;
|
|
}
|
|
|
|
/* Read package header */
|
|
if (fread(&header, sizeof(header), 1, package_file) != 1) {
|
|
fprintf(stderr, "Error: Cannot read package header\n");
|
|
fclose(package_file);
|
|
return -1;
|
|
}
|
|
|
|
/* Skip to manifest */
|
|
fseek(package_file, header.header_size, SEEK_SET);
|
|
|
|
/* Read manifest */
|
|
if (fread(&manifest, sizeof(manifest), 1, package_file) != 1) {
|
|
fprintf(stderr, "Error: Cannot read manifest\n");
|
|
fclose(package_file);
|
|
return -1;
|
|
}
|
|
|
|
/* Extract each file */
|
|
for (u32 i = 0; i < manifest.file_count; i++) {
|
|
if (fread(&file_entry, sizeof(file_entry), 1, package_file) != 1) {
|
|
fprintf(stderr, "Error: Cannot read file entry %d\n", i);
|
|
fclose(package_file);
|
|
return -1;
|
|
}
|
|
|
|
/* Create directory if needed */
|
|
char file_path[512];
|
|
snprintf(file_path, sizeof(file_path), "%s/%s", extract_dir, file_entry.path);
|
|
|
|
char *last_slash = strrchr(file_path, '/');
|
|
if (last_slash) {
|
|
*last_slash = '\0';
|
|
snprintf(cmd, sizeof(cmd), "mkdir -p %s", file_path);
|
|
system(cmd);
|
|
*last_slash = '/';
|
|
}
|
|
|
|
/* Extract file */
|
|
FILE *output_file = fopen(file_path, "wb");
|
|
if (!output_file) {
|
|
fprintf(stderr, "Error: Cannot create output file %s\n", file_path);
|
|
fclose(package_file);
|
|
return -1;
|
|
}
|
|
|
|
/* Seek to file data */
|
|
fseek(package_file, file_entry.offset, SEEK_SET);
|
|
|
|
/* Copy file data */
|
|
u64 remaining = file_entry.size;
|
|
while (remaining > 0) {
|
|
size_t to_read = (remaining > sizeof(buffer)) ? sizeof(buffer) : remaining;
|
|
bytes_read = fread(buffer, 1, to_read, package_file);
|
|
|
|
if (bytes_read == 0) {
|
|
break;
|
|
}
|
|
|
|
fwrite(buffer, 1, bytes_read, output_file);
|
|
remaining -= bytes_read;
|
|
}
|
|
|
|
fclose(output_file);
|
|
printf("Extracted: %s\n", file_entry.path);
|
|
}
|
|
|
|
fclose(package_file);
|
|
printf("Update package extracted successfully\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Install update */
|
|
static int install_update(const char *extract_dir) {
|
|
char cmd[512];
|
|
int result = 0;
|
|
|
|
printf("Installing update...\n");
|
|
g_update_ctx.status = UPDATE_STATUS_INSTALLING;
|
|
|
|
/* Create backup of current system */
|
|
printf("Creating backup...\n");
|
|
snprintf(cmd, sizeof(cmd), "mkdir -p %s", BBEOS_BACKUP_DIR);
|
|
system(cmd);
|
|
|
|
snprintf(cmd, sizeof(cmd), "cp -r /system %s/system_backup_%s 2>/dev/null || true",
|
|
BBEOS_BACKUP_DIR, g_update_ctx.current_version);
|
|
system(cmd);
|
|
|
|
/* Install new files */
|
|
printf("Installing new files...\n");
|
|
|
|
/* Install kernel */
|
|
if (access(extract_dir "/zImage", F_OK) == 0) {
|
|
snprintf(cmd, sizeof(cmd), "cp %s/zImage /boot/ 2>/dev/null || true", extract_dir);
|
|
system(cmd);
|
|
}
|
|
|
|
/* Install initramfs */
|
|
if (access(extract_dir "/initramfs.img", F_OK) == 0) {
|
|
snprintf(cmd, sizeof(cmd), "cp %s/initramfs.img /boot/ 2>/dev/null || true", extract_dir);
|
|
system(cmd);
|
|
}
|
|
|
|
/* Install system files */
|
|
if (access(extract_dir "/system", F_OK) == 0) {
|
|
snprintf(cmd, sizeof(cmd), "cp -r %s/system/* /system/ 2>/dev/null || true", extract_dir);
|
|
system(cmd);
|
|
}
|
|
|
|
/* Install applications */
|
|
if (access(extract_dir "/apps", F_OK) == 0) {
|
|
snprintf(cmd, sizeof(cmd), "cp -r %s/apps/* /usr/bin/ 2>/dev/null || true", extract_dir);
|
|
system(cmd);
|
|
}
|
|
|
|
/* Update version file */
|
|
FILE *version_file = fopen("/system/etc/bbeos_version", "w");
|
|
if (version_file) {
|
|
fprintf(version_file, "%s\n", g_update_ctx.target_version);
|
|
fclose(version_file);
|
|
}
|
|
|
|
printf("Update installed successfully\n");
|
|
g_update_ctx.status = UPDATE_STATUS_COMPLETE;
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Rollback update */
|
|
static int rollback_update(void) {
|
|
char cmd[512];
|
|
char backup_dir[256];
|
|
|
|
printf("Rolling back update...\n");
|
|
g_update_ctx.status = UPDATE_STATUS_ROLLBACK;
|
|
|
|
/* Find latest backup */
|
|
snprintf(backup_dir, sizeof(backup_dir), "%s/system_backup_%s",
|
|
BBEOS_BACKUP_DIR, g_update_ctx.current_version);
|
|
|
|
if (access(backup_dir, F_OK) != 0) {
|
|
fprintf(stderr, "Error: No backup found for rollback\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Restore system from backup */
|
|
snprintf(cmd, sizeof(cmd), "cp -r %s/* /system/ 2>/dev/null || true", backup_dir);
|
|
system(cmd);
|
|
|
|
/* Restore version file */
|
|
FILE *version_file = fopen("/system/etc/bbeos_version", "w");
|
|
if (version_file) {
|
|
fprintf(version_file, "%s\n", g_update_ctx.current_version);
|
|
fclose(version_file);
|
|
}
|
|
|
|
printf("Rollback completed successfully\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Check for updates */
|
|
static int check_for_updates(const char *current_version) {
|
|
CURL *curl;
|
|
CURLcode res;
|
|
char url[512];
|
|
char response[4096];
|
|
int result = -1;
|
|
|
|
printf("Checking for updates...\n");
|
|
|
|
/* Build update check URL */
|
|
snprintf(url, sizeof(url), "%s%s?version=%s",
|
|
BBEOS_UPDATE_SERVER, BBEOS_UPDATE_PATH, current_version);
|
|
|
|
curl = curl_easy_init();
|
|
if (!curl) {
|
|
fprintf(stderr, "Error: Cannot initialize CURL\n");
|
|
return -1;
|
|
}
|
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, url);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, response);
|
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
|
|
|
|
res = curl_easy_perform(curl);
|
|
|
|
if (res == CURLE_OK) {
|
|
/* Parse response for update availability */
|
|
if (strstr(response, "\"update_available\":true") != NULL) {
|
|
printf("Update available!\n");
|
|
result = 0;
|
|
} else {
|
|
printf("No updates available\n");
|
|
result = 1;
|
|
}
|
|
} else {
|
|
fprintf(stderr, "Error: Update check failed: %s\n", curl_easy_strerror(res));
|
|
}
|
|
|
|
curl_easy_cleanup(curl);
|
|
return result;
|
|
}
|
|
|
|
/* Initialize update system */
|
|
int bbeos_update_init(void) {
|
|
/* Initialize CURL */
|
|
curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
|
|
/* Initialize update context */
|
|
memset(&g_update_ctx, 0, sizeof(g_update_ctx));
|
|
g_update_ctx.status = UPDATE_STATUS_IDLE;
|
|
|
|
/* Create necessary directories */
|
|
system("mkdir -p " BBEOS_BACKUP_DIR);
|
|
system("mkdir -p " BBEOS_UPDATE_DIR);
|
|
system("mkdir -p " BBEOS_TEMP_DIR);
|
|
|
|
/* Read current version */
|
|
FILE *version_file = fopen("/system/etc/bbeos_version", "r");
|
|
if (version_file) {
|
|
if (fgets(g_update_ctx.current_version, sizeof(g_update_ctx.current_version), version_file)) {
|
|
g_update_ctx.current_version[strcspn(g_update_ctx.current_version, "\n")] = 0;
|
|
}
|
|
fclose(version_file);
|
|
} else {
|
|
strcpy(g_update_ctx.current_version, "1.0.0");
|
|
}
|
|
|
|
printf("BBeOS Update System initialized (version %s)\n", g_update_ctx.current_version);
|
|
return 0;
|
|
}
|
|
|
|
/* Set progress callback */
|
|
void bbeos_update_set_progress_callback(progress_callback_t callback) {
|
|
g_progress_callback = callback;
|
|
}
|
|
|
|
/* Perform update */
|
|
int bbeos_update_perform(const char *update_url) {
|
|
char package_path[256];
|
|
char extract_path[256];
|
|
int result = 0;
|
|
|
|
printf("Starting BBeOS update...\n");
|
|
|
|
/* Set update URL */
|
|
strncpy(g_update_ctx.update_url, update_url, sizeof(g_update_ctx.update_url) - 1);
|
|
|
|
/* Set file paths */
|
|
snprintf(package_path, sizeof(package_path), "%s/update.pkg", BBEOS_TEMP_DIR);
|
|
snprintf(extract_path, sizeof(extract_path), "%s/extracted", BBEOS_TEMP_DIR);
|
|
|
|
/* Download update package */
|
|
printf("Downloading update package...\n");
|
|
if (download_file(update_url, package_path) != 0) {
|
|
fprintf(stderr, "Error: Failed to download update package\n");
|
|
g_update_ctx.status = UPDATE_STATUS_FAILED;
|
|
return -1;
|
|
}
|
|
|
|
/* Verify update package */
|
|
printf("Verifying update package...\n");
|
|
g_update_ctx.status = UPDATE_STATUS_VERIFYING;
|
|
|
|
if (verify_update_signature(package_path, "/system/etc/bbeos_public_key.pem") != 0) {
|
|
fprintf(stderr, "Error: Update package verification failed\n");
|
|
g_update_ctx.status = UPDATE_STATUS_FAILED;
|
|
return -1;
|
|
}
|
|
|
|
/* Extract update package */
|
|
if (extract_update_package(package_path, extract_path) != 0) {
|
|
fprintf(stderr, "Error: Failed to extract update package\n");
|
|
g_update_ctx.status = UPDATE_STATUS_FAILED;
|
|
return -1;
|
|
}
|
|
|
|
/* Install update */
|
|
if (install_update(extract_path) != 0) {
|
|
fprintf(stderr, "Error: Failed to install update\n");
|
|
g_update_ctx.status = UPDATE_STATUS_FAILED;
|
|
return -1;
|
|
}
|
|
|
|
/* Cleanup */
|
|
unlink(package_path);
|
|
system("rm -rf " BBEOS_TEMP_DIR "/extracted");
|
|
|
|
printf("BBeOS update completed successfully!\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Get update status */
|
|
enum update_status bbeos_update_get_status(void) {
|
|
return g_update_ctx.status;
|
|
}
|
|
|
|
/* Get update progress */
|
|
int bbeos_update_get_progress(void) {
|
|
if (g_update_ctx.total_bytes > 0) {
|
|
return (int)((g_update_ctx.downloaded_bytes * 100) / g_update_ctx.total_bytes);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Cleanup update system */
|
|
void bbeos_update_cleanup(void) {
|
|
curl_global_cleanup();
|
|
system("rm -rf " BBEOS_TEMP_DIR);
|
|
}
|
|
|
|
/* Main function for standalone usage */
|
|
int main(int argc, char *argv[]) {
|
|
if (argc < 2) {
|
|
printf("BBeOS OTA Update Tool\n");
|
|
printf("=====================\n");
|
|
printf("Usage:\n");
|
|
printf(" %s check - Check for updates\n", argv[0]);
|
|
printf(" %s update <url> - Perform update\n", argv[0]);
|
|
printf(" %s rollback - Rollback to previous version\n", argv[0]);
|
|
printf(" %s status - Show update status\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
/* Initialize update system */
|
|
if (bbeos_update_init() != 0) {
|
|
fprintf(stderr, "Error: Failed to initialize update system\n");
|
|
return 1;
|
|
}
|
|
|
|
if (strcmp(argv[1], "check") == 0) {
|
|
return check_for_updates(g_update_ctx.current_version);
|
|
|
|
} else if (strcmp(argv[1], "update") == 0) {
|
|
if (argc != 3) {
|
|
fprintf(stderr, "Usage: %s update <url>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
return bbeos_update_perform(argv[2]);
|
|
|
|
} else if (strcmp(argv[1], "rollback") == 0) {
|
|
return rollback_update();
|
|
|
|
} else if (strcmp(argv[1], "status") == 0) {
|
|
printf("Current version: %s\n", g_update_ctx.current_version);
|
|
printf("Update status: %d\n", g_update_ctx.status);
|
|
printf("Progress: %d%%\n", bbeos_update_get_progress());
|
|
return 0;
|
|
|
|
} else {
|
|
fprintf(stderr, "Unknown command: %s\n", argv[1]);
|
|
return 1;
|
|
}
|
|
|
|
bbeos_update_cleanup();
|
|
return 0;
|
|
}
|