initial commit
This commit is contained in:
22
Makefile
Normal file
22
Makefile
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
CC = gcc
|
||||||
|
CFLAGS = `pkg-config --cflags gtk+-3.0` -I$(INC_DIR)
|
||||||
|
LDFLAGS = `pkg-config --libs gtk+-3.0`
|
||||||
|
SRC_DIR = src
|
||||||
|
INC_DIR = include
|
||||||
|
OBJ_DIR = obj
|
||||||
|
OBJS = $(OBJ_DIR)/battery_monitor.o $(OBJ_DIR)/notification.o $(OBJ_DIR)/process_monitor.o
|
||||||
|
TARGET = battery_monitor
|
||||||
|
|
||||||
|
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
|
||||||
|
@mkdir -p $(OBJ_DIR)
|
||||||
|
$(CC) $(CFLAGS) -I$(INC_DIR) -o $@ -c $<
|
||||||
|
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
$(TARGET): $(OBJS)
|
||||||
|
$(CC) -o $(TARGET) $(OBJS) $(LDFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(OBJ_DIR) $(TARGET)
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
67
battery_daemon.sh
Executable file
67
battery_daemon.sh
Executable file
@@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Path to the battery monitor binary
|
||||||
|
BINARY_PATH="/usr/local/bin/battery_monitor"
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
LOG_FILE="/tmp/battery_monitor.log"
|
||||||
|
|
||||||
|
# Create Log Directory if it doesn't exist
|
||||||
|
LOG_DIR=$(dirname "$LOG_FILE")
|
||||||
|
|
||||||
|
if [ ! -d "$LOG_DIR" ]; then
|
||||||
|
mkdir -p "$LOG_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$LOG_FILE" ]; then
|
||||||
|
echo "Creating Find the log file"
|
||||||
|
touch "$LOG_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -w "$LOG_FILE" ]; then
|
||||||
|
echo "Cannot Write to the Log: $LOG_FILE. Attempting to change perms..."
|
||||||
|
sudo chown $USER "$LOG_FILE" || {
|
||||||
|
echo "Failed to change perms. Exiting"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Log starting message and environment variables
|
||||||
|
echo "Starting battery monitor script" >> "$LOG_FILE"
|
||||||
|
echo "DISPLAY=$DISPLAY" >> "$LOG_FILE"
|
||||||
|
echo "XAUTHORITY=$XAUTHORITY" >> "$LOG_FILE"
|
||||||
|
|
||||||
|
# Check if binary exists and is executable
|
||||||
|
if [ ! -x "$BINARY_PATH" ]; then
|
||||||
|
echo "Binary not found or not executable: $BINARY_PATH" >> "$LOG_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
check_x_server() {
|
||||||
|
if xset q > /dev/null 2>&1; then
|
||||||
|
echo "X Server is running."
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "X Server is not running yet."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
until check_x_server; do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
if pgrep -x "dwm" > /dev/null; then
|
||||||
|
echo "dwm running"
|
||||||
|
# Start the battery monitor and redirect output to log file
|
||||||
|
exec "$BINARY_PATH" >> "$LOG_FILE" 2>&1
|
||||||
|
|
||||||
|
while pgrep -x "dwm" > /dev/null; do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "waiting for DWM to start"
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
BIN
battery_monitor
Executable file
BIN
battery_monitor
Executable file
Binary file not shown.
16
battery_monitor.service
Executable file
16
battery_monitor.service
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Battery Monitor Service
|
||||||
|
After=default.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/local/bin/battery_daemon.sh
|
||||||
|
Restart=on-failure
|
||||||
|
Environment=DISPLAY=:0
|
||||||
|
Environment=XAUTHORITY=%h/.Xauthority
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
|
||||||
16
include/battery_monitor.h
Normal file
16
include/battery_monitor.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#ifndef BATTERY_MONITOR_H
|
||||||
|
#define BATTERY_MONITOR_H
|
||||||
|
|
||||||
|
void show_notification(const char *message, const char *title);
|
||||||
|
int get_battery_level();
|
||||||
|
int is_charging();
|
||||||
|
int activate_battery_saving_mode();
|
||||||
|
int enter_sleep_mode();
|
||||||
|
int kill_processes(const char *filename);
|
||||||
|
int set_brightness(int brightness);
|
||||||
|
void log_message(const char *message);
|
||||||
|
|
||||||
|
// New function declaration for process monitoring
|
||||||
|
int get_high_cpu_processes(char *process_list[], int max_processes);
|
||||||
|
|
||||||
|
#endif // BATTERY_MONITOR_H
|
||||||
7
include/log_message.h
Normal file
7
include/log_message.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#ifndef LOG_MESSAGE_H
|
||||||
|
#define LOG_MESSAGE_H
|
||||||
|
|
||||||
|
void log_message(const char* msg);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
8
include/process_monitor.h
Normal file
8
include/process_monitor.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#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);
|
||||||
|
|
||||||
|
#endif // PROCESS_MONITOR_H
|
||||||
|
|
||||||
73
install.sh
Executable file
73
install.sh
Executable file
@@ -0,0 +1,73 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Function to check if a command exists
|
||||||
|
check_dependency() {
|
||||||
|
if ! command -v "$1" &> /dev/null; then
|
||||||
|
echo "$1 is not installed. Installing..."
|
||||||
|
sudo apt-get install -y "$1"
|
||||||
|
else
|
||||||
|
echo "$1 is already installed."
|
||||||
|
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"
|
||||||
|
|
||||||
|
# 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")
|
||||||
|
|
||||||
|
for dep in "${dependencies[@]}"; do
|
||||||
|
check_dependency "$dep"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Step 3: Copy battery_monitor.service to user systemd folder
|
||||||
|
SYSTEMD_SERVICE="$SCRIPT_DIR/battery_monitor.service"
|
||||||
|
USER_SYSTEMD_DIR="$HOME/.config/systemd/user"
|
||||||
|
|
||||||
|
if [[ -f "$SYSTEMD_SERVICE" ]]; then
|
||||||
|
echo "Found battery_monitor.service."
|
||||||
|
|
||||||
|
# Create the user systemd directory if it doesn't exist
|
||||||
|
if [[ ! -d "$USER_SYSTEMD_DIR" ]]; then
|
||||||
|
echo "Creating user systemd directory: $USER_SYSTEMD_DIR"
|
||||||
|
mkdir -p "$USER_SYSTEMD_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Copying battery_monitor.service to $USER_SYSTEMD_DIR"
|
||||||
|
cp "$SYSTEMD_SERVICE" "$USER_SYSTEMD_DIR/"
|
||||||
|
else
|
||||||
|
echo "battery_monitor.service not found in the current directory!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 4: Reload the systemd daemon, enable and restart the service (user-level)
|
||||||
|
echo "Reloading systemd user daemon..."
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
|
||||||
|
echo "Enabling battery_monitor.service..."
|
||||||
|
systemctl --user enable battery_monitor.service
|
||||||
|
|
||||||
|
echo "Restarting battery_monitor.service..."
|
||||||
|
systemctl --user restart battery_monitor.service
|
||||||
|
|
||||||
|
# Check if the service was successfully started
|
||||||
|
if systemctl --user is-active --quiet battery_monitor.service; then
|
||||||
|
echo "Service started successfully!"
|
||||||
|
else
|
||||||
|
echo "Failed to start the service."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
BIN
obj/battery_monitor.o
Normal file
BIN
obj/battery_monitor.o
Normal file
Binary file not shown.
BIN
obj/notification.o
Normal file
BIN
obj/notification.o
Normal file
Binary file not shown.
BIN
obj/process_monitor.o
Normal file
BIN
obj/process_monitor.o
Normal file
Binary file not shown.
70
src/battery_monitor.c
Normal file
70
src/battery_monitor.c
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "battery_monitor.h"
|
||||||
|
|
||||||
|
// Define the battery thresholds
|
||||||
|
#define THRESHOLD_LOW 15
|
||||||
|
#define THRESHOLD_CRITICAL 5
|
||||||
|
#define THRESHOLD_HIGH 75
|
||||||
|
|
||||||
|
// Track if notifications have been sent
|
||||||
|
int notified_low = 0;
|
||||||
|
int notified_critical = 0;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
log_message("Battery monitor started");
|
||||||
|
|
||||||
|
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;
|
||||||
|
sleep(300); // Sleep for 5 minutes while charging
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int battery_level = get_battery_level();
|
||||||
|
if (battery_level == -1) {
|
||||||
|
log_message("Battery level read failed, retrying in 1 minute");
|
||||||
|
sleep(60);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamic sleep interval based on battery level
|
||||||
|
int sleep_duration = 60; // Default 1 minute
|
||||||
|
|
||||||
|
if (battery_level > THRESHOLD_HIGH) {
|
||||||
|
sleep_duration = 300; // Sleep for 5 minutes
|
||||||
|
} else if (battery_level <= THRESHOLD_CRITICAL) {
|
||||||
|
sleep_duration = 30; // Sleep for 30 seconds when critically low
|
||||||
|
} else if (battery_level <= THRESHOLD_LOW) {
|
||||||
|
sleep_duration = 60; // Sleep for 1 minute when low
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the battery level is below the critical threshold
|
||||||
|
if (battery_level <= THRESHOLD_CRITICAL && !notified_critical) {
|
||||||
|
log_message("Battery critically low, showing notification");
|
||||||
|
show_notification("Battery is critically low, below 5%", "Critical Battery Warning");
|
||||||
|
notified_critical = 1;
|
||||||
|
} else if (battery_level <= THRESHOLD_LOW && !notified_low) {
|
||||||
|
log_message("Battery low, showing notification");
|
||||||
|
show_notification("Battery is low, below 15%", "Low Battery Warning");
|
||||||
|
notified_low = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset notifications if battery level goes back up
|
||||||
|
if (battery_level > THRESHOLD_LOW) {
|
||||||
|
notified_low = 0;
|
||||||
|
}
|
||||||
|
if (battery_level > THRESHOLD_CRITICAL) {
|
||||||
|
notified_critical = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the dynamically determined duration before checking again
|
||||||
|
sleep(sleep_duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
325
src/notification.c
Normal file
325
src/notification.c
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <signal.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
int process_count = get_high_cpu_processes(process_list, 100);
|
||||||
|
|
||||||
|
if (process_count == -1) {
|
||||||
|
log_message("Failed to get 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to enter sleep mode
|
||||||
|
int enter_sleep_mode() {
|
||||||
|
log_message("Entering sleep mode");
|
||||||
|
return system("systemctl suspend");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal handler for SIGINT
|
||||||
|
void handle_sigint(int sig) {
|
||||||
|
log_message("SIGINT caught, ignoring Ctrl-C");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to set up signal handling
|
||||||
|
void setup_signal_handling() {
|
||||||
|
signal(SIGINT, handle_sigint);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to ignore Enter key and other keyboard inputs
|
||||||
|
gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data) {
|
||||||
|
if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_Escape) {
|
||||||
|
return TRUE; // Prevent default behavior for Enter and Escape keys
|
||||||
|
}
|
||||||
|
return FALSE; // Allow other keys if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Connect key-press-event signal to disable Enter key behavior
|
||||||
|
g_signal_connect(dialog, "key-press-event", G_CALLBACK(on_key_press), NULL);
|
||||||
|
|
||||||
|
// Set up signal handling to trap SIGINT
|
||||||
|
setup_signal_handling();
|
||||||
|
|
||||||
|
// Show the dialog and enter the GTK main loop
|
||||||
|
gtk_widget_show_all(dialog);
|
||||||
|
gtk_main();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
307
src/notification.c.bak
Normal file
307
src/notification.c.bak
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
209
src/process_monitor.c
Normal file
209
src/process_monitor.c
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
#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 (expanded with more essential processes)
|
||||||
|
const char *default_critical_processes[] = {"systemd", "Xorg", "dbus-daemon", "NetworkManager", "dwm", "DWM", "sddm", "gdm", "fprintd", NULL};
|
||||||
|
|
||||||
|
// Function to perform case-insensitive string comparison
|
||||||
|
int case_insensitive_compare(const char *a, const char *b) {
|
||||||
|
while (*a && *b) {
|
||||||
|
if (tolower((unsigned char)*a) != tolower((unsigned char)*b)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
a++;
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
*count = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Add default critical processes like dwm and Xorg if not already included
|
||||||
|
build_critical_processes_list(ignore_list, &ignore_count);
|
||||||
|
|
||||||
|
return ignore_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to check if a process is critical (case-insensitive check)
|
||||||
|
int is_process_critical(const char *process_name, char *ignore_list[], int ignore_count) {
|
||||||
|
for (int i = 0; i < ignore_count; i++) {
|
||||||
|
if (case_insensitive_compare(process_name, ignore_list[i])) {
|
||||||
|
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++;
|
||||||
|
} 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
177
src/process_monitor.c.bak
Normal file
177
src/process_monitor.c.bak
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
#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