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

This commit is contained in:
klein panic
2024-10-29 00:07:08 -04:00
parent 5c0af3b725
commit 8edd71b476
20 changed files with 853 additions and 686 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
batter_monitor
obj/*

22
LICENSE Normal file
View File

@@ -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.

View File

@@ -16,7 +16,13 @@ all: $(TARGET)
$(TARGET): $(OBJS) $(TARGET): $(OBJS)
$(CC) -o $(TARGET) $(OBJS) $(LDFLAGS) $(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: clean:
rm -rf $(OBJ_DIR) $(TARGET) rm -rf $(OBJ_DIR) $(TARGET)
.PHONY: all clean .PHONY: all clean install

175
README.md
View File

@@ -1,57 +1,73 @@
# Battery 0 Dameon # Battery Monitor Daemon
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).
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 ## Table of Contents
- [Features](#features) - [Features](#features)
- [Installation](#installation) - [Installation](#installation)
- [Dependencies](#dependencies) - [Dependencies](#dependencies)
- [Building the Application](#building-the-application) - [Building and Installing the Application](#building-and-installing-the-application)
- [Installing the Application](#installing-the-application) - [Configuration](#configuration)
- [Adjusting Battery Thresholds](#adjusting-battery-thresholds)
- [Configuring Process Management](#configuring-process-management)
- [Uninstallation](#uninstallation) - [Uninstallation](#uninstallation)
- [Contributing](#contributing) - [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: Ensure you have the following dependencies installed on your system:
- **gcc**: GNU Compiler Collection for compiling the C code. - **gcc**: GNU Compiler Collection for compiling the C code.
- **make**: Utility for directing compilation. - **make**: Utility for directing compilation.
- **GTK+**: Library for creating Graphical User Interfaces - **pkg-config**: Helper tool used during compilation.
- **Standard C Headers**: Standard Libraries found in glibc - **GTK+ 3 Development Libraries**: Library for creating graphical user interfaces.
**On Debian/Ubuntu:** **On Debian/Ubuntu:**
```bash ```bash
sudo apt-get update 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:** **On Fedora:**
```bash ```bash
sudo dnf install gcc make gtk3-devel sudo dnf install gcc make pkgconf-pkg-config gtk3-devel
``` ```
**On Arch Linux:** **On Arch Linux:**
```bash ```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** 1. **Clone the Repository**
@@ -60,45 +76,124 @@ sudo pacman -S base-devel gtk3
cd bat0daemon cd bat0daemon
``` ```
2. **Build the Application** 2. **Run the Installation Script**
```bash ```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 ## Uninstallation
To remove the application and its associated files: 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 ```bash
make clean make clean
``` ```
2. **Delete Configuration and Data Files** ---
```bash
sudo rm -rf ~/usr/local/bin/bat0daemon
*add more*
```
## Contributing ## 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.
---

View File

@@ -1,5 +1,14 @@
#!/usr/bin/env bash #!/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 # Path to the battery monitor binary
BINARY_PATH="/usr/local/bin/battery_monitor" BINARY_PATH="/usr/local/bin/battery_monitor"

Binary file not shown.

7
config.conf Normal file
View File

@@ -0,0 +1,7 @@
# Default configuration for battery_monitor
ignore_processes_for_sleep=vi,neovim,vim
threshold_low=25
threshold_critical=5
threshold_high=80

View File

@@ -13,4 +13,11 @@ void log_message(const char *message);
// New function declaration for process monitoring // New function declaration for process monitoring
int get_high_cpu_processes(char *process_list[], int max_processes); int get_high_cpu_processes(char *process_list[], int max_processes);
extern int battery_saving_mode_active;
extern int THRESHOLD_LOW;
extern int THRESHOLD_CRITICAL;
extern int THRESHOLD_HIGH;
void load_thresholds_from_config();
#endif // BATTERY_MONITOR_H #endif // BATTERY_MONITOR_H

View File

@@ -1,8 +1,21 @@
#ifndef PROCESS_MONITOR_H #ifndef PROCESS_MONITOR_H
#define PROCESS_MONITOR_H #define PROCESS_MONITOR_H
int get_high_cpu_processes(char *process_list[], int max_processes); #include <stdbool.h>
void free_process_list(char *process_list[], int count); #include <sys/types.h>
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 #endif // PROCESS_MONITOR_H

9
include/version.h Normal file
View File

@@ -0,0 +1,9 @@
// version.h
#ifndef VERSION_H
#define VERSION_H
#define VERSION "1.0.0"
#endif // VERSION_H

View File

@@ -10,43 +10,136 @@ check_dependency() {
fi fi
} }
# Step 1: Finding the current working directory and script location # Function to check if a package is installed (for libraries)
SCRIPT_DIR=$(pwd) check_library() {
BASH_SCRIPT="$SCRIPT_DIR/battery_daemon.sh" if ! dpkg -s "$1" &> /dev/null; then
SRC_SCRIPT="$SCRIPT_DIR/battery_monitor" 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 # Step 1: Check for dependencies and install if not present
if [[ -f "$BASH_SCRIPT" ]]; then dependencies=("gcc" "make" "pkg-config")
echo "Found battery_daemon.sh. Moving to /usr/local/bin." libraries=("libgtk-3-dev") # Add other required development libraries here
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")
echo "Checking for build dependencies..."
for dep in "${dependencies[@]}"; do for dep in "${dependencies[@]}"; do
check_dependency "$dep" check_dependency "$dep"
done 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" SYSTEMD_SERVICE="$SCRIPT_DIR/battery_monitor.service"
USER_SYSTEMD_DIR="$HOME/.config/systemd/user" USER_SYSTEMD_DIR="$HOME/.config/systemd/user"
if [[ -f "$SYSTEMD_SERVICE" ]]; then if [[ -f "$SYSTEMD_SERVICE" ]]; then
echo "Found battery_monitor.service." echo "Found battery_monitor.service."
# Create the user systemd directory if it doesn't exist # Create the user systemd directory if it doesn't exist
if [[ ! -d "$USER_SYSTEMD_DIR" ]]; then if [[ ! -d "$USER_SYSTEMD_DIR" ]]; then
echo "Creating user systemd directory: $USER_SYSTEMD_DIR" echo "Creating user systemd directory: $USER_SYSTEMD_DIR"
mkdir -p "$USER_SYSTEMD_DIR" mkdir -p "$USER_SYSTEMD_DIR"
fi fi
echo "Copying battery_monitor.service to $USER_SYSTEMD_DIR" echo "Copying battery_monitor.service to $USER_SYSTEMD_DIR"
cp "$SYSTEMD_SERVICE" "$USER_SYSTEMD_DIR/" cp "$SYSTEMD_SERVICE" "$USER_SYSTEMD_DIR/"
else else
@@ -54,7 +147,7 @@ else
exit 1 exit 1
fi 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..." echo "Reloading systemd user daemon..."
systemctl --user daemon-reload systemctl --user daemon-reload
@@ -71,3 +164,38 @@ else
echo "Failed to start the service." echo "Failed to start the service."
exit 1 exit 1
fi 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 <<EOF > "$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."

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
src/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
backups/

View File

@@ -1,26 +1,117 @@
// Battery_monitor.c
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include "battery_monitor.h" #include "battery_monitor.h"
#include "process_monitor.h"
#include "version.h"
#include <ctype.h>
#include <string.h>
#include <limits.h>
// Define the battery thresholds // Global Variables for Thresholds
#define THRESHOLD_LOW 15 int THRESHOLD_LOW = 25; // Default values
#define THRESHOLD_CRITICAL 5 int THRESHOLD_CRITICAL = 5;
#define THRESHOLD_HIGH 75 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 // Track if notifications have been sent
int notified_low = 0; int notified_low = 0;
int notified_critical = 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"); log_message("Battery monitor started");
load_thresholds_from_config();
while (1) { while (1) {
if (is_charging()) { if (is_charging()) {
// Reset notifications if the battery is charging // Reset notifications if the battery is charging
log_message("Battery is charging, notifications reset"); log_message("Battery is charging, notifications reset");
notified_low = 0; notified_low = 0;
notified_critical = 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 sleep(300); // Sleep for 5 minutes while charging
continue; continue;
} }
@@ -43,6 +134,14 @@ int main() {
sleep_duration = 60; // Sleep for 1 minute when low 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 // Check if the battery level is below the critical threshold
if (battery_level <= THRESHOLD_CRITICAL && !notified_critical) { if (battery_level <= THRESHOLD_CRITICAL && !notified_critical) {
log_message("Battery critically low, showing notification"); log_message("Battery critically low, showing notification");

View File

@@ -1,3 +1,5 @@
// Notification.c
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -9,6 +11,8 @@
#include "battery_monitor.h" #include "battery_monitor.h"
#include "process_monitor.h" #include "process_monitor.h"
#include "log_message.h" #include "log_message.h"
#include <glob.h>
#include <sys/stat.h>
#define CSS_STYLE "\ #define CSS_STYLE "\
* { \ * { \
@@ -21,28 +25,67 @@
} \ } \
" "
// Function to get the battery level extern int battery_saving_mode_active;
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++) { typedef enum {
file = fopen(battery_paths[i], "r"); INIT_SYSTEMD,
if (file != NULL) { INIT_SYSVINIT,
break; 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) { if (file == NULL) {
perror("Failed to open capacity file"); perror("Failed to open capacity file");
log_message("Failed to open capacity file"); log_message("Failed to open capacity file");
return -1; return -1;
} }
int battery_level;
if (fscanf(file, "%d", &battery_level) != 1) { if (fscanf(file, "%d", &battery_level) != 1) {
perror("Failed to read battery level"); perror("Failed to read battery level");
log_message("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 // Function to set the screen brightness
int set_brightness(int brightness) { int set_brightness(int brightness) {
const char *brightness_path = "/sys/class/backlight/intel_backlight/brightness"; char *brightness_path = get_backlight_device_path("brightness");
const char *max_brightness_path = "/sys/class/backlight/intel_backlight/max_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 max_brightness = 100;
int new_brightness = 0; int new_brightness = 0;
char buffer[10]; char buffer[10];
@@ -124,6 +193,8 @@ int set_brightness(int brightness) {
return -1; return -1;
} }
free(brightness_path);
free(max_brightness_path);
close(fd); close(fd);
return 0; return 0;
} }
@@ -135,58 +206,49 @@ int activate_battery_saving_mode() {
// Get the current PID of the running program // Get the current PID of the running program
pid_t current_pid = getpid(); pid_t current_pid = getpid();
// Call the get_high_cpu_processes from process_monitor.c to get the list of high CPU-consuming processes // Suspend high CPU processes
char *process_list[100]; log_message("Suspending high CPU processes");
int process_count = get_high_cpu_processes(process_list, 100); if (run_battery_saving_mode(current_pid) == -1) {
log_message("Failed to suspend high CPU processes");
if (process_count == -1) {
log_message("Failed to get high CPU processes");
return -1; return -1;
} }
// Loop through each high CPU process and kill it, excluding this program's own PID // Suspend user daemons
for (int i = 0; i < process_count; i++) { log_message("Suspending user daemons");
char command[300]; if (suspend_user_daemons() == -1) {
char pid[10]; log_message("Failed to suspend user daemons");
char process_name[100]; return -1;
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;
}
} }
free_process_list(process_list, process_count);
// Set the brightness to 50% for battery saving // Set the brightness to 50% for battery saving
if (set_brightness(50) == -1) { if (set_brightness(50) == -1) {
log_message("Failed to set brightness to 50%"); log_message("Failed to set brightness to 50%");
return -1; return -1;
} }
// Set the battery-saving mode active flag
battery_saving_mode_active = 1;
return 0; return 0;
} }
// Function to enter sleep mode // Function to enter sleep mode
int enter_sleep_mode() { int enter_sleep_mode() {
log_message("Entering 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 // 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 // Function to check if the battery is charging
int is_charging() { int is_charging() {
const char *status_paths[] = { char *status_path = get_battery_device_path("status");
"/sys/class/power_supply/BAT0/status", if (status_path == NULL) {
"/sys/class/power_supply/BAT1/status" log_message("Failed to find battery status file");
}; return -1;
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;
}
} }
FILE *file = fopen(status_path, "r");
free(status_path);
if (file == NULL) { if (file == NULL) {
perror("Failed to open status file"); perror("Failed to open status file");
log_message("Failed to open status file"); log_message("Failed to open status file");
return -1; return -1;
} }
char status[16];
if (fscanf(file, "%15s", status) != 1) { if (fscanf(file, "%15s", status) != 1) {
perror("Failed to read battery status"); perror("Failed to read battery status");
log_message("Failed to read battery status"); log_message("Failed to read battery status");
@@ -322,4 +380,3 @@ int is_charging() {
fclose(file); fclose(file);
return (strcmp(status, "Charging") == 0); return (strcmp(status, "Charging") == 0);
} }

View File

@@ -1,307 +0,0 @@
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <libgen.h>
#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);
}

View File

@@ -1,18 +1,44 @@
// process_monitor.c
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <ctype.h> #include <ctype.h>
#include <sys/types.h> #include <sys/types.h>
#include <stdbool.h>
#include <signal.h>
#include "process_monitor.h"
#include "log_message.h" #include "log_message.h"
#include <dirent.h>
#include <pwd.h>
#include <sys/stat.h>
#define BUFFER_SIZE 1024 #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_CRITICAL_PROCESSES 100
#define MAX_IGNORE_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) // 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 // Function to perform case-insensitive string comparison
int case_insensitive_compare(const char *a, const char *b) { 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; return *a == *b;
} }
// Function to dynamically build the list of critical processes // Function to output messages based on dry run mode
void build_critical_processes_list(char *critical_list[], int *count) { void output_message(const char *message) {
int index = 0; if (dry_run) {
printf("%s\n", message);
// Add default critical processes } else {
for (int i = 0; default_critical_processes[i] != NULL; i++) { log_message(message);
critical_list[index++] = strdup(default_critical_processes[i]);
} }
*count = index;
} }
// Helper function to remove leading/trailing whitespace // Helper function to remove leading/trailing whitespace
char *trim_whitespace(char *str) { static char *trim_whitespace(char *str) {
char *end; char *end;
// Trim leading space // Trim leading space
@@ -57,15 +80,28 @@ char *trim_whitespace(char *str) {
return 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 // Function to dynamically get the user's home directory and build the config file path
char *get_config_file_path() { char *get_config_file_path() {
const char *home_dir = getenv("HOME"); const char *home_dir = getenv("HOME");
if (home_dir == NULL) { if (home_dir == NULL) {
log_message("Failed to get HOME environment variable"); output_message("Failed to get HOME environment variable");
return NULL; 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) { if (config_file_path != NULL) {
strcpy(config_file_path, home_dir); strcpy(config_file_path, home_dir);
strcat(config_file_path, CONFIG_FILE); 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 // 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(); char *config_file_path = get_config_file_path();
if (config_file_path == NULL) { if (config_file_path == NULL) {
log_message("Could not determine the config file path"); output_message("Could not determine the config file path");
return -1; // 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"); FILE *config_file = fopen(config_file_path, "r");
free(config_file_path); free(config_file_path);
int ignore_count = 0;
if (config_file == NULL) { if (config_file == NULL) {
log_message("Failed to open config file"); output_message("Failed to open config file");
return -1; // Proceed with default critical processes
build_critical_processes_list(ignore_list, &ignore_count);
return ignore_count;
} }
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
int ignore_count = 0;
while (fgets(buffer, sizeof(buffer), config_file) != NULL) { 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, "="); char *token = strtok(buffer, "=");
token = strtok(NULL, "="); // Get the processes list after '=' 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); 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); build_critical_processes_list(ignore_list, &ignore_count);
return ignore_count; return ignore_count;
@@ -127,83 +169,237 @@ int is_process_critical(const char *process_name, char *ignore_list[], int ignor
return 0; return 0;
} }
// Get the list of high CPU processes excluding ignored and root processes // Main function to run battery saving mode
int get_high_cpu_processes(char *process_list[], int max_processes) { int run_battery_saving_mode(pid_t current_pid) {
output_message("Running battery saving mode in process_monitor");
FILE *fp; FILE *fp;
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
int process_count = 0;
// Command to get top CPU-consuming processes excluding root processes // Command to get high CPU-consuming processes excluding root processes
const char *command = "ps -eo user,pid,comm,%cpu --sort=-%cpu | grep -vE '^root'"; const char *command = "ps -eo user:32,pid,pcpu,comm --no-headers --sort=-pcpu";
fp = popen(command, "r"); fp = popen(command, "r");
if (fp == NULL) { 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; return -1;
} }
// Load ignore processes from config file // Load ignore processes from config file
char *ignore_list[100]; char *ignore_list[MAX_IGNORE_PROCESSES];
int ignore_count = get_ignore_processes(ignore_list, 100); 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 // Process the list and handle processes accordingly
while (fgets(buffer, sizeof(buffer), fp) != NULL && process_count < max_processes) { while (fgets(buffer, sizeof(buffer), fp) != NULL) {
char user[50], command_name[100]; // 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; int pid;
float cpu_usage; float cpu_usage;
if (sscanf(buffer, "%49s %d %99s %f", user, &pid, command_name, &cpu_usage) == 4) { int items = sscanf(line, "%32s %d %f %255[^\n]", user, &pid, &cpu_usage, command_name);
if (!is_process_critical(command_name, ignore_list, ignore_count)) {
// Allocate memory for the process info and store the PID if (items == 4) {
process_list[process_count] = malloc(BUFFER_SIZE); // Exclude root processes
snprintf(process_list[process_count], BUFFER_SIZE, "%d", pid); // Only storing the PID if (strcmp(user, "root") == 0) {
process_count++; continue;
} 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
} }
// 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++) { for (int i = 0; i < ignore_count; i++) {
free(ignore_list[i]); free(ignore_list[i]);
} }
pclose(fp); pclose(fp);
return process_count; return 0;
} }
// Function to handle killing high CPU processes int resume_high_cpu_processes() {
void kill_high_cpu_processes(char *process_list[], int process_count, pid_t current_pid) { for (int i = 0; i < suspended_high_cpu_count; i++) {
for (int i = 0; i < process_count; i++) { pid_t pid = suspended_high_cpu_pids[i];
pid_t process_pid = atoi(process_list[i]);
if (process_pid == current_pid) { if (dry_run) {
log_message("Skipping killing the current process."); printf("Dry run: Would resume high CPU process PID: %d\n", pid);
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 { } 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 int suspend_user_daemons() {
void free_process_list(char *process_list[], int count) { DIR *proc_dir = opendir("/proc");
for (int i = 0; i < count; i++) { if (proc_dir == NULL) {
free(process_list[i]); 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;
}

View File

@@ -1,177 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#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]);
}
}