mirror of https://github.com/sfeakes/AqualinkD.git
316 lines
8.3 KiB
C
316 lines
8.3 KiB
C
/*
|
|
* Copyright (c) 2017 Shaun Feakes - All rights reserved
|
|
*
|
|
* You may use redistribute and/or modify this code under the terms of
|
|
* the GNU General Public License version 2 as published by the
|
|
* Free Software Foundation. For the terms of this license,
|
|
* see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* You are free to use this software under the terms of the GNU General
|
|
* Public License, but WITHOUT ANY WARRANTY; without even the implied
|
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* https://github.com/sfeakes/aqualinkd
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/statvfs.h>
|
|
#include <sys/wait.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "aqualink.h"
|
|
#include "aq_scheduler.h"
|
|
#include "aq_systemutils.h"
|
|
|
|
bool remount_root_ro(bool readonly)
|
|
{
|
|
|
|
#ifdef AQ_CONTAINER
|
|
// In container this is pointless
|
|
return false;
|
|
#endif
|
|
|
|
if (readonly)
|
|
{
|
|
LOG(AQUA_LOG, LOG_INFO, "reMounting root RO\n");
|
|
mount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
struct statvfs fsinfo;
|
|
statvfs("/", &fsinfo);
|
|
if ((fsinfo.f_flag & ST_RDONLY) == 0) // We are readwrite, ignore
|
|
return false;
|
|
|
|
LOG(AQUA_LOG, LOG_INFO, "reMounting root RW\n");
|
|
mount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
FILE *aq_open_file(char *filename, bool *ro_root, bool *created_file)
|
|
{
|
|
FILE *fp;
|
|
|
|
*ro_root = remount_root_ro(false);
|
|
|
|
if (access(filename, F_OK) == 0)
|
|
{
|
|
*created_file = true;
|
|
}
|
|
|
|
fp = fopen(filename, "w");
|
|
if (fp == NULL)
|
|
{
|
|
remount_root_ro(*ro_root);
|
|
}
|
|
|
|
return fp;
|
|
}
|
|
|
|
bool aq_close_file(FILE *file, bool ro_root)
|
|
{
|
|
if (file != NULL)
|
|
fclose(file);
|
|
|
|
return remount_root_ro(ro_root);
|
|
}
|
|
|
|
#define BUFFER_SIZE 4096
|
|
|
|
bool copy_file(const char *source_path, const char *destination_path)
|
|
{
|
|
|
|
bool ro_root = remount_root_ro(false);
|
|
|
|
FILE *source_file = fopen(source_path, "rb");
|
|
if (source_file == NULL)
|
|
{
|
|
LOG(AQUA_LOG, LOG_ERR, "Error opening source file: %s\n", source_path);
|
|
remount_root_ro(ro_root);
|
|
return false;
|
|
}
|
|
|
|
FILE *destination_file = fopen(destination_path, "wb");
|
|
if (destination_file == NULL)
|
|
{
|
|
LOG(AQUA_LOG, LOG_ERR, "Error opening source file: %s\n", destination_path);
|
|
fclose(source_file);
|
|
remount_root_ro(ro_root);
|
|
return false;
|
|
}
|
|
|
|
char buffer[BUFFER_SIZE];
|
|
size_t bytes_read;
|
|
|
|
while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, source_file)) > 0)
|
|
{
|
|
if (fwrite(buffer, 1, bytes_read, destination_file) != bytes_read)
|
|
{
|
|
LOG(AQUA_LOG, LOG_ERR, "Error writing to destination file: %s\n", destination_path);
|
|
fclose(source_file);
|
|
fclose(destination_file);
|
|
remount_root_ro(ro_root);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (ferror(source_file))
|
|
{
|
|
LOG(AQUA_LOG, LOG_ERR, "Error reading from source: %s\n", source_path);
|
|
fclose(source_file);
|
|
fclose(destination_file);
|
|
remount_root_ro(ro_root);
|
|
return false;
|
|
}
|
|
|
|
fclose(source_file);
|
|
fclose(destination_file);
|
|
remount_root_ro(ro_root);
|
|
return true;
|
|
}
|
|
|
|
bool run_aqualinkd_upgrade(uint8_t type)
|
|
{
|
|
int pipe_curl_to_bash[2];
|
|
pid_t pid_curl, pid_bash;
|
|
//char *curl_args[] = {"curl", "-fsSl", "http://tiger/scratch/remote_install.sh", NULL};
|
|
char *curl_args[] = {"curl", "-fsSl", "-H", "Accept: application/vnd.github.raw", "https://api.github.com/repos/AqualinkD/AqualinkD/contents/release/remote_install.sh", NULL};
|
|
char *bash_args[] = {"bash", "-s", "--", "", NULL};
|
|
int status_curl, status_bash;
|
|
|
|
if (isMASK_SET(type, CHECKONLY)) {
|
|
bash_args[3] = "check";
|
|
} else {
|
|
if (isMASK_SET(type, INSTALLDEVRELEASE)) {
|
|
bash_args[3] = "development";
|
|
}
|
|
}
|
|
|
|
if (pipe(pipe_curl_to_bash) == -1)
|
|
{
|
|
LOG(AQUA_LOG, LOG_ERR,"Upgrade error, opening pipe");
|
|
return false;
|
|
}
|
|
|
|
// Fork for curl
|
|
pid_curl = fork();
|
|
if (pid_curl == -1)
|
|
{
|
|
LOG(AQUA_LOG, LOG_ERR,"Upgrade error, fork (curl)");
|
|
return false;
|
|
}
|
|
|
|
if (pid_curl == 0)
|
|
{ // Child process (curl)
|
|
close(pipe_curl_to_bash[0]);
|
|
dup2(pipe_curl_to_bash[1], STDOUT_FILENO);
|
|
close(pipe_curl_to_bash[1]);
|
|
execvp("curl", curl_args);
|
|
LOG(AQUA_LOG, LOG_ERR,"Upgrade error, execvp (curl)");
|
|
return false;
|
|
}
|
|
|
|
// Fork for bash
|
|
pid_bash = fork();
|
|
if (pid_bash == -1)
|
|
{
|
|
LOG(AQUA_LOG, LOG_ERR,"Upgrade error, fork (bash)");
|
|
return false;
|
|
}
|
|
|
|
if (pid_bash == 0)
|
|
{ // Child process (bash)
|
|
close(pipe_curl_to_bash[1]);
|
|
dup2(pipe_curl_to_bash[0], STDIN_FILENO);
|
|
close(pipe_curl_to_bash[0]);
|
|
execvp("bash", bash_args);
|
|
LOG(AQUA_LOG, LOG_ERR,"Upgrade error, execvp (bash)");
|
|
return false;
|
|
}
|
|
|
|
// Parent process
|
|
close(pipe_curl_to_bash[0]);
|
|
close(pipe_curl_to_bash[1]);
|
|
|
|
// Wait for curl and get its exit status
|
|
if (waitpid(pid_curl, &status_curl, 0) == -1)
|
|
{
|
|
LOG(AQUA_LOG, LOG_ERR,"Upgrade error, waitpid (curl)");
|
|
return false;
|
|
}
|
|
|
|
// Wait for bash and get its exit status
|
|
if (waitpid(pid_bash, &status_bash, 0) == -1)
|
|
{
|
|
LOG(AQUA_LOG, LOG_ERR,"Upgrade error, waitpid (bash)");
|
|
return false;
|
|
}
|
|
|
|
// Check the exit status of curl
|
|
if (WIFEXITED(status_curl))
|
|
{
|
|
//printf("curl exited with status: %d\n", WEXITSTATUS(status_curl));
|
|
if (WEXITSTATUS(status_curl) != 0) {
|
|
LOG(AQUA_LOG, LOG_ERR,"Upgrade error, curl exited with status: %d\n", WEXITSTATUS(status_curl));
|
|
return false;
|
|
}
|
|
}
|
|
else if (WIFSIGNALED(status_curl))
|
|
{
|
|
//printf("curl terminated by signal: %d\n", WTERMSIG(status_curl));
|
|
LOG(AQUA_LOG, LOG_ERR,"Upgrade error, curl terminated by signal: %d\n", WTERMSIG(status_curl));
|
|
return false;
|
|
}
|
|
|
|
// Check the exit status of bash
|
|
if (WIFEXITED(status_bash))
|
|
{
|
|
//printf("bash exited with status: %d\n", WEXITSTATUS(status_bash));
|
|
if (WEXITSTATUS(status_bash) != 0) {
|
|
LOG(AQUA_LOG, LOG_ERR,"Upgrade error, bash exited with status: %d\n", WEXITSTATUS(status_bash));
|
|
return false;
|
|
}
|
|
}
|
|
else if (WIFSIGNALED(status_bash))
|
|
{
|
|
//printf("bash terminated by signal: %d\n", WTERMSIG(status_bash));
|
|
LOG(AQUA_LOG, LOG_ERR,"Upgrade error, bash terminated by signal: %d\n", WTERMSIG(status_bash));
|
|
return false;
|
|
}
|
|
|
|
//printf("Command execution complete.\n");
|
|
LOG(AQUA_LOG, LOG_NOTICE, "AqualinkD is upgrading!");
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
#if MG_TLS > 0
|
|
|
|
char* read_pem_file(const char* fmt, ...) {
|
|
char filepath[1024];
|
|
|
|
// Build file path from format + arguments
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vsnprintf(filepath, sizeof(filepath), fmt, args);
|
|
va_end(args);
|
|
|
|
FILE* file = fopen(filepath, "rb");
|
|
if (!file) {
|
|
LOG(AQUA_LOG, LOG_ERR, "Failed to open file %s\n",filepath);
|
|
return NULL;
|
|
}
|
|
|
|
// Move to end to check file size
|
|
if (fseek(file, 0, SEEK_END) != 0) {
|
|
LOG(AQUA_LOG, LOG_ERR, "fseek failed on %s\n",filepath);
|
|
fclose(file);
|
|
return NULL;
|
|
}
|
|
|
|
long filesize = ftell(file);
|
|
if (filesize < 0) {
|
|
LOG(AQUA_LOG, LOG_ERR, "ftell failed on %s\n",filepath);
|
|
fclose(file);
|
|
return NULL;
|
|
}
|
|
|
|
// Enforce size sanity check
|
|
if (filesize > MAX_PEM_SIZE) {
|
|
LOG(AQUA_LOG, LOG_ERR, "Error: PEM file %s too large (%ld bytes, limit is %d)\n", filepath, filesize, MAX_PEM_SIZE);
|
|
fclose(file);
|
|
return NULL;
|
|
}
|
|
|
|
// Allocate buffer (+1 for null terminator)
|
|
char* buffer = malloc(filesize + 1);
|
|
if (!buffer) {
|
|
LOG(AQUA_LOG, LOG_ERR, "malloc failed on %s\n",filepath);
|
|
fclose(file);
|
|
return NULL;
|
|
}
|
|
|
|
rewind(file);
|
|
size_t bytesRead = fread(buffer, 1, filesize, file);
|
|
if (bytesRead != (size_t)filesize) {
|
|
LOG(AQUA_LOG, LOG_ERR, "fread failed on %s\n",filepath);
|
|
free(buffer);
|
|
fclose(file);
|
|
return NULL;
|
|
}
|
|
|
|
buffer[filesize] = '\0'; // Null-terminate
|
|
|
|
fclose(file);
|
|
return buffer;
|
|
}
|
|
#endif |