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)
$(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

171
README.md
View File

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

View File

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

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

View File

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

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,31 +10,124 @@ 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"
@@ -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 <<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 <unistd.h>
#include <stdlib.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
#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");

View File

@@ -1,3 +1,5 @@
// Notification.c
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
@@ -9,6 +11,8 @@
#include "battery_monitor.h"
#include "process_monitor.h"
#include "log_message.h"
#include <glob.h>
#include <sys/stat.h>
#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);
}

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 <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <stdbool.h>
#include <signal.h>
#include "process_monitor.h"
#include "log_message.h"
#include <dirent.h>
#include <pwd.h>
#include <sys/stat.h>
#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;
}

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]);
}
}