633 lines
15 KiB
C
Executable File
633 lines
15 KiB
C
Executable File
#include "utils.h"
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/wait.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <openssl/evp.h>
|
|
|
|
char _ERROR_BUFFER[ERROR_BUFFER_SIZE];
|
|
|
|
result_t success(void) {
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
result_t failure(const char* format, ...) {
|
|
va_list args;
|
|
va_start(args, format);
|
|
|
|
// Print the error message into the error buffer
|
|
vsnprintf(_ERROR_BUFFER, ERROR_BUFFER_SIZE, format, args);
|
|
|
|
va_end(args);
|
|
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
const char* error(void) {
|
|
return _ERROR_BUFFER;
|
|
}
|
|
|
|
result_t format(char** _str, const char* format, ...) {
|
|
// Initialize the output parameters
|
|
*_str = NULL;
|
|
|
|
// Calculate the length of the formatted string
|
|
va_list args;
|
|
va_start(args, format);
|
|
|
|
int length = vsnprintf(NULL, 0, format, args);
|
|
|
|
va_end(args);
|
|
|
|
if (length < 0)
|
|
return failure("Failed to calculate the length of the formatted string.");
|
|
|
|
// Allocate memory for the formatted string
|
|
errno = 0;
|
|
char* str = malloc(length + 1);
|
|
if (str == NULL)
|
|
return failure("Failed to allocate memory for the formatted string (%s).", strerror(errno));
|
|
|
|
// Format the string
|
|
va_start(args, format);
|
|
|
|
if (vsnprintf(str, length + 1, format, args) < 0)
|
|
return failure("Failed to format the string.");
|
|
|
|
va_end(args);
|
|
|
|
// Return the formatted string
|
|
*_str = str;
|
|
return success();
|
|
}
|
|
|
|
result_t substring(char** _str, const char* str, size_t start, size_t length) {
|
|
// Initialize the output parameters
|
|
*_str = NULL;
|
|
|
|
// Check the start index
|
|
size_t str_length = strlen(str);
|
|
|
|
if (start > str_length)
|
|
return failure("The start index is out of range.");
|
|
if (start + length > str_length)
|
|
length = str_length - start;
|
|
|
|
// Allocate memory for the substring
|
|
errno = 0;
|
|
char* substr = malloc(length + 1); // +1 for the null terminator
|
|
if (substr == NULL)
|
|
return failure("Failed to allocate memory for the substring (%s).", strerror(errno));
|
|
|
|
// Copy the substring
|
|
memcpy(substr, str + start, length);
|
|
substr[length] = '\0';
|
|
|
|
// Return the substring
|
|
*_str = substr;
|
|
return success();
|
|
}
|
|
|
|
result_t format_size(char** _str, uint64_t size) {
|
|
// Initialize the output parameters
|
|
*_str = NULL;
|
|
|
|
// Format the size
|
|
if (size < 1024ULL)
|
|
return format(_str, "%lluB", size);
|
|
if (size < 1024ULL * 1024)
|
|
return format(_str, "%.2fKiB", size / (1024.0));
|
|
if (size < 1024ULL * 1024 * 1024)
|
|
return format(_str, "%.2fMiB", size / (1024.0 * 1024.0));
|
|
if (size < 1024ULL * 1024 * 1024 * 1024)
|
|
return format(_str, "%.2fGiB", size / (1024.0 * 1024.0 * 1024.0));
|
|
if (size < 1024ULL * 1024 * 1024 * 1024 * 1024)
|
|
return format(_str, "%.2fTiB", size / (1024.0 * 1024.0 * 1024.0 * 1024.0));
|
|
if (size < 1024ULL * 1024 * 1024 * 1024 * 1024 * 1024)
|
|
return format(_str, "%.2fPiB", size / (1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0));
|
|
|
|
return format(_str, "%.2fEiB", size / (1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0));
|
|
}
|
|
|
|
result_t parse_size(uint64_t* _size, const char* str) {
|
|
// Initialize the output parameters
|
|
*_size = 0;
|
|
|
|
// Parse the size
|
|
char* endptr;
|
|
uint64_t size = strtoull(str, &endptr, 10);
|
|
if (endptr == str)
|
|
return failure("Failed to parse the size.");
|
|
|
|
// Check the suffix
|
|
if (*endptr == '\0') {
|
|
// No suffix
|
|
*_size = size;
|
|
return success();
|
|
}
|
|
|
|
// Parse the suffix
|
|
if (endptr[1] != '\0')
|
|
return failure("Invalid size suffix.");
|
|
|
|
switch (endptr[0]) {
|
|
case 'B':
|
|
*_size = size;
|
|
return success();
|
|
case 'K':
|
|
*_size = size * 1024;
|
|
return success();
|
|
case 'M':
|
|
*_size = size * 1024 * 1024;
|
|
return success();
|
|
case 'G':
|
|
*_size = size * 1024 * 1024 * 1024;
|
|
return success();
|
|
case 'T':
|
|
*_size = size * 1024 * 1024 * 1024 * 1024;
|
|
return success();
|
|
case 'P':
|
|
*_size = size * 1024 * 1024 * 1024 * 1024 * 1024;
|
|
return success();
|
|
case 'E':
|
|
*_size = size * 1024 * 1024 * 1024 * 1024 * 1024 * 1024;
|
|
return success();
|
|
default:
|
|
return failure("Invalid size suffix.");
|
|
}
|
|
}
|
|
|
|
result_t execute(int* _exit_code, char** _stdoutbuf, char** _stderrbuf, const char* executable, ...) {
|
|
// Initialize the output parameters
|
|
if (_exit_code != NULL)
|
|
*_exit_code = 0;
|
|
if (_stdoutbuf != NULL)
|
|
*_stdoutbuf = NULL;
|
|
if (_stderrbuf != NULL)
|
|
*_stderrbuf = NULL;
|
|
|
|
// Count the number of arguments to pass to the executable
|
|
int argc = 1; // +1 for the executable itself
|
|
|
|
va_list args;
|
|
va_start(args, executable);
|
|
|
|
while (va_arg(args, const char*) != NULL)
|
|
argc++;
|
|
|
|
va_end(args);
|
|
|
|
// Allocate memory for the arguments array
|
|
errno = 0;
|
|
char** argv = malloc((argc + 1) * sizeof(char*)); // +1 for the NULL terminator
|
|
if (argv == NULL)
|
|
return failure("Failed to allocate memory for the arguments array (%s).", strerror(errno));
|
|
|
|
// Fill the arguments array
|
|
|
|
errno = 0;
|
|
argv[0] = strdup(executable);
|
|
if (argv[0] == NULL) {
|
|
free(argv);
|
|
return failure("Failed to duplicate the executable string (%s).", strerror(errno));
|
|
}
|
|
|
|
va_start(args, executable);
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
argv[i] = strdup(va_arg(args, const char*));
|
|
if (argv[i] == NULL) {
|
|
for (int j = 0; j < i; j++)
|
|
free(argv[j]);
|
|
free(argv);
|
|
va_end(args);
|
|
return failure("Failed to duplicate the argument string (%s).", strerror(errno));
|
|
}
|
|
}
|
|
|
|
va_end(args);
|
|
|
|
argv[argc] = NULL;
|
|
|
|
// Create pipes for the standard output and standard error of the child process
|
|
int stdout_pipe[2];
|
|
errno = 0;
|
|
if (pipe(stdout_pipe) < 0) {
|
|
for (int i = 0; i < argc; i++)
|
|
free(argv[i]);
|
|
free(argv);
|
|
return failure("Failed to create a pipe for the standard output of the child process (%s).", strerror(errno));
|
|
}
|
|
|
|
int stderr_pipe[2];
|
|
errno = 0;
|
|
if (pipe(stderr_pipe) < 0) {
|
|
for (int i = 0; i < argc; i++)
|
|
free(argv[i]);
|
|
free(argv);
|
|
close(stdout_pipe[0]);
|
|
close(stdout_pipe[1]);
|
|
return failure("Failed to create a pipe for the standard error of the child process (%s).", strerror(errno));
|
|
}
|
|
|
|
// Fork the child process
|
|
errno = 0;
|
|
pid_t pid = fork();
|
|
|
|
if (pid < 0) {
|
|
for (int i = 0; i < argc; i++)
|
|
free(argv[i]);
|
|
free(argv);
|
|
close(stdout_pipe[0]);
|
|
close(stdout_pipe[1]);
|
|
close(stderr_pipe[0]);
|
|
close(stderr_pipe[1]);
|
|
return failure("Failed to fork the child process (%s).", strerror(errno));
|
|
}
|
|
|
|
if (pid == 0) {
|
|
// Redirect the standard output and standard error of the child process
|
|
close(stdout_pipe[0]);
|
|
close(stderr_pipe[0]);
|
|
|
|
if (dup2(stdout_pipe[1], STDOUT_FILENO) < 0)
|
|
exit(EXIT_FAILURE);
|
|
|
|
if (dup2(stderr_pipe[1], STDERR_FILENO) < 0)
|
|
exit(EXIT_FAILURE);
|
|
|
|
close(stdout_pipe[1]);
|
|
close(stderr_pipe[1]);
|
|
|
|
// Execute the child process
|
|
errno = 0;
|
|
execvp(executable, argv);
|
|
|
|
// If the child process reaches this point, it failed to execute
|
|
fprintf(stderr, "Failed to execute the child process (%s).\n", strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Free the arguments array
|
|
for (int i = 0; i < argc; i++)
|
|
free(argv[i]);
|
|
free(argv);
|
|
|
|
// Close the write ends of the pipes
|
|
close(stdout_pipe[1]);
|
|
close(stderr_pipe[1]);
|
|
|
|
// Wait for the child process to terminate
|
|
int status;
|
|
errno = 0;
|
|
if (waitpid(pid, &status, 0) < 0) {
|
|
close(stdout_pipe[0]);
|
|
close(stderr_pipe[0]);
|
|
return failure("Failed to wait for the child process to terminate (%s).", strerror(errno));
|
|
}
|
|
|
|
// Read the standard output and standard error of the child process
|
|
if (_exit_code != NULL)
|
|
*_exit_code = WEXITSTATUS(status);
|
|
if (_stdoutbuf != NULL)
|
|
read_fd(_stdoutbuf, stdout_pipe[0]);
|
|
if (_stderrbuf != NULL)
|
|
read_fd(_stderrbuf, stderr_pipe[0]);
|
|
|
|
// Close the read ends of the pipes
|
|
close(stdout_pipe[0]);
|
|
close(stderr_pipe[0]);
|
|
|
|
return success();
|
|
}
|
|
|
|
result_t read_fd(char** _str, int fd) {
|
|
// Initialize the output parameters
|
|
*_str = NULL;
|
|
|
|
// Read the file descriptor
|
|
size_t length = 0;
|
|
size_t capacity = 4096;
|
|
|
|
// Allocate memory for the output string
|
|
errno = 0;
|
|
char* str = malloc(capacity + 1); // +1 for the null terminator
|
|
if (str == NULL)
|
|
return failure("Failed to allocate memory for the output string (%s).", strerror(errno));
|
|
|
|
ssize_t count;
|
|
while (1) {
|
|
// Read the file descriptor into the output string
|
|
errno = 0;
|
|
count = read(fd, str + length, capacity - length);
|
|
if (count < 0) {
|
|
free(str);
|
|
return failure("Failed to read the file descriptor %d (%s).", fd, strerror(errno));
|
|
}
|
|
|
|
// Update the length of the output string
|
|
length += count;
|
|
if (count == 0)
|
|
break;
|
|
|
|
// Reallocate memory for the output string if necessary
|
|
if (length >= capacity) {
|
|
capacity *= 2;
|
|
|
|
errno = 0;
|
|
char* new_str = realloc(str, capacity + 1); // +1 for the null terminator
|
|
if (new_str == NULL) {
|
|
free(str);
|
|
return failure("Failed to reallocate memory for the output string (%s).", strerror(errno));
|
|
}
|
|
str = new_str;
|
|
}
|
|
}
|
|
|
|
// Null-terminate the string
|
|
str[length] = '\0';
|
|
|
|
// Return the output string
|
|
*_str = str;
|
|
return success();
|
|
}
|
|
|
|
result_t write_fd(int fd, const char* str) {
|
|
// Write the string to the file descriptor
|
|
size_t length = strlen(str);
|
|
size_t written = 0;
|
|
|
|
while (written < length) {
|
|
// Try to write the entire string at once
|
|
errno = 0;
|
|
ssize_t count = write(fd, str + written, length - written);
|
|
if (count < 0)
|
|
return failure("Failed to write to the file descriptor %d (%s).", fd, strerror(errno));
|
|
|
|
written += count;
|
|
}
|
|
|
|
return success();
|
|
}
|
|
|
|
result_t read_file(char** _str, const char* path) {
|
|
// Initialize the output parameters
|
|
*_str = NULL;
|
|
|
|
// Open the file
|
|
errno = 0;
|
|
int fd = open(path, O_RDONLY);
|
|
if (fd < 0)
|
|
return failure("Failed to open the file '%s' (%s).", path, strerror(errno));
|
|
|
|
// Read the file
|
|
result_t result = read_fd(_str, fd);
|
|
|
|
// Close the file
|
|
close(fd);
|
|
|
|
return result;
|
|
}
|
|
|
|
result_t write_file(const char* path, const char* str) {
|
|
// Open the file
|
|
errno = 0;
|
|
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
|
if (fd < 0)
|
|
return failure("Failed to open the file '%s' (%s).", path, strerror(errno));
|
|
|
|
// Write the string to the file
|
|
result_t result = write_fd(fd, str);
|
|
|
|
// Close the file
|
|
close(fd);
|
|
|
|
return result;
|
|
}
|
|
|
|
result_t copy_file(const char* src, const char* dst) {
|
|
// Open the source file
|
|
errno = 0;
|
|
int src_fd = open(src, O_RDONLY);
|
|
if (src_fd < 0)
|
|
return failure("Failed to open the source file '%s' (%s).", src, strerror(errno));
|
|
|
|
// Open the destination file
|
|
errno = 0;
|
|
int dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
|
if (dst_fd < 0) {
|
|
close(src_fd);
|
|
return failure("Failed to open the destination file '%s' (%s).", dst, strerror(errno));
|
|
}
|
|
|
|
// Copy the source file to the destination file
|
|
char buffer[4096];
|
|
ssize_t count;
|
|
|
|
while (1) {
|
|
// Read from the source file
|
|
errno = 0;
|
|
count = read(src_fd, buffer, sizeof(buffer));
|
|
if (count < 0) {
|
|
close(src_fd);
|
|
close(dst_fd);
|
|
return failure("Failed to read from the source file '%s' (%s).", src, strerror(errno));
|
|
}
|
|
|
|
// If the end of the file is reached, stop copying
|
|
if (count == 0)
|
|
break;
|
|
|
|
// Write to the destination file
|
|
while (count > 0) {
|
|
errno = 0;
|
|
ssize_t written = write(dst_fd, buffer, count);
|
|
if (written < 0) {
|
|
close(src_fd);
|
|
close(dst_fd);
|
|
return failure("Failed to write to the destination file '%s' (%s).", dst, strerror(errno));
|
|
}
|
|
|
|
count -= written;
|
|
}
|
|
}
|
|
|
|
// Close the source file
|
|
close(src_fd);
|
|
|
|
// Close the destination file
|
|
close(dst_fd);
|
|
|
|
return success();
|
|
}
|
|
|
|
result_t list_files(char*** _files, const char* path, bool (*filter)(const char*)) {
|
|
// Initialize the output parameters
|
|
*_files = NULL;
|
|
|
|
// Allocate memory for the files array
|
|
size_t length = 0;
|
|
size_t capacity = 64;
|
|
|
|
errno = 0;
|
|
char** files = malloc(capacity * sizeof(char*));
|
|
if (files == NULL)
|
|
return failure("Failed to allocate memory for the files array (%s).", strerror(errno));
|
|
|
|
// Open the directory
|
|
errno = 0;
|
|
DIR* dir = opendir(path);
|
|
if (dir == NULL) {
|
|
free(files);
|
|
return failure("Failed to open the directory '%s' (%s).", path, strerror(errno));
|
|
}
|
|
|
|
// Read the directory
|
|
struct dirent* entry;
|
|
while (1) {
|
|
// Read the next entry
|
|
errno = 0;
|
|
entry = readdir(dir);
|
|
if (entry == NULL) {
|
|
if (errno != 0) {
|
|
for (size_t i = 0; i < length; i++)
|
|
free(files[i]);
|
|
free(files);
|
|
closedir(dir);
|
|
return failure("Failed to read the directory '%s' (%s).", path, strerror(errno));
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Filter the entry
|
|
if (filter != NULL && !filter(entry->d_name))
|
|
continue;
|
|
|
|
// Add the entry to the files array
|
|
files[length] = strdup(entry->d_name);
|
|
if (files[length] == NULL) {
|
|
for (size_t i = 0; i < length; i++)
|
|
free(files[i]);
|
|
free(files);
|
|
closedir(dir);
|
|
return failure("Failed to allocate memory for the file name.");
|
|
}
|
|
|
|
length++;
|
|
|
|
// Reallocate memory for the files array if necessary
|
|
if (length > capacity) {
|
|
capacity *= 2;
|
|
|
|
errno = 0;
|
|
char** new_files = realloc(files, capacity * sizeof(char*));
|
|
if (new_files == NULL) {
|
|
for (size_t i = 0; i < length; i++)
|
|
free(files[i]);
|
|
free(files);
|
|
closedir(dir);
|
|
return failure("Failed to reallocate memory for the files array (%s).", strerror(errno));
|
|
}
|
|
files = new_files;
|
|
}
|
|
}
|
|
|
|
// Close the directory
|
|
closedir(dir);
|
|
|
|
// Null-terminate the files array
|
|
files[length] = NULL;
|
|
|
|
// Return the files array
|
|
*_files = files;
|
|
return success();
|
|
}
|
|
|
|
result_t md5sum(char** _md5, const char* path) {
|
|
// Initialize the output parameters
|
|
*_md5 = NULL;
|
|
|
|
// Open the file
|
|
errno = 0;
|
|
int fd = open(path, O_RDONLY);
|
|
if (fd < 0)
|
|
return failure("Failed to open the file '%s' (%s).", path, strerror(errno));
|
|
|
|
// Initialize the MD5 context
|
|
EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
|
|
if (mdctx == NULL) {
|
|
close(fd);
|
|
return failure("Failed to initialize the MD5 context.");
|
|
}
|
|
|
|
if (EVP_DigestInit_ex(mdctx, EVP_md5(), NULL) != 1) {
|
|
EVP_MD_CTX_free(mdctx);
|
|
close(fd);
|
|
return failure("Failed to initialize the MD5 context.");
|
|
}
|
|
|
|
// Read the file and update the MD5 context
|
|
char buffer[4096];
|
|
ssize_t count;
|
|
|
|
while (1) {
|
|
// Read from the file
|
|
errno = 0;
|
|
count = read(fd, buffer, sizeof(buffer));
|
|
if (count < 0) {
|
|
EVP_MD_CTX_free(mdctx);
|
|
close(fd);
|
|
return failure("Failed to read from the file '%s' (%s).", path, strerror(errno));
|
|
}
|
|
|
|
// If the end of the file is reached, stop updating the MD5 context
|
|
if (count == 0)
|
|
break;
|
|
|
|
// Update the MD5 context
|
|
if (EVP_DigestUpdate(mdctx, buffer, count) != 1) {
|
|
EVP_MD_CTX_free(mdctx);
|
|
close(fd);
|
|
return failure("Failed to update the MD5 context.");
|
|
}
|
|
}
|
|
|
|
// Finalize the MD5 context
|
|
unsigned char md_value[EVP_MAX_MD_SIZE];
|
|
unsigned int md_len;
|
|
|
|
if (EVP_DigestFinal_ex(mdctx, md_value, &md_len) != 1) {
|
|
EVP_MD_CTX_free(mdctx);
|
|
close(fd);
|
|
return failure("Failed to finalize the MD5 context.");
|
|
}
|
|
|
|
EVP_MD_CTX_free(mdctx);
|
|
|
|
// Close the file
|
|
close(fd);
|
|
|
|
// Format the MD5 checksum
|
|
errno = 0;
|
|
char* md5 = malloc(2 * md_len + 1); // +1 for the null terminator
|
|
if (md5 == NULL)
|
|
return failure("Failed to allocate memory for the MD5 checksum (%s).", strerror(errno));
|
|
|
|
for (unsigned int i = 0; i < md_len; i++)
|
|
if (snprintf(md5 + 2 * i, 3, "%02x", md_value[i]) != 2) {
|
|
free(md5);
|
|
return failure("Failed to format the MD5 checksum.");
|
|
}
|
|
md5[2 * md_len] = '\0';
|
|
|
|
// Return the MD5 checksum
|
|
*_md5 = md5;
|
|
return success();
|
|
} |