/** * @file main.c * @brief Automated Debian System Maintenance Utility * * Automates package list updates, conditional upgrades, orphaned dependency * purging, package archive cleaning, and temporary user cache removal. * * Requirements: Debian-based distribution (Debian, Ubuntu, Linux Mint) with apt, sed, and sudo. */ #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #define MAX_CMD_LEN 512 #define MAX_MSG_LEN 128 #define MAX_LINE_LEN 1024 /* Optimization flags for parallel package downloads */ #define APT_OPT "-o Acquire::Queue-Mode=\"access\" -o Acquire::http::Pipeline-Depth=\"10\"" /** * @brief Prints an underlined section header. * * @param message Header text to display. */ static void print_message(const char *message) { if (!message) return; printf("\n%s\n", message); size_t len = strlen(message); for (size_t i = 0; i < len; i++) { putchar('-'); } putchar('\n'); } /** * @brief Formats and executes a shell command with indented stdout. * * Wraps the shell command to route standard error to /dev/null and * pipes standard output to sed for clean line indentation. * * @param message Display title for the task. * @param command Command string to execute via subshell. * @return true if the command executed and returned status 0, false otherwise. */ static bool run_task(const char *message, const char *command) { print_message(message); char full_cmd[MAX_CMD_LEN]; int bytes_written = snprintf(full_cmd, sizeof(full_cmd), "%s 2>/dev/null | sed 's/^/ /'", command); /* Verify buffer bounds to prevent shell command truncation vulnerabilities */ if (bytes_written < 0 || (size_t)bytes_written >= sizeof(full_cmd)) { fprintf(stderr, "Error: Command string truncated or invalid.\n"); return false; } int status = system(full_cmd); if (status == -1) { perror("Error invoking shell process"); return false; } /* Inspect POSIX exit status from subshell execution */ if (WIFEXITED(status)) { int exit_code = WEXITSTATUS(status); if (exit_code != 0) { fprintf(stderr, " [!] Command returned non-zero exit code: %d\n", exit_code); return false; } } else { fprintf(stderr, " [!] Command terminated abnormally.\n"); return false; } return true; } /** * @brief Queries APT for pending upgradable packages. * * Spawns a read pipeline to apt list --upgradable and tallies target lines. * * @return Count of upgradable packages, or -1 on execution error. */ static int count_upgradable_packages(void) { FILE *fp = popen("apt list --upgradable 2>/dev/null", "r"); if (!fp) { perror("Error opening pipe to apt"); return -1; } int count = 0; char buffer[MAX_LINE_LEN]; while (fgets(buffer, sizeof(buffer), fp) != NULL) { if (strstr(buffer, "upgradable from:") != NULL) { count++; } } int status = pclose(fp); if (status == -1 || (WIFEXITED(status) && WEXITSTATUS(status) != 0)) { /* If apt list fails, report 0 to safely skip upgrade block without crashing */ return 0; } return count; } int main(void) { /* Step 1: Update local package index with parallel fetch queue */ run_task("UPDATING LOCAL CACHE `apt update`", "sudo apt " APT_OPT " update"); /* Step 2: Query for upgrades */ int upgrade_count = count_upgradable_packages(); if (upgrade_count < 0) { fprintf(stderr, "Failed to query pending updates.\n"); } else if (upgrade_count == 0) { print_message("NO UPDATES AVAILABLE | UPGRADE SKIPPED"); } else { char msg[MAX_MSG_LEN]; int bytes = snprintf(msg, sizeof(msg), "%d UPDATE%s AVAILABLE | UPGRADING `apt full-upgrade`", upgrade_count, (upgrade_count > 1) ? "S" : ""); if (bytes > 0 && (size_t)bytes < sizeof(msg)) { char cmd[MAX_CMD_LEN]; snprintf(cmd, sizeof(cmd), "sudo apt full-upgrade -y"); run_task(msg, cmd); } } /* Step 3: Remove lingering packages and clear package archive caches */ run_task("PURGING ORPHANED DEPENDENCIES & CONFIGS `apt autoremove --purge`", "sudo apt autoremove --purge -y"); run_task("WIPING ALL DOWNLOADED PACKAGE FILES `apt clean`", "sudo apt clean"); /* Step 4: Clean local user caches safely (suppressing error if glob matches 0 files) */ run_task("CLEARING LOCAL THUMBNAIL CACHE `rm -rf ~/.cache/thumbnails/*`", "rm -rf ~/.cache/thumbnails/* 2>/dev/null || true"); run_task("CLEARING SCREENSHOTS FOLDER `rm -rf ~/Pictures/Screenshots/*`", "rm -rf ~/Pictures/Screenshots/* 2>/dev/null || true"); putchar('\n'); return EXIT_SUCCESS; }