diff options
| author | David Faulkner <[email protected]> | 2026-08-05 23:25:25 -0500 |
|---|---|---|
| committer | David Faulkner <[email protected]> | 2026-08-05 23:25:25 -0500 |
| commit | 3d51ff671e0bf079063c6270bbb985bd3d218b34 (patch) | |
| tree | 2d99d81b2e0c1cad6ce119dfc6da5844ed49b3e1 /main.c | |
feat: initial commit for ud maintenance utility
- Add C source code for Debian apt update, autoremove, and cleanup tasks
- Add Makefile with build, install, uninstall, and clean targets
- Add README.md with build and usage instructions
- Add MIT license and .gitignore for build artifacts
Diffstat (limited to 'main.c')
| -rw-r--r-- | main.c | 154 |
1 files changed, 154 insertions, 0 deletions
@@ -0,0 +1,154 @@ +/** + * @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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <sys/wait.h> + +#define MAX_CMD_LEN 512 +#define MAX_MSG_LEN 128 +#define MAX_LINE_LEN 1024 + +/** + * @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 */ + run_task("UPDATING LOCAL CACHE `apt update`", "sudo apt 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)) { + run_task(msg, "sudo apt full-upgrade -y"); + } + } + + /* 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; +} |
