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:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
batter_monitor
|
||||||
|
obj/*
|
||||||
22
LICENSE
Normal file
22
LICENSE
Normal 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.
|
||||||
|
|
||||||
8
Makefile
8
Makefile
@@ -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
175
README.md
@@ -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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
BIN
battery_monitor
BIN
battery_monitor
Binary file not shown.
7
config.conf
Normal file
7
config.conf
Normal 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
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
9
include/version.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// version.h
|
||||||
|
|
||||||
|
#ifndef VERSION_H
|
||||||
|
#define VERSION_H
|
||||||
|
|
||||||
|
#define VERSION "1.0.0"
|
||||||
|
|
||||||
|
#endif // VERSION_H
|
||||||
|
|
||||||
172
install.sh
172
install.sh
@@ -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
1
src/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
backups/
|
||||||
@@ -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");
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user