From 8edd71b476c5ea0b923c57888f448471d3388b7d Mon Sep 17 00:00:00 2001 From: klein panic Date: Tue, 29 Oct 2024 00:07:08 -0400 Subject: [PATCH] Fixed git ignore, updated much functionality, updated the procress_monitor to work, updated install.sh, added license, updated Makefile, updated install.sh, updated README.md, all that --- .gitignore | 2 + LICENSE | 22 +++ Makefile | 8 +- README.md | 175 +++++++++++++++----- battery_daemon.sh | 9 + battery_monitor | Bin 28160 -> 37712 bytes config.conf | 7 + include/battery_monitor.h | 7 + include/process_monitor.h | 19 ++- include/version.h | 9 + install.sh | 172 +++++++++++++++++--- obj/battery_monitor.o | Bin 2904 -> 0 bytes obj/notification.o | Bin 12360 -> 0 bytes obj/process_monitor.o | Bin 7592 -> 0 bytes src/.gitignore | 1 + src/battery_monitor.c | 109 ++++++++++++- src/notification.c | 181 ++++++++++++++------- src/notification.c.bak | 307 ----------------------------------- src/process_monitor.c | 334 ++++++++++++++++++++++++++++++-------- src/process_monitor.c.bak | 177 -------------------- 20 files changed, 853 insertions(+), 686 deletions(-) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 config.conf create mode 100644 include/version.h delete mode 100644 obj/battery_monitor.o delete mode 100644 obj/notification.o delete mode 100644 obj/process_monitor.o create mode 100644 src/.gitignore delete mode 100644 src/notification.c.bak delete mode 100644 src/process_monitor.c.bak diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..458144c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +batter_monitor +obj/* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bce361a --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) [year] [fullname] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/Makefile b/Makefile index 63be530..5af3303 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,13 @@ all: $(TARGET) $(TARGET): $(OBJS) $(CC) -o $(TARGET) $(OBJS) $(LDFLAGS) +install: $(TARGET) + @echo "Installing $(TARGET) to /usr/local/bin" + cp $(TARGET) /usr/local/bin/ + chmod +x /usr/local/bin/$(TARGET) + clean: rm -rf $(OBJ_DIR) $(TARGET) -.PHONY: all clean +.PHONY: all clean install + diff --git a/README.md b/README.md index e099731..915576f 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,73 @@ -# Battery 0 Dameon -A battery daemon running the GTK framework built in C. Application monitors file structure /sys/class/power_suppply/BAT0/capacity file and if you have it its accounts for BAT1 (I'm pretty sure. In theory it does but I have not tested it as I dont have that file structure). +# Battery Monitor Daemon + +A battery monitor daemon built in C using the GTK framework. The application monitors your system's battery level and provides notifications when the battery is low or critically low. It also implements battery-saving features like reducing screen brightness and managing background processes to help preserve battery life. + +--- ## Table of Contents - [Features](#features) - [Installation](#installation) - [Dependencies](#dependencies) - - [Building the Application](#building-the-application) - - [Installing the Application](#installing-the-application) + - [Building and Installing the Application](#building-and-installing-the-application) +- [Configuration](#configuration) + - [Adjusting Battery Thresholds](#adjusting-battery-thresholds) + - [Configuring Process Management](#configuring-process-management) - [Uninstallation](#uninstallation) - [Contributing](#contributing) -## features +## Features -- GTK notification for 15 percent or less, and GTK notification for 5 percent or less. - - to change, go to src/battery_monitor.c, change #define THRESHOLD_LOW or #define THRESHOLD_CRITICAL to custom values. -- Dynamic battery percentage changing depending on last read value. - - When above THRESHOLD_HIGH, checks every 5 minutes. - - When Below critical, checks every 30 seconds. - - When low, checks every minute. -- Logging file created in /tmp/battery_monitor.log -- Battery saving mode changes brightness to help preserve battery -- Battery saving mode attempt to manage background procress is still in progress. +--- -## Installation +- **Configurable Battery Thresholds**: Receive notifications when the battery level falls below user-defined thresholds for low and critical levels. Adjust these thresholds easily via a configuration file. + +- **Dynamic Monitoring Interval**: The application adjusts its battery level check interval based on the current battery percentage to optimize performance and responsiveness. + +- **Battery Saving Mode**: + - Reduces screen brightness to 50% when the battery is low. + - Suspends high CPU-consuming processes and user daemons to conserve battery life. + - Allows users to specify which processes to ignore during suspension. + +- **Logging**: Activity is logged to `/tmp/battery_monitor.log` for debugging and monitoring purposes. + +- **Systemd Service**: Runs as a user-level systemd service, starting automatically upon login. + +- **Version Checking**: Supports version checking for both the application and the accompanying scripts. + +--- + +## Installation + +### Dependencies -### Dependencies Ensure you have the following dependencies installed on your system: + - **gcc**: GNU Compiler Collection for compiling the C code. - **make**: Utility for directing compilation. -- **GTK+**: Library for creating Graphical User Interfaces -- **Standard C Headers**: Standard Libraries found in glibc +- **pkg-config**: Helper tool used during compilation. +- **GTK+ 3 Development Libraries**: Library for creating graphical user interfaces. **On Debian/Ubuntu:** ```bash sudo apt-get update -sudo apt-get install build-essential libgtk-3-dev +sudo apt-get install build-essential pkg-config libgtk-3-dev ``` **On Fedora:** ```bash -sudo dnf install gcc make gtk3-devel +sudo dnf install gcc make pkgconf-pkg-config gtk3-devel ``` **On Arch Linux:** ```bash -sudo pacman -S base-devel gtk3 +sudo pacman -S base-devel pkgconf gtk3 ``` -### Building the Application +### Building and Installing the Application 1. **Clone the Repository** @@ -60,45 +76,124 @@ sudo pacman -S base-devel gtk3 cd bat0daemon ``` -2. **Build the Application** +2. **Run the Installation Script** ```bash - make + ./install.sh ``` - This will compile the source code and create the executable in the base directory. + The `install.sh` script will: -### Installing the Application + - Check for and install any missing dependencies. + - Build the application using `make`. + - Install the `battery_monitor` binary to `/usr/local/bin`. + - Install the `battery_daemon.sh` script to `/usr/local/bin`. + - Set up a user-level systemd service to run the application automatically on login. + - Create a default configuration file at `~/.config/battery_monitor/config.conf` if it does not exist. -Optionally, you can install the application system-wide: +**Note**: The `install.sh` script may prompt for your password to use `sudo` for installation. -```bash -make -./install.sh +--- + +## Configuration + +The application uses a configuration file located at `~/.config/battery_monitor/config.conf`. If the file does not exist, the `install.sh` script will create one with default values. + +### Adjusting Battery Thresholds + +Edit the configuration file to adjust battery thresholds: + +```ini +# ~/.config/battery_monitor/config.conf + +threshold_low=25 +threshold_critical=5 +threshold_high=80 ``` +- **threshold_low**: Battery percentage at which the application will send a low battery notification. +- **threshold_critical**: Battery percentage at which the application will send a critical battery notification. +- **threshold_high**: Battery percentage above which the application checks the battery level less frequently. + +### Configuring Process Management + +Specify processes to ignore when the application suspends high CPU-consuming processes or user daemons: + +```ini +ignore_processes_for_kill=process1, process2, process3 +ignore_processes_for_sleep=process4, process5, process6 +``` + +- **ignore_processes_for_kill**: List of processes to ignore when suspending high CPU-consuming processes. +- **ignore_processes_for_sleep**: List of processes to ignore when suspending user daemons. + +**Example**: + +```ini +ignore_processes_for_kill=firefox, code +ignore_processes_for_sleep=dropbox, slack +``` + +--- + ## Uninstallation To remove the application and its associated files: -1. **Remove the Executable** +1. **Stop and Disable the Systemd Service** - If installed system-wide: + ```bash + systemctl --user stop battery_monitor.service + systemctl --user disable battery_monitor.service + ``` + +2. **Remove Installed Files** + + ```bash + sudo rm /usr/local/bin/battery_monitor + sudo rm /usr/local/bin/battery_daemon.sh + ``` + +3. **Remove the Systemd Service File** + + ```bash + rm ~/.config/systemd/user/battery_monitor.service + ``` + +4. **Reload the Systemd Daemon** + + ```bash + systemctl --user daemon-reload + ``` + +5. **Remove Configuration and Log Files (Optional)** + + ```bash + rm -rf ~/.config/battery_monitor + rm /tmp/battery_monitor.log + ``` + +6. **Clean Up Build Files** ```bash make clean ``` -2. **Delete Configuration and Data Files** - - ```bash - sudo rm -rf ~/usr/local/bin/bat0daemon - *add more* - ``` - +--- ## Contributing -Contributions are welcome! If you have ideas for new features or improvements, feel free to fork the repository and submit a pull request. Let's make this application even better together. +Contributions are welcome! If you have ideas for new features, improvements, or bug fixes, feel free to fork the repository and submit a pull request. Let's make this application even better together. --- + +**Additional Notes**: + +- **Battery Monitoring**: The application dynamically finds the battery device path, supporting systems with different battery naming conventions (e.g., `BAT0`, `BAT1`). + +- **Process Suspension**: The application can suspend non-critical background processes to conserve battery life when in battery-saving mode. Critical system processes are automatically excluded. + +- **Customization**: Users can tailor the application's behavior extensively through the configuration file. + +--- + diff --git a/battery_daemon.sh b/battery_daemon.sh index 8dd1307..2622534 100755 --- a/battery_daemon.sh +++ b/battery_daemon.sh @@ -1,5 +1,14 @@ #!/usr/bin/env bash +# Version of the script +VERSION="1.0.0" + +# Check for version option +if [[ "$1" == "--version" ]]; then + echo "battery_daemon.sh version $VERSION" + exit 0 +fi + # Path to the battery monitor binary BINARY_PATH="/usr/local/bin/battery_monitor" diff --git a/battery_monitor b/battery_monitor index 30eaa3723a5a24491dde4aa8afe106c4dc0d4a96..f68a584e85064e94b1099be4cf42f4f3e15745e7 100755 GIT binary patch literal 37712 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%0D0{ z!0d&kqt#IV!}P=Mr9Y4lQq8~sy0{L)f{-x%F#0c=e%Lyf7f}5YQ2Qae5$keb`q{wq zj*v14tOhzR1UrX81C%(x5)cBKJs=!VJTpSfgt;F!PpkpiUdrGB9S4GGK-Vt_wI59x z157_!@&_I62sR4lED(+6MXKrbde)A{bgMcRZ<`aLYQC@jmrQ4#&zv; literal 28160 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 +#include + +extern bool dry_run; +extern pid_t suspended_pids[]; +extern int suspended_count; +extern pid_t suspended_high_cpu_pids[]; +extern int suspended_high_cpu_count; + +int run_battery_saving_mode(pid_t current_pid); +int get_ignore_processes(char *ignore_list[], int max_ignores, const char *config_key); +int is_process_critical(const char *process_name, char *ignore_list[], int ignore_count); + +int suspend_user_daemons(); +int resume_user_daemons(); +int resume_high_cpu_processes(); #endif // PROCESS_MONITOR_H - diff --git a/include/version.h b/include/version.h new file mode 100644 index 0000000..7d1c7b2 --- /dev/null +++ b/include/version.h @@ -0,0 +1,9 @@ +// version.h + +#ifndef VERSION_H +#define VERSION_H + +#define VERSION "1.0.0" + +#endif // VERSION_H + diff --git a/install.sh b/install.sh index 2f1658b..60ee1f4 100755 --- a/install.sh +++ b/install.sh @@ -10,43 +10,136 @@ check_dependency() { fi } -# Step 1: Finding the current working directory and script location -SCRIPT_DIR=$(pwd) -BASH_SCRIPT="$SCRIPT_DIR/battery_daemon.sh" -SRC_SCRIPT="$SCRIPT_DIR/battery_monitor" +# Function to check if a package is installed (for libraries) +check_library() { + if ! dpkg -s "$1" &> /dev/null; then + echo "$1 is not installed. Installing..." + sudo apt-get install -y "$1" + else + echo "$1 is already installed." + fi +} -# Check if battery_daemon.sh exists -if [[ -f "$BASH_SCRIPT" ]]; then - echo "Found battery_daemon.sh. Moving to /usr/local/bin." - sudo cp "$BASH_SCRIPT" /usr/local/bin/battery_daemon.sh - sudo cp "$SRC_SCRIPT" /usr/local/bin/battery_monitor - sudo chmod +x /usr/local/bin/battery_monitor - sudo chmod +x /usr/local/bin/battery_daemon.sh -else - echo "battery_daemon.sh not found in the current directory!" - exit 1 -fi - -# Step 2: Check for dependencies and install if not present -dependencies=("gcc" "make" "brightnessctl") +# Step 1: Check for dependencies and install if not present +dependencies=("gcc" "make" "pkg-config") +libraries=("libgtk-3-dev") # Add other required development libraries here +echo "Checking for build dependencies..." for dep in "${dependencies[@]}"; do check_dependency "$dep" done -# Step 3: Copy battery_monitor.service to user systemd folder +echo "Checking for required libraries..." +for lib in "${libraries[@]}"; do + check_library "$lib" +done + +# Step 2: Build the application +echo "Building the application..." +make clean +make + +if [ $? -ne 0 ]; then + echo "Build failed. Exiting." + exit 1 +fi + +# Step 3: Find the current working directory and script location +SCRIPT_DIR=$(pwd) +SRC_SCRIPT="$SCRIPT_DIR/battery_monitor" +BASH_SCRIPT="$SCRIPT_DIR/battery_daemon.sh" + +# Step 4: Check if battery_monitor exists in common locations +INSTALL_PATH="/usr/local/bin/battery_monitor" +EXISTING_VERSION="" +NEW_VERSION="" + +if [ -f "$INSTALL_PATH" ]; then + echo "Existing installation of battery_monitor found at $INSTALL_PATH" + # Retrieve the version of the existing installation + EXISTING_VERSION=$("$INSTALL_PATH" --version | awk '{print $NF}') + echo "Existing battery_monitor version: $EXISTING_VERSION" +else + echo "No existing installation of battery_monitor found." +fi + +# Retrieve the version of the new build +if [ -f "$SRC_SCRIPT" ]; then + NEW_VERSION=$("$SRC_SCRIPT" --version | awk '{print $NF}') + echo "New battery_monitor version: $NEW_VERSION" +else + echo "New build of battery_monitor not found. Exiting." + exit 1 +fi + +# Function to compare versions +version_greater() { + # Returns 0 if $1 > $2 + [ "$(printf '%s\n' "$2" "$1" | sort -V | head -n1)" != "$1" ] +} + +# Step 5: Compare versions and install if new version is greater +if [ -z "$EXISTING_VERSION" ] || version_greater "$NEW_VERSION" "$EXISTING_VERSION"; then + echo "Installing new version of battery_monitor..." + + # Copy the binary to /usr/local/bin + sudo cp "$SRC_SCRIPT" "$INSTALL_PATH" + sudo chmod +x "$INSTALL_PATH" + + echo "battery_monitor installed successfully." +else + echo "Existing battery_monitor is up to date or newer. No installation needed." +fi + +# Step 6: Check and install battery_daemon.sh +DAEMON_INSTALL_PATH="/usr/local/bin/battery_daemon.sh" +EXISTING_DAEMON_VERSION="" +NEW_DAEMON_VERSION="" + +if [ -f "$DAEMON_INSTALL_PATH" ]; then + echo "Existing installation of battery_daemon.sh found at $DAEMON_INSTALL_PATH" + # Retrieve the version of the existing daemon + EXISTING_DAEMON_VERSION=$("$DAEMON_INSTALL_PATH" --version | awk '{print $NF}') + echo "Existing battery_daemon.sh version: $EXISTING_DAEMON_VERSION" +else + echo "No existing installation of battery_daemon.sh found." +fi + +# Retrieve the version of the new daemon script +if [ -f "$BASH_SCRIPT" ]; then + NEW_DAEMON_VERSION=$("$BASH_SCRIPT" --version | awk '{print $NF}') + echo "New battery_daemon.sh version: $NEW_DAEMON_VERSION" +else + echo "battery_daemon.sh script not found in the current directory. Exiting." + exit 1 +fi + +# Compare versions and install if new version is greater +if [ -z "$EXISTING_DAEMON_VERSION" ] || version_greater "$NEW_DAEMON_VERSION" "$EXISTING_DAEMON_VERSION"; then + echo "Installing new version of battery_daemon.sh..." + + # Copy the daemon script to /usr/local/bin + sudo cp "$BASH_SCRIPT" "$DAEMON_INSTALL_PATH" + sudo chmod +x "$DAEMON_INSTALL_PATH" + + echo "battery_daemon.sh installed successfully." +else + echo "Existing battery_daemon.sh is up to date or newer. No installation needed." +fi + +# Step 7: Copy battery_monitor.service to user systemd folder SYSTEMD_SERVICE="$SCRIPT_DIR/battery_monitor.service" USER_SYSTEMD_DIR="$HOME/.config/systemd/user" if [[ -f "$SYSTEMD_SERVICE" ]]; then echo "Found battery_monitor.service." - + # Create the user systemd directory if it doesn't exist if [[ ! -d "$USER_SYSTEMD_DIR" ]]; then echo "Creating user systemd directory: $USER_SYSTEMD_DIR" mkdir -p "$USER_SYSTEMD_DIR" fi - + echo "Copying battery_monitor.service to $USER_SYSTEMD_DIR" cp "$SYSTEMD_SERVICE" "$USER_SYSTEMD_DIR/" else @@ -54,7 +147,7 @@ else exit 1 fi -# Step 4: Reload the systemd daemon, enable and restart the service (user-level) +# Step 8: Reload the systemd daemon, enable and restart the service (user-level) echo "Reloading systemd user daemon..." systemctl --user daemon-reload @@ -71,3 +164,38 @@ else echo "Failed to start the service." exit 1 fi + +# Step 9: Create default configuration if not exists +CONFIG_DIR="$HOME/.config/battery_monitor" +CONFIG_FILE="$CONFIG_DIR/config.conf" +DEFAULT_CONFIG="$SCRIPT_DIR/config.conf" + +if [[ ! -d "$CONFIG_DIR" ]]; then + echo "Creating configuration directory at $CONFIG_DIR" + mkdir -p "$CONFIG_DIR" +fi + +if [[ ! -f "$CONFIG_FILE" ]]; then + if [[ -f "$DEFAULT_CONFIG" ]]; then + echo "Copying default config.conf to $CONFIG_FILE" + cp "$DEFAULT_CONFIG" "$CONFIG_FILE" + else + echo "Default config.conf not found!" + # Optionally, create a default config file here + cat < "$CONFIG_FILE" +# Default configuration for battery_monitor + +threshold_low=25 +threshold_critical=5 +threshold_high=80 + +# Add other configuration options as needed +EOF + echo "Created default configuration file." + fi +else + echo "Configuration file already exists at $CONFIG_FILE" +fi + +echo "Installation and setup complete." + diff --git a/obj/battery_monitor.o b/obj/battery_monitor.o deleted file mode 100644 index b5992b1c73d7c7d3021c3381aadb2ec52756c848..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2904 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 diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..75f3075 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +backups/ diff --git a/src/battery_monitor.c b/src/battery_monitor.c index 1e5fdc4..15dec02 100644 --- a/src/battery_monitor.c +++ b/src/battery_monitor.c @@ -1,26 +1,117 @@ +// Battery_monitor.c + #include #include #include #include "battery_monitor.h" +#include "process_monitor.h" +#include "version.h" +#include +#include +#include -// Define the battery thresholds -#define THRESHOLD_LOW 15 -#define THRESHOLD_CRITICAL 5 -#define THRESHOLD_HIGH 75 +// Global Variables for Thresholds +int THRESHOLD_LOW = 25; // Default values +int THRESHOLD_CRITICAL = 5; +int THRESHOLD_HIGH = 80; + +// Function to trim leading and trailing whitespace +static char *trim_whitespace(char *str) { + char *end; + + // Trim leading space + while (isspace((unsigned char)*str)) str++; + + if (*str == 0) return str; // All spaces? + + // Trim trailing space + end = str + strlen(str) - 1; + while (end > str && isspace((unsigned char)*end)) end--; + + // Write new null terminator character + end[1] = '\0'; + + return str; +} + +void load_thresholds_from_config() { + char *home_dir = getenv("HOME"); + if (home_dir == NULL) { + log_message("Failed to get HOME environment variable"); + return; + } + + char config_file_path[PATH_MAX]; + snprintf(config_file_path, sizeof(config_file_path), "%s/.config/battery_monitor/config.config", home_dir); + + FILE *config_file = fopen(config_file_path, "r"); + if (config_file == NULL) { + log_message("Failed to open config file, using default thresholds"); + return; + } + + char buffer[256]; + + while (fgets(buffer, sizeof(buffer), config_file) != NULL) { + char *line = trim_whitespace(buffer); + + // Skip empty lines and comments + if (strlen(line) == 0 || line[0] == '#') { + continue; + } + + char key[64]; + char value[64]; + + if (sscanf(line, "%63[^=]=%63s", key, value) == 2) { + if (strcmp(key, "threshold_low") == 0) { + THRESHOLD_LOW = atoi(value); + } else if (strcmp(key, "threshold_critical") == 0) { + THRESHOLD_CRITICAL = atoi(value); + } else if (strcmp(key, "threshold_high") == 0) { + THRESHOLD_HIGH = atoi(value); + } + } + } + + fclose(config_file); + + // Log the loaded thresholds + char message[256]; + snprintf(message, sizeof(message), "Loaded thresholds: LOW=%d, CRITICAL=%d, HIGH=%d", + THRESHOLD_LOW, THRESHOLD_CRITICAL, THRESHOLD_HIGH); + log_message(message); +} // Track if notifications have been sent int notified_low = 0; int notified_critical = 0; +int battery_saving_mode_active = 0; // 0: inactive, 1: active -int main() { +int main(int argc, char *argv[]) { + if (argc > 1 && strcmp(argv[1], "--version") == 0) { + printf("Battery Monitor version %s\n", VERSION); + return 0; + } log_message("Battery monitor started"); + load_thresholds_from_config(); + while (1) { if (is_charging()) { // Reset notifications if the battery is charging log_message("Battery is charging, notifications reset"); notified_low = 0; notified_critical = 0; + + if (battery_saving_mode_active) { + // Resume suspended processes + log_message("Battery is charging, resuming suspended processes"); + resume_high_cpu_processes(); + resume_user_daemons(); + battery_saving_mode_active = 0; + } + sleep(300); // Sleep for 5 minutes while charging continue; } @@ -43,6 +134,14 @@ int main() { sleep_duration = 60; // Sleep for 1 minute when low } + // Check if battery-saving mode is active and battery level has surpassed THRESHOLD_LOW + if (battery_saving_mode_active && battery_level > THRESHOLD_LOW) { + log_message("Battery level above threshold, resuming suspended processes"); + resume_high_cpu_processes(); + resume_user_daemons(); + battery_saving_mode_active = 0; + } + // Check if the battery level is below the critical threshold if (battery_level <= THRESHOLD_CRITICAL && !notified_critical) { log_message("Battery critically low, showing notification"); diff --git a/src/notification.c b/src/notification.c index 09af4b6..fcc4015 100644 --- a/src/notification.c +++ b/src/notification.c @@ -1,3 +1,5 @@ +// Notification.c + #include #include #include @@ -9,6 +11,8 @@ #include "battery_monitor.h" #include "process_monitor.h" #include "log_message.h" +#include +#include #define CSS_STYLE "\ * { \ @@ -21,28 +25,67 @@ } \ " -// Function to get the battery level -int get_battery_level() { - const char *battery_paths[] = { - "/sys/class/power_supply/BAT0/capacity", - "/sys/class/power_supply/BAT1/capacity" - }; - FILE *file; - int battery_level = -1; +extern int battery_saving_mode_active; - for (int i = 0; i < sizeof(battery_paths) / sizeof(battery_paths[0]); i++) { - file = fopen(battery_paths[i], "r"); - if (file != NULL) { - break; +typedef enum { + INIT_SYSTEMD, + INIT_SYSVINIT, + INIT_OPENRC, + INIT_UNKNOWN +} init_system_t; + +init_system_t detect_init_system() { + struct stat sb; + + if (stat("/run/systemd/system", &sb) == 0) { + return INIT_SYSTEMD; + } else if (stat("/sbin/init", &sb) == 0) { + // Additional checks can be added here + return INIT_SYSVINIT; + } else if (stat("/run/openrc", &sb) == 0) { + return INIT_OPENRC; + } + + return INIT_UNKNOWN; +} + +// Function to find the battery path dynamically +char *get_battery_device_path(const char *file_name) { + glob_t glob_result; + char pattern[PATH_MAX]; + + snprintf(pattern, sizeof(pattern), "/sys/class/power_supply/BAT*/%s", file_name); + + if (glob(pattern, 0, NULL, &glob_result) == 0) { + if (glob_result.gl_pathc > 0) { + char *battery_path = strdup(glob_result.gl_pathv[0]); + globfree(&glob_result); + return battery_path; } } + globfree(&glob_result); + return NULL; +} + +// Function to get the battery level +int get_battery_level() { + char *capacity_path = get_battery_device_path("capacity"); + if (capacity_path == NULL) { + log_message("Failed to find battery capacity file"); + return -1; + } + + FILE *file = fopen(capacity_path, "r"); + free(capacity_path); + if (file == NULL) { perror("Failed to open capacity file"); log_message("Failed to open capacity file"); return -1; } + int battery_level; if (fscanf(file, "%d", &battery_level) != 1) { perror("Failed to read battery level"); log_message("Failed to read battery level"); @@ -78,10 +121,36 @@ void log_message(const char *message) { } } +char *get_backlight_device_path(const char *file_name) { + glob_t glob_result; + char pattern[PATH_MAX]; + + snprintf(pattern, sizeof(pattern), "/sys/class/backlight/*/%s", file_name); + + if (glob(pattern, 0, NULL, &glob_result) == 0) { + if (glob_result.gl_pathc > 0) { + char *backlight_path = strdup(glob_result.gl_pathv[0]); + globfree(&glob_result); + return backlight_path; + } + } + + globfree(&glob_result); + return NULL; +} + // Function to set the screen brightness int set_brightness(int brightness) { - const char *brightness_path = "/sys/class/backlight/intel_backlight/brightness"; - const char *max_brightness_path = "/sys/class/backlight/intel_backlight/max_brightness"; + char *brightness_path = get_backlight_device_path("brightness"); + char *max_brightness_path = get_backlight_device_path("max_brightness"); + + if (brightness_path == NULL || max_brightness_path == NULL) { + log_message("Failed to find backlight brightness files"); + free(brightness_path); + free(max_brightness_path); + return -1; + } + int max_brightness = 100; int new_brightness = 0; char buffer[10]; @@ -124,6 +193,8 @@ int set_brightness(int brightness) { return -1; } + free(brightness_path); + free(max_brightness_path); close(fd); return 0; } @@ -135,58 +206,49 @@ int activate_battery_saving_mode() { // Get the current PID of the running program pid_t current_pid = getpid(); - // Call the get_high_cpu_processes from process_monitor.c to get the list of high CPU-consuming processes - char *process_list[100]; - int process_count = get_high_cpu_processes(process_list, 100); - - if (process_count == -1) { - log_message("Failed to get high CPU processes"); + // Suspend high CPU processes + log_message("Suspending high CPU processes"); + if (run_battery_saving_mode(current_pid) == -1) { + log_message("Failed to suspend high CPU processes"); return -1; } - // Loop through each high CPU process and kill it, excluding this program's own PID - for (int i = 0; i < process_count; i++) { - char command[300]; - char pid[10]; - char process_name[100]; - sscanf(process_list[i], "%9s %99s", pid, process_name); - - pid_t process_pid = atoi(pid); - - if (process_pid == current_pid) { - char log_msg[200]; - snprintf(log_msg, sizeof(log_msg), "Skipping own process: %s (PID: %s)", process_name, pid); - log_message(log_msg); - continue; - } - - char log_msg[200]; - snprintf(log_msg, sizeof(log_msg), "Killing process: %s (PID: %s)", process_name, pid); - log_message(log_msg); - - snprintf(command, sizeof(command), "kill -9 %s", pid); - if (system(command) == -1) { - log_message("Failed to kill process"); - free_process_list(process_list, process_count); - return -1; - } + // Suspend user daemons + log_message("Suspending user daemons"); + if (suspend_user_daemons() == -1) { + log_message("Failed to suspend user daemons"); + return -1; } - free_process_list(process_list, process_count); - // Set the brightness to 50% for battery saving if (set_brightness(50) == -1) { log_message("Failed to set brightness to 50%"); return -1; } + // Set the battery-saving mode active flag + battery_saving_mode_active = 1; + return 0; } // Function to enter sleep mode int enter_sleep_mode() { log_message("Entering sleep mode"); - return system("systemctl suspend"); + + init_system_t init_sys = detect_init_system(); + + switch (init_sys) { + case INIT_SYSTEMD: + return system("systemctl suspend"); + case INIT_SYSVINIT: + return system("pm-suspend"); + case INIT_OPENRC: + return system("loginctl suspend"); + default: + log_message("Unknown init system, cannot enter sleep mode"); + return -1; + } } // Function to apply custom CSS styles to the GTK widgets @@ -292,26 +354,22 @@ void show_notification(const char *message, const char *title) { // Function to check if the battery is charging int is_charging() { - const char *status_paths[] = { - "/sys/class/power_supply/BAT0/status", - "/sys/class/power_supply/BAT1/status" - }; - FILE *file; - char status[16]; - - for (int i = 0; i < sizeof(status_paths) / sizeof(status_paths[0]); i++) { - file = fopen(status_paths[i], "r"); - if (file != NULL) { - break; - } + char *status_path = get_battery_device_path("status"); + if (status_path == NULL) { + log_message("Failed to find battery status file"); + return -1; } + FILE *file = fopen(status_path, "r"); + free(status_path); + if (file == NULL) { perror("Failed to open status file"); log_message("Failed to open status file"); return -1; } + char status[16]; if (fscanf(file, "%15s", status) != 1) { perror("Failed to read battery status"); log_message("Failed to read battery status"); @@ -322,4 +380,3 @@ int is_charging() { fclose(file); return (strcmp(status, "Charging") == 0); } - diff --git a/src/notification.c.bak b/src/notification.c.bak deleted file mode 100644 index 58b0938..0000000 --- a/src/notification.c.bak +++ /dev/null @@ -1,307 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "battery_monitor.h" -#include "process_monitor.h" -#include "log_message.h" - -#define CSS_STYLE "\ - * { \ - background-color: #333333; \ - color: white; \ - } \ - button { \ - background-color: #555555; \ - color: white; \ - } \ -" - -// Function to get the battery level -int get_battery_level() { - const char *battery_paths[] = { - "/sys/class/power_supply/BAT0/capacity", - "/sys/class/power_supply/BAT1/capacity" - }; - FILE *file; - int battery_level = -1; - - for (int i = 0; i < sizeof(battery_paths) / sizeof(battery_paths[0]); i++) { - file = fopen(battery_paths[i], "r"); - if (file != NULL) { - break; - } - } - - if (file == NULL) { - perror("Failed to open capacity file"); - log_message("Failed to open capacity file"); - return -1; - } - - if (fscanf(file, "%d", &battery_level) != 1) { - perror("Failed to read battery level"); - log_message("Failed to read battery level"); - fclose(file); - return -1; - } - - fclose(file); - return battery_level; -} - -// Function to get the base directory of the executable -char *get_base_directory() { - static char base_dir[PATH_MAX]; - ssize_t count = readlink("/proc/self/exe", base_dir, PATH_MAX); - if (count != -1) { - dirname(base_dir); - } - return base_dir; -} - -// Function to log messages to a file -void log_message(const char *message) { - char log_file[PATH_MAX]; - snprintf(log_file, PATH_MAX, "/tmp/battery_monitor.log"); - - FILE *log_file_ptr = fopen(log_file, "a"); - if (log_file_ptr) { - fprintf(log_file_ptr, "%s\n", message); - fclose(log_file_ptr); - } else { - perror("Failed to open log file"); - } -} - -// Function to set the screen brightness -int set_brightness(int brightness) { - const char *brightness_path = "/sys/class/backlight/intel_backlight/brightness"; - const char *max_brightness_path = "/sys/class/backlight/intel_backlight/max_brightness"; - int max_brightness = 100; - int new_brightness = 0; - char buffer[10]; - - // Open max brightness file - int fd = open(max_brightness_path, O_RDONLY); - if (fd == -1) { - perror("Failed to open max brightness file"); - log_message("Failed to open max brightness file"); - return -1; // Return failure if the file can't be opened - } - - // Read max brightness value - if (read(fd, buffer, sizeof(buffer)) != -1) { - max_brightness = atoi(buffer); - } else { - perror("Failed to read max brightness"); - log_message("Failed to read max brightness"); - close(fd); - return -1; // Return failure if the file can't be read - } - close(fd); - - // Calculate the new brightness - new_brightness = max_brightness * brightness / 100; - - // Write the new brightness value to the brightness file - fd = open(brightness_path, O_WRONLY); - if (fd == -1) { - perror("Failed to open brightness file"); - log_message("Failed to open brightness file"); - return -1; // Return failure if the file can't be opened - } - - snprintf(buffer, sizeof(buffer), "%d", new_brightness); - if (write(fd, buffer, strlen(buffer)) == -1) { - perror("Failed to write to brightness file"); - log_message("Failed to write to brightness file"); - close(fd); - return -1; // Return failure if the write fails - } - - close(fd); - return 0; // Success -} - -// Function to activate battery saving mode -int activate_battery_saving_mode() { - log_message("Activating battery saving mode"); - - // Get the current PID of the running program - pid_t current_pid = getpid(); - - // Call the get_high_cpu_processes from process_monitor.c to get the list of high CPU-consuming processes - char *process_list[100]; // Assuming a maximum of 100 processes to handle - int process_count = get_high_cpu_processes(process_list, 100); - - if (process_count == -1) { - log_message("Failed to get high CPU processes"); - return -1; // Return failure if processes couldn't be killed - } - - // Loop through each high CPU process and kill it, excluding this program's own PID - for (int i = 0; i < process_count; i++) { - char command[300]; - - // Extract the PID and process name from process_list[i] - char pid[10]; - char process_name[100]; - sscanf(process_list[i], "%9s %99s", pid, process_name); // Assuming the format "PID ProcessName" in process_list - - // Convert the PID string to a number for comparison - pid_t process_pid = atoi(pid); - - // Check if the process PID matches the current program's PID, if so, skip it - if (process_pid == current_pid) { - char log_msg[200]; // Declare log_msg correctly - snprintf(log_msg, sizeof(log_msg), "Skipping own process: %s (PID: %s)", process_name, pid); - log_message(log_msg); - continue; // Skip killing the current program's own process - } - - // Log the process name and PID before killing the process - char log_msg[200]; - snprintf(log_msg, sizeof(log_msg), "Killing process: %s (PID: %s)", process_name, pid); - log_message(log_msg); - - // Kill the process by PID - snprintf(command, sizeof(command), "kill -9 %s", pid); - if (system(command) == -1) { - log_message("Failed to kill process"); - free_process_list(process_list, process_count); - return -1; // Return failure if the command fails - } - } - - // Free the dynamically allocated process list - free_process_list(process_list, process_count); - - // Set the brightness to 50% for battery saving - if (set_brightness(50) == -1) { - log_message("Failed to set brightness to 50%"); - return -1; // Return failure if brightness couldn't be set - } - - return 0; // Success -} - -// Function to enter sleep mode -int enter_sleep_mode() { - log_message("Entering sleep mode"); - return system("systemctl suspend"); // Return system command result -} - -// Function to apply custom CSS styles to the GTK widgets -void apply_css(GtkWidget *widget, const char *css) { - GtkCssProvider *provider = gtk_css_provider_new(); - gtk_css_provider_load_from_data(provider, css, -1, NULL); - GtkStyleContext *context = gtk_widget_get_style_context(widget); - gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_USER); - g_object_unref(provider); -} - -// Function to check the battery status and close the dialog if charging -gboolean check_battery_status(gpointer user_data) { - GtkWidget *dialog = GTK_WIDGET(user_data); - if (is_charging()) { - log_message("Battery started charging, closing notification"); - gtk_widget_destroy(dialog); - gtk_main_quit(); // Exit the GTK main loop - return FALSE; // Stop checking - } - return TRUE; // Continue checking -} - -// Function to handle dialog response -void on_dialog_response(GtkDialog *dialog, gint response_id, gpointer user_data) { - switch (response_id) { - case GTK_RESPONSE_OK: - log_message("User clicked OK"); - break; - case GTK_RESPONSE_APPLY: - log_message("User activated Battery Saving Mode"); - activate_battery_saving_mode(); - break; - case GTK_RESPONSE_CLOSE: - log_message("User triggered Sleep Mode"); - enter_sleep_mode(); - break; - default: - break; - } - gtk_widget_destroy(GTK_WIDGET(dialog)); - gtk_main_quit(); // Exit the GTK main loop -} - -// Function to show the notification dialog -void show_notification(const char *message, const char *title) { - log_message("Showing notification"); - - GtkWidget *dialog; - gtk_init(0, NULL); - - dialog = gtk_message_dialog_new(NULL, - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_INFO, - GTK_BUTTONS_NONE, - "%s", message); - gtk_dialog_add_button(GTK_DIALOG(dialog), "OK", GTK_RESPONSE_OK); - gtk_dialog_add_button(GTK_DIALOG(dialog), "Battery Saving Mode", GTK_RESPONSE_APPLY); - - if (g_strcmp0(title, "Critical Battery Warning") == 0) { - gtk_dialog_add_button(GTK_DIALOG(dialog), "Sleep", GTK_RESPONSE_CLOSE); - } - - gtk_window_set_title(GTK_WINDOW(dialog), title); - - // Apply CSS styles - apply_css(dialog, CSS_STYLE); - - // Set up the callback to check battery status and close if charging - g_timeout_add(1000, check_battery_status, dialog); - - // Connect the dialog response to handle button clicks and ensure proper cleanup - g_signal_connect(dialog, "response", G_CALLBACK(on_dialog_response), NULL); - - // Show the dialog and enter the GTK main loop - gtk_widget_show_all(dialog); - gtk_main(); // Start the GTK main loop -} - -// Function to check if the battery is charging -int is_charging() { - const char *status_paths[] = { - "/sys/class/power_supply/BAT0/status", - "/sys/class/power_supply/BAT1/status" - }; - FILE *file; - char status[16]; - - for (int i = 0; i < sizeof(status_paths) / sizeof(status_paths[0]); i++) { - file = fopen(status_paths[i], "r"); - if (file != NULL) { - break; - } - } - - if (file == NULL) { - perror("Failed to open status file"); - log_message("Failed to open status file"); - return -1; - } - - if (fscanf(file, "%15s", status) != 1) { - perror("Failed to read battery status"); - log_message("Failed to read battery status"); - fclose(file); - return -1; - } - - fclose(file); - return (strcmp(status, "Charging") == 0); -} - diff --git a/src/process_monitor.c b/src/process_monitor.c index 3c168ae..51ecf01 100644 --- a/src/process_monitor.c +++ b/src/process_monitor.c @@ -1,18 +1,44 @@ +// process_monitor.c + #include #include #include #include #include #include +#include +#include +#include "process_monitor.h" #include "log_message.h" +#include +#include +#include #define BUFFER_SIZE 1024 -#define CONFIG_FILE "/.config/battery_monitor/config.config" +#define CONFIG_FILE "/.config/battery_monitor/config.conf" #define MAX_CRITICAL_PROCESSES 100 #define MAX_IGNORE_PROCESSES 100 +#define MAX_SUSPENDED_PROCESSES 1024 + +pid_t suspended_pids[MAX_SUSPENDED_PROCESSES]; +int suspended_count = 0; +pid_t suspended_high_cpu_pids[MAX_SUSPENDED_PROCESSES]; +int suspended_high_cpu_count = 0; + +bool dry_run = true; // Set to 'true' for dry run, 'false' for normal operation + +// CPU usage threshold to consider a process as high CPU-consuming +#define CPU_USAGE_THRESHOLD 1.0 // List of default critical processes (expanded with more essential processes) -const char *default_critical_processes[] = {"systemd", "Xorg", "dbus-daemon", "NetworkManager", "dwm", "DWM", "sddm", "gdm", "fprintd", NULL}; +const char *default_critical_processes[] = { + "systemd", "Xorg", "dbus-daemon", "NetworkManager", "dwm", "DWM", + "sddm", "gdm", "fprintd", "gnome-shell", "plasmashell", "kdeinit", + "kwin", "xfce4-session", "cinnamon", "mate-session", "pulseaudio", + "pipewire", "pipewire-pulse", "wireplumber", "lightdm", "udisksd", + "upowerd", "bash", "st", "picom", "python3", "gvfsd", "xdg-document-portal", + "at-spi-bus-launcher", "at-spi2-registr", "volumeicon", NULL +}; // Function to perform case-insensitive string comparison int case_insensitive_compare(const char *a, const char *b) { @@ -26,20 +52,17 @@ int case_insensitive_compare(const char *a, const char *b) { return *a == *b; } -// Function to dynamically build the list of critical processes -void build_critical_processes_list(char *critical_list[], int *count) { - int index = 0; - - // Add default critical processes - for (int i = 0; default_critical_processes[i] != NULL; i++) { - critical_list[index++] = strdup(default_critical_processes[i]); +// Function to output messages based on dry run mode +void output_message(const char *message) { + if (dry_run) { + printf("%s\n", message); + } else { + log_message(message); } - - *count = index; } // Helper function to remove leading/trailing whitespace -char *trim_whitespace(char *str) { +static char *trim_whitespace(char *str) { char *end; // Trim leading space @@ -57,15 +80,28 @@ char *trim_whitespace(char *str) { return str; } +// Function to dynamically build the list of critical processes +void build_critical_processes_list(char *critical_list[], int *count) { + int index = *count; + + // Add default critical processes + for (int i = 0; default_critical_processes[i] != NULL && index < MAX_IGNORE_PROCESSES; i++) { + critical_list[index++] = strdup(default_critical_processes[i]); + } + + *count = index; +} + // Function to dynamically get the user's home directory and build the config file path char *get_config_file_path() { const char *home_dir = getenv("HOME"); if (home_dir == NULL) { - log_message("Failed to get HOME environment variable"); + output_message("Failed to get HOME environment variable"); return NULL; } - char *config_file_path = malloc(strlen(home_dir) + strlen(CONFIG_FILE) + 1); + size_t path_len = strlen(home_dir) + strlen(CONFIG_FILE) + 1; + char *config_file_path = malloc(path_len); if (config_file_path != NULL) { strcpy(config_file_path, home_dir); strcat(config_file_path, CONFIG_FILE); @@ -75,26 +111,32 @@ char *get_config_file_path() { } // Function to parse the ignore list from the config file -int get_ignore_processes(char *ignore_list[], int max_ignores) { +int get_ignore_processes(char *ignore_list[], int max_ignores, const char *config_key) { char *config_file_path = get_config_file_path(); if (config_file_path == NULL) { - log_message("Could not determine the config file path"); - return -1; + output_message("Could not determine the config file path"); + // Proceed with default critical processes + int ignore_count = 0; + build_critical_processes_list(ignore_list, &ignore_count); + return ignore_count; } FILE *config_file = fopen(config_file_path, "r"); free(config_file_path); + int ignore_count = 0; + if (config_file == NULL) { - log_message("Failed to open config file"); - return -1; + output_message("Failed to open config file"); + // Proceed with default critical processes + build_critical_processes_list(ignore_list, &ignore_count); + return ignore_count; } char buffer[BUFFER_SIZE]; - int ignore_count = 0; while (fgets(buffer, sizeof(buffer), config_file) != NULL) { - if (strstr(buffer, "ignore_processes_for_sleep") != NULL) { + if (strstr(buffer, config_key) != NULL) { char *token = strtok(buffer, "="); token = strtok(NULL, "="); // Get the processes list after '=' @@ -111,7 +153,7 @@ int get_ignore_processes(char *ignore_list[], int max_ignores) { fclose(config_file); - // Add default critical processes like dwm and Xorg if not already included + // Add default critical processes if not already included build_critical_processes_list(ignore_list, &ignore_count); return ignore_count; @@ -127,83 +169,237 @@ int is_process_critical(const char *process_name, char *ignore_list[], int ignor return 0; } -// Get the list of high CPU processes excluding ignored and root processes -int get_high_cpu_processes(char *process_list[], int max_processes) { +// Main function to run battery saving mode +int run_battery_saving_mode(pid_t current_pid) { + output_message("Running battery saving mode in process_monitor"); + FILE *fp; char buffer[BUFFER_SIZE]; - int process_count = 0; - // Command to get top CPU-consuming processes excluding root processes - const char *command = "ps -eo user,pid,comm,%cpu --sort=-%cpu | grep -vE '^root'"; + // Command to get high CPU-consuming processes excluding root processes + const char *command = "ps -eo user:32,pid,pcpu,comm --no-headers --sort=-pcpu"; fp = popen(command, "r"); if (fp == NULL) { - log_message("Failed to run command to get high CPU processes"); + output_message("Failed to run command to get high CPU processes"); return -1; } // Load ignore processes from config file - char *ignore_list[100]; - int ignore_count = get_ignore_processes(ignore_list, 100); + char *ignore_list[MAX_IGNORE_PROCESSES]; + int ignore_count = get_ignore_processes(ignore_list, MAX_IGNORE_PROCESSES, "ignore_processes_for_kill"); + if (ignore_count == -1) { + output_message("Failed to get ignore processes"); + pclose(fp); + return -1; + } - // Parse each line from the process list - while (fgets(buffer, sizeof(buffer), fp) != NULL && process_count < max_processes) { - char user[50], command_name[100]; + // Process the list and handle processes accordingly + while (fgets(buffer, sizeof(buffer), fp) != NULL) { + // Remove leading and trailing whitespace from the buffer + char *line = trim_whitespace(buffer); + + // Skip empty lines + if (strlen(line) == 0) { + continue; + } + + char user[33]; // +1 for null terminator + char command_name[256]; int pid; float cpu_usage; - if (sscanf(buffer, "%49s %d %99s %f", user, &pid, command_name, &cpu_usage) == 4) { - if (!is_process_critical(command_name, ignore_list, ignore_count)) { - // Allocate memory for the process info and store the PID - process_list[process_count] = malloc(BUFFER_SIZE); - snprintf(process_list[process_count], BUFFER_SIZE, "%d", pid); // Only storing the PID - process_count++; - } else { - char log_msg[200]; - snprintf(log_msg, sizeof(log_msg), "Skipping critical process: %s (PID: %d)", command_name, pid); - log_message(log_msg); // Log critical processes skipped + int items = sscanf(line, "%32s %d %f %255[^\n]", user, &pid, &cpu_usage, command_name); + + if (items == 4) { + // Exclude root processes + if (strcmp(user, "root") == 0) { + continue; } + + // Exclude processes with CPU usage below threshold + if (cpu_usage < CPU_USAGE_THRESHOLD) { + continue; + } + + if (pid == current_pid) { + output_message("Skipping suspending the current process."); + continue; + } + + if (!is_process_critical(command_name, ignore_list, ignore_count)) { + char message[512]; + snprintf(message, sizeof(message), "Process to be suspended: %s (PID: %d, CPU Usage: %.2f%%)", command_name, pid, cpu_usage); + output_message(message); + + if (dry_run) { + snprintf(message, sizeof(message), "Dry run mode active: Would suspend process %s (PID: %d)", command_name, pid); + output_message(message); + } else { + // Suspend the process + if (kill(pid, SIGSTOP) == -1) { + perror("Failed to suspend process"); + output_message("Failed to suspend process"); + } else { + // Add to the list of suspended processes + if (suspended_high_cpu_count < MAX_SUSPENDED_PROCESSES) { + suspended_high_cpu_pids[suspended_high_cpu_count++] = pid; + output_message("Process suspended successfully"); + } else { + output_message("Maximum suspended processes limit reached."); + } + } + } + } else { + char message[512]; + snprintf(message, sizeof(message), "Skipping critical process: %s (PID: %d)", command_name, pid); + output_message(message); + } + } else { + output_message("Failed to parse process information"); } } - // Free ignore list + // Clean up for (int i = 0; i < ignore_count; i++) { free(ignore_list[i]); } pclose(fp); - return process_count; + return 0; } -// Function to handle killing high CPU processes -void kill_high_cpu_processes(char *process_list[], int process_count, pid_t current_pid) { - for (int i = 0; i < process_count; i++) { - pid_t process_pid = atoi(process_list[i]); +int resume_high_cpu_processes() { + for (int i = 0; i < suspended_high_cpu_count; i++) { + pid_t pid = suspended_high_cpu_pids[i]; - if (process_pid == current_pid) { - log_message("Skipping killing the current process."); - continue; - } - - // Log the process PID before killing - char log_msg[200]; - snprintf(log_msg, sizeof(log_msg), "Killing process (PID: %d)", process_pid); - log_message(log_msg); - - // Kill the process by PID - char command[50]; - snprintf(command, sizeof(command), "kill -9 %d", process_pid); - if (system(command) == -1) { - log_message("Failed to kill process"); + if (dry_run) { + printf("Dry run: Would resume high CPU process PID: %d\n", pid); } else { - log_message("Process killed successfully"); + if (kill(pid, SIGCONT) == 0) { + output_message("Resumed high CPU process"); + } else { + perror("Failed to resume high CPU process"); + } } } + suspended_high_cpu_count = 0; // Reset the count after resuming + return 0; } -// Free the process list -void free_process_list(char *process_list[], int count) { - for (int i = 0; i < count; i++) { - free(process_list[i]); +int suspend_user_daemons() { + DIR *proc_dir = opendir("/proc"); + if (proc_dir == NULL) { + perror("Failed to open /proc directory"); + return -1; } + + struct dirent *entry; + uid_t uid = getuid(); // Get the UID of the current user + + // Load ignore processes from config file for suspending daemons + char *ignore_list[MAX_IGNORE_PROCESSES]; + int ignore_count = get_ignore_processes(ignore_list, MAX_IGNORE_PROCESSES, "ignore_processes_for_sleep"); + if (ignore_count == -1) { + output_message("Failed to get ignore processes"); + closedir(proc_dir); + return -1; + } + + while ((entry = readdir(proc_dir)) != NULL) { + if (!isdigit(entry->d_name[0])) + continue; + + pid_t pid = atoi(entry->d_name); + if (pid == getpid()) // Skip current process + continue; + + char status_file[256]; + snprintf(status_file, sizeof(status_file), "/proc/%d/stat", pid); + + FILE *status_fp = fopen(status_file, "r"); + if (status_fp == NULL) + continue; + + char comm[256]; + char state; + int ppid, pgrp, session; + unsigned int tty_nr; + uid_t proc_uid = -1; + + // Read necessary fields from /proc/[pid]/stat + fscanf(status_fp, "%*d (%255[^)]) %c %d %d %d %u", comm, &state, &ppid, &pgrp, &session, &tty_nr); + fclose(status_fp); + + // Get UID of the process owner + char proc_status_file[256]; + snprintf(proc_status_file, sizeof(proc_status_file), "/proc/%d/status", pid); + + FILE *proc_status_fp = fopen(proc_status_file, "r"); + if (proc_status_fp == NULL) + continue; + + char line[256]; + while (fgets(line, sizeof(line), proc_status_fp)) { + if (strncmp(line, "Uid:", 4) == 0) { + sscanf(line, "Uid:\t%u", &proc_uid); + break; + } + } + fclose(proc_status_fp); + + // Check if process is owned by the user and has no controlling terminal + if (proc_uid == uid && tty_nr == 0) { + // Check if process is not critical + if (!is_process_critical(comm, ignore_list, ignore_count)) { + if (dry_run) { + printf("Dry run: Would suspend process PID: %d (%s)\n", pid, comm); + } else { + if (suspended_count < MAX_SUSPENDED_PROCESSES) { + if (kill(pid, SIGSTOP) == 0) { + suspended_pids[suspended_count++] = pid; + output_message("Suspended process"); + } else { + perror("Failed to suspend process"); + } + } else { + output_message("Maximum suspended processes limit reached."); + break; + } + } + } else { + // Log that we are skipping a critical process + if (dry_run) { + printf("Skipping critical process: PID: %d (%s)\n", pid, comm); + } + } + } + } + + // Free ignore_list + for (int i = 0; i < ignore_count; i++) { + free(ignore_list[i]); + } + + closedir(proc_dir); + return 0; } + +int resume_user_daemons() { + for (int i = 0; i < suspended_count; i++) { + pid_t pid = suspended_pids[i]; + + if (dry_run) { + printf("Dry run: Would resume process PID: %d\n", pid); + } else { + if (kill(pid, SIGCONT) == 0) { + output_message("Resumed process"); + } else { + perror("Failed to resume process"); + } + } + } + suspended_count = 0; // Reset the count after resuming + return 0; +} + diff --git a/src/process_monitor.c.bak b/src/process_monitor.c.bak deleted file mode 100644 index 404e7fb..0000000 --- a/src/process_monitor.c.bak +++ /dev/null @@ -1,177 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "log_message.h" - -#define BUFFER_SIZE 1024 -#define CONFIG_FILE "/.config/battery_monitor/config.config" -#define MAX_CRITICAL_PROCESSES 100 -#define MAX_IGNORE_PROCESSES 100 - -// List of default critical processes -const char *default_critical_processes[] = {"systemd", "Xorg", "dbus-daemon", "NetworkManager", NULL}; - -// Helper function to remove leading/trailing whitespace -char *trim_whitespace(char *str) { - char *end; - - // Trim leading space - while (isspace((unsigned char)*str)) str++; - - if (*str == 0) return str; // All spaces? - - // Trim trailing space - end = str + strlen(str) - 1; - while (end > str && isspace((unsigned char)*end)) end--; - - // Write new null terminator character - end[1] = '\0'; - - return str; -} - -// Function to dynamically get the user's home directory and build the config file path -char *get_config_file_path() { - const char *home_dir = getenv("HOME"); - if (home_dir == NULL) { - log_message("Failed to get HOME environment variable"); - return NULL; - } - - char *config_file_path = malloc(strlen(home_dir) + strlen(CONFIG_FILE) + 1); - if (config_file_path != NULL) { - strcpy(config_file_path, home_dir); - strcat(config_file_path, CONFIG_FILE); - } - - return config_file_path; -} - -// Function to parse the ignore list from the config file -int get_ignore_processes(char *ignore_list[], int max_ignores) { - char *config_file_path = get_config_file_path(); - if (config_file_path == NULL) { - log_message("Could not determine the config file path"); - return -1; - } - - FILE *config_file = fopen(config_file_path, "r"); - free(config_file_path); - - if (config_file == NULL) { - log_message("Failed to open config file"); - return -1; - } - - char buffer[BUFFER_SIZE]; - int ignore_count = 0; - - while (fgets(buffer, sizeof(buffer), config_file) != NULL) { - if (strstr(buffer, "ignore_processes_for_sleep") != NULL) { - char *token = strtok(buffer, "="); - token = strtok(NULL, "="); // Get the processes list after '=' - - if (token != NULL) { - token = strtok(token, ","); - while (token != NULL && ignore_count < max_ignores) { - ignore_list[ignore_count] = strdup(trim_whitespace(token)); - ignore_count++; - token = strtok(NULL, ","); - } - } - } - } - - fclose(config_file); - return ignore_count; -} - -// Function to check if a process is critical -int is_process_critical(const char *process_name, char *ignore_list[], int ignore_count) { - for (int i = 0; i < ignore_count; i++) { - if (strcmp(process_name, ignore_list[i]) == 0) { - return 1; // Process is critical - } - } - return 0; -} - -// Get the list of high CPU processes excluding ignored and root processes -int get_high_cpu_processes(char *process_list[], int max_processes) { - FILE *fp; - char buffer[BUFFER_SIZE]; - int process_count = 0; - - // Command to get top CPU-consuming processes excluding root processes - const char *command = "ps -eo user,pid,comm,%cpu --sort=-%cpu | grep -vE '^root'"; - - fp = popen(command, "r"); - if (fp == NULL) { - log_message("Failed to run command to get high CPU processes"); - return -1; - } - - // Load ignore processes from config file - char *ignore_list[100]; - int ignore_count = get_ignore_processes(ignore_list, 100); - - // Parse each line from the process list - while (fgets(buffer, sizeof(buffer), fp) != NULL && process_count < max_processes) { - char user[50], command_name[100]; - int pid; - float cpu_usage; - - if (sscanf(buffer, "%49s %d %99s %f", user, &pid, command_name, &cpu_usage) == 4) { - if (!is_process_critical(command_name, ignore_list, ignore_count)) { - // Allocate memory for the process info and store the PID - process_list[process_count] = malloc(BUFFER_SIZE); - snprintf(process_list[process_count], BUFFER_SIZE, "%d", pid); // Only storing the PID - process_count++; - } - } - } - - // Free ignore list - for (int i = 0; i < ignore_count; i++) { - free(ignore_list[i]); - } - - pclose(fp); - return process_count; -} - -// Function to handle killing high CPU processes -void kill_high_cpu_processes(char *process_list[], int process_count, pid_t current_pid) { - for (int i = 0; i < process_count; i++) { - pid_t process_pid = atoi(process_list[i]); - - if (process_pid == current_pid) { - log_message("Skipping killing the current process."); - continue; - } - - // Log the process PID before killing - char log_msg[200]; - snprintf(log_msg, sizeof(log_msg), "Killing process (PID: %d)", process_pid); - log_message(log_msg); - - // Kill the process by PID - char command[50]; - snprintf(command, sizeof(command), "kill -9 %d", process_pid); - if (system(command) == -1) { - log_message("Failed to kill process"); - } else { - log_message("Process killed successfully"); - } - } -} - -// Free the process list -void free_process_list(char *process_list[], int count) { - for (int i = 0; i < count; i++) { - free(process_list[i]); - } -}