From a2305bed2a9e0349e96d49ac0a79ac7241966038 Mon Sep 17 00:00:00 2001 From: krgannon Date: Sun, 25 Jan 2026 20:01:01 -0700 Subject: [PATCH] update to work on Apple m-series processors --- .gitignore | 43 +++++++++++++ README.md | 61 ++++++++++++++++--- SECURITY.md | 21 +++++++ antipopd | Bin 38360 -> 85144 bytes antipopd.m | 104 +++++++++++++++++++++++++++++--- com.blendedcocoa.antipopd.plist | 14 +++-- scripts/install.sh | 26 ++++++++ scripts/uninstall.sh | 14 +++++ 8 files changed, 259 insertions(+), 24 deletions(-) create mode 100644 .gitignore create mode 100644 SECURITY.md create mode 100644 scripts/install.sh create mode 100644 scripts/uninstall.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..886d0b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# Compiled binaries +antipopd +*.o +*.a +*.so +*.dylib + +# Build artifacts +build/ +dist/ +*.dSYM/ + +# macOS system files +.DS_Store +.AppleDouble +.LSOverride +*.swp +*.swo +*~ +.vscode/ +.idea/ +*.swp + +# Xcode +*.xcodeproj/ +*.xcworkspace/ +xcuserdata/ +*.playground + +# Temporary files +*.tmp +*.bak +*.log + +# IDEs +.vscode/ +.idea/ +*.sublime-workspace +*.sublime-project + +# OS-specific +Thumbs.db + diff --git a/README.md b/README.md index e0ef3a5..e10c10c 100755 --- a/README.md +++ b/README.md @@ -26,10 +26,11 @@ Build ===== `antipopd` can be built in a terminal using the following command: +```shell +clang -framework Foundation -framework CoreFoundation -framework AVFoundation -framework IOKit -arch arm64 -arch x86_64 -o antipopd antipopd.m +``` - clang -framework AppKit -framework IOKit -arch i386 -arch x86_64 -o antipopd antipopd.m - -A built version (`i386` and `x86_64`) of `antipopd` is included in the repository. +A universal (`arm64` and `x86_64`) build will run on Apple Silicon and Intel Macs. Configuration ============= @@ -45,14 +46,56 @@ only be kept alive when on AC power. The configuration file is only read once when `antipopd` launches. Changing the configuration file will not take effect until `antipopd` is restarted. +Debug Mode +========== + +`antipopd` supports a debug mode that allows you to hear the audio system +being kept active. This is useful for testing and verification purposes. + +To run `antipopd` in debug mode, use the `-d` flag: + + ./antipopd -d + +In debug mode, `antipopd` will speak an audible "beep" every 10 seconds instead +of a silent space. This lets you hear the program working and verify that the +audio system is being kept awake as expected. + +Command Line Options +==================== + + -d Enable debug mode (plays audible beep every 10 seconds) + -h Show help message and usage information + +Logging +======= + +`antipopd` writes occasional log entries to `~/Library/Logs/antipopd.log` so you +can verify that the program is running. Log entries are written every 5 minutes +(every 30 cycles) to keep the log file small and manageable. + +Each log entry includes a timestamp and an utterance counter showing how many times +the audio system has been kept active. + +**Log Rotation**: When the log file reaches 5MB, it is automatically rotated. The +current log is renamed with a timestamp suffix (e.g., `antipopd.log.20260125_143022`) +and a new `antipopd.log` is created. This prevents the log from consuming excessive +disk space. + +You can view the log using: + + tail -f ~/Library/Logs/antipopd.log + Installation ============ -In order to have `antipopd` run as a daemon (run automatically) it is -necessary to configure `launchctl`. In `Terminal` run the following commands: +To have `antipopd` run automatically for your user account, install it as a +LaunchAgent using the provided script: + + ./scripts/install.sh + +Uninstallation +============== - sudo cp com.blendedcocoa.antipopd.plist /Library/LaunchDaemons - sudo cp antipopd /usr/local/bin - sudo launchctl load -w /Library/LaunchDaemons/com.blendedcocoa.antipopd.plist +To remove `antipopd` from your user account, run: -You will need to provide your password to allow the installation. + ./scripts/uninstall.sh diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..aba7c61 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,21 @@ +# Security Notes + +This project is a small, long-running user agent that keeps the audio subsystem +awake by speaking a silent space. The code itself does not require elevated +privileges, but installation and runtime choices can affect security posture. + +Considerations and mitigations: + +- User-scoped launch: The LaunchAgent runs under your user account instead of + as root, reducing impact if the binary is ever replaced or misused. +- Binary trust: The installer copies `antipopd` to `/usr/local/bin`. Ensure that + path is owned by root or your admin user and not writable by untrusted users. +- Config file integrity: If you use the optional `/usr/local/share/antipop/ac_only` + config file, keep its directory permissions restricted to prevent tampering. +- Log file location: Logs are written to `~/Library/Logs/antipopd.log`. This is + user-writable and may reveal basic runtime info (process start/stop events). +- No network access: The program does not open sockets or accept input, reducing + exposure to remote attacks. + +If you need a system-wide install, consider signing the binary and using file +permissions to prevent tampering. diff --git a/antipopd b/antipopd index 79c86c44b2b516b8b9327fc94866edd023454fbc..39bfdc4452b134abdb7903f6a56876dc31f67424 100755 GIT binary patch literal 85144 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 en>zIDiW@)m#4rBhsT~)r{jGtDju>S%w#Fh~N literal 38360 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 #import -#import +#import #import #import +#import +#import +#import #define ANTIPOPD_CONFIG "/usr/local/share/antipop/ac_only" #define BATTERY_STATE CFSTR("State:/IOKit/PowerSources/InternalBattery-0") #define POWER_SOURCE CFSTR("Power Source State") #define INTERVAL 10 // seconds +#define MAX_LOG_SIZE (5 * 1024 * 1024) // 5MB in bytes static BOOL runOnACOnly = NO; +static BOOL debugMode = NO; +static char logFilePath[PATH_MAX]; +static int logCounter = 0; + +// Get the log file path +void setupLogPath() { + const char *homeDir = getenv("HOME"); + if (homeDir == NULL) { + homeDir = "/tmp"; + } + snprintf(logFilePath, PATH_MAX, "%s/Library/Logs/antipopd.log", homeDir); +} + +// Rotate log file if it exceeds MAX_LOG_SIZE +void rotateLogFile() { + struct stat st; + if (stat(logFilePath, &st) == 0 && st.st_size >= MAX_LOG_SIZE) { + // Create backup filename with timestamp + char backupPath[PATH_MAX]; + time_t now = time(NULL); + struct tm *tm_info = localtime(&now); + char timestamp[32]; + strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", tm_info); + snprintf(backupPath, PATH_MAX, "%s.%s", logFilePath, timestamp); + + // Rename current log to backup + rename(logFilePath, backupPath); + } +} + +// Write to log file +void writeLog(const char *message) { + FILE *logFile = fopen(logFilePath, "a"); + if (logFile) { + time_t now = time(NULL); + struct tm *tm_info = localtime(&now); + char timestamp[32]; + strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info); + + fprintf(logFile, "[%s] %s\n", timestamp, message); + fclose(logFile); + } +} void banner() { printf("antipopd\n\n"); @@ -51,17 +98,31 @@ void banner() { printf("a Creative Commons Attribution Noncommercial Share Alike License 3.0,\n"); printf("http://creativecommons.org/licenses/by-nc-sa/3.0/us\n\n"); + printf("Usage: antipopd [options]\n"); + printf(" -d Enable debug mode (plays audible beep every 10 seconds)\n"); + printf(" -h Show this help message\n\n"); + } -NSSpeechSynthesizer *speech = nil; +AVSpeechSynthesizer *speech = nil; // Use AVFoundation speech to keep audio subsystem awake. // Timer callback that actually speaks the space void speak(CFRunLoopTimerRef timer, void *info) { if (!speech) { - speech = [[NSSpeechSynthesizer alloc] initWithVoice:nil]; + // Lazily initialize the synthesizer once to keep overhead low. + speech = [[AVSpeechSynthesizer alloc] init]; } + // Increment log counter and log periodically (every 30 calls = 5 minutes) + logCounter++; + if (logCounter % 30 == 0) { + rotateLogFile(); + char message[256]; + snprintf(message, sizeof(message), "Spoke utterance (count: %d)", logCounter); + writeLog(message); + } + // If we are only supposed to run on AC power if (runOnACOnly) { // and we don't have unlimited power remaining @@ -71,7 +132,21 @@ void speak(CFRunLoopTimerRef timer, void *info) { } } - [speech startSpeakingString:@" "]; + if (!speech.isSpeaking) { + AVSpeechUtterance *utterance; + + if (debugMode) { + // In debug mode, speak "beep" audibly so we can hear it + utterance = [AVSpeechUtterance speechUtteranceWithString:@"beep"]; + utterance.volume = 1.0f; + } else { + // Speak a silent space so the audio system stays active without audible output. + utterance = [AVSpeechUtterance speechUtteranceWithString:@" "]; + utterance.volume = 0.0f; + } + + [speech speakUtterance:utterance]; + } } // Check for the existance of the ac_only file, check the contents @@ -96,15 +171,26 @@ void loadACOnlyConfig() { } int main(int argc, char *argv[]) { + setupLogPath(); loadACOnlyConfig(); // Put an AutoreleasePool in place in case NSSpeechSynthesizer expects it @autoreleasepool { - if (argc >= 2) { // if we have any parameter show the banner - banner(); - exit(EXIT_SUCCESS); + // Parse command line arguments + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-d") == 0) { + debugMode = YES; + } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + banner(); + exit(EXIT_SUCCESS); + } } - + + // If debug mode is enabled, show banner for confirmation + if (debugMode) { + printf("antipopd running in DEBUG MODE - audible beeps every 10 seconds\n\n"); + } + CFRunLoopTimerContext context = { 0, NULL, NULL, NULL, NULL, }; diff --git a/com.blendedcocoa.antipopd.plist b/com.blendedcocoa.antipopd.plist index 62df59e..6a95e16 100644 --- a/com.blendedcocoa.antipopd.plist +++ b/com.blendedcocoa.antipopd.plist @@ -2,16 +2,18 @@ - Disabled - KeepAlive Label com.blendedcocoa.antipopd - OnDemand - - Program - /usr/local/bin/antipopd + ProgramArguments + + /usr/local/bin/antipopd + + StandardErrorPath + __HOME__/Library/Logs/antipopd.log + StandardOutPath + __HOME__/Library/Logs/antipopd.log RunAtLoad diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100644 index 0000000..5860e64 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,26 @@ +#!/bin/sh +set -eu + +PLIST_SRC="com.blendedcocoa.antipopd.plist" +PLIST_DST="$HOME/Library/LaunchAgents/com.blendedcocoa.antipopd.plist" +BIN_DST="/usr/local/bin/antipopd" +LOG_PATH="$HOME/Library/Logs/antipopd.log" + +mkdir -p "$HOME/Library/LaunchAgents" +mkdir -p "/usr/local/bin" +mkdir -p "$HOME/Library/Logs" + +# Compile the antipopd binary +echo "Compiling antipopd..." +clang -framework Foundation -framework CoreFoundation -framework AVFoundation -framework IOKit -arch arm64 -arch x86_64 -o antipopd antipopd.m + +cp antipopd "$BIN_DST" +cp "$PLIST_SRC" "$PLIST_DST" + +# launchd does not expand "~" in plist paths, so inject the absolute $HOME. +sed -i '' "s|__HOME__|$HOME|g" "$PLIST_DST" + +launchctl unload -w "$PLIST_DST" >/dev/null 2>&1 || true +launchctl load -w "$PLIST_DST" + +echo "Installed LaunchAgent and logging to $LOG_PATH" diff --git a/scripts/uninstall.sh b/scripts/uninstall.sh new file mode 100644 index 0000000..a34aa76 --- /dev/null +++ b/scripts/uninstall.sh @@ -0,0 +1,14 @@ +#!/bin/sh +set -eu + +PLIST_DST="$HOME/Library/LaunchAgents/com.blendedcocoa.antipopd.plist" +BIN_DST="/usr/local/bin/antipopd" +LOG_PATH="$HOME/Library/Logs/antipopd.log" + +launchctl unload -w "$PLIST_DST" >/dev/null 2>&1 || true + +rm -f "$PLIST_DST" +rm -f "$BIN_DST" +rm -f "$LOG_PATH" + +echo "Uninstalled LaunchAgent and removed $LOG_PATH"