Fixed git ignore, updated much functionality, updated the procress_monitor to work, updated install.sh, added license, updated Makefile, updated install.sh, updated README.md, all that

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

1
src/.gitignore vendored Normal file
View File

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

View File

@@ -1,26 +1,117 @@
// Battery_monitor.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "battery_monitor.h"
#include "process_monitor.h"
#include "version.h"
#include <ctype.h>
#include <string.h>
#include <limits.h>
// Define the battery thresholds
#define THRESHOLD_LOW 15
#define THRESHOLD_CRITICAL 5
#define THRESHOLD_HIGH 75
// Global Variables for Thresholds
int THRESHOLD_LOW = 25; // Default values
int THRESHOLD_CRITICAL = 5;
int THRESHOLD_HIGH = 80;
// Function to trim leading and trailing whitespace
static char *trim_whitespace(char *str) {
char *end;
// Trim leading space
while (isspace((unsigned char)*str)) str++;
if (*str == 0) return str; // All spaces?
// Trim trailing space
end = str + strlen(str) - 1;
while (end > str && isspace((unsigned char)*end)) end--;
// Write new null terminator character
end[1] = '\0';
return str;
}
void load_thresholds_from_config() {
char *home_dir = getenv("HOME");
if (home_dir == NULL) {
log_message("Failed to get HOME environment variable");
return;
}
char config_file_path[PATH_MAX];
snprintf(config_file_path, sizeof(config_file_path), "%s/.config/battery_monitor/config.config", home_dir);
FILE *config_file = fopen(config_file_path, "r");
if (config_file == NULL) {
log_message("Failed to open config file, using default thresholds");
return;
}
char buffer[256];
while (fgets(buffer, sizeof(buffer), config_file) != NULL) {
char *line = trim_whitespace(buffer);
// Skip empty lines and comments
if (strlen(line) == 0 || line[0] == '#') {
continue;
}
char key[64];
char value[64];
if (sscanf(line, "%63[^=]=%63s", key, value) == 2) {
if (strcmp(key, "threshold_low") == 0) {
THRESHOLD_LOW = atoi(value);
} else if (strcmp(key, "threshold_critical") == 0) {
THRESHOLD_CRITICAL = atoi(value);
} else if (strcmp(key, "threshold_high") == 0) {
THRESHOLD_HIGH = atoi(value);
}
}
}
fclose(config_file);
// Log the loaded thresholds
char message[256];
snprintf(message, sizeof(message), "Loaded thresholds: LOW=%d, CRITICAL=%d, HIGH=%d",
THRESHOLD_LOW, THRESHOLD_CRITICAL, THRESHOLD_HIGH);
log_message(message);
}
// Track if notifications have been sent
int notified_low = 0;
int notified_critical = 0;
int battery_saving_mode_active = 0; // 0: inactive, 1: active
int main() {
int main(int argc, char *argv[]) {
if (argc > 1 && strcmp(argv[1], "--version") == 0) {
printf("Battery Monitor version %s\n", VERSION);
return 0;
}
log_message("Battery monitor started");
load_thresholds_from_config();
while (1) {
if (is_charging()) {
// Reset notifications if the battery is charging
log_message("Battery is charging, notifications reset");
notified_low = 0;
notified_critical = 0;
if (battery_saving_mode_active) {
// Resume suspended processes
log_message("Battery is charging, resuming suspended processes");
resume_high_cpu_processes();
resume_user_daemons();
battery_saving_mode_active = 0;
}
sleep(300); // Sleep for 5 minutes while charging
continue;
}
@@ -43,6 +134,14 @@ int main() {
sleep_duration = 60; // Sleep for 1 minute when low
}
// Check if battery-saving mode is active and battery level has surpassed THRESHOLD_LOW
if (battery_saving_mode_active && battery_level > THRESHOLD_LOW) {
log_message("Battery level above threshold, resuming suspended processes");
resume_high_cpu_processes();
resume_user_daemons();
battery_saving_mode_active = 0;
}
// Check if the battery level is below the critical threshold
if (battery_level <= THRESHOLD_CRITICAL && !notified_critical) {
log_message("Battery critically low, showing notification");

View File

@@ -1,3 +1,5 @@
// Notification.c
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
@@ -9,6 +11,8 @@
#include "battery_monitor.h"
#include "process_monitor.h"
#include "log_message.h"
#include <glob.h>
#include <sys/stat.h>
#define CSS_STYLE "\
* { \
@@ -21,28 +25,67 @@
} \
"
// Function to get the battery level
int get_battery_level() {
const char *battery_paths[] = {
"/sys/class/power_supply/BAT0/capacity",
"/sys/class/power_supply/BAT1/capacity"
};
FILE *file;
int battery_level = -1;
extern int battery_saving_mode_active;
for (int i = 0; i < sizeof(battery_paths) / sizeof(battery_paths[0]); i++) {
file = fopen(battery_paths[i], "r");
if (file != NULL) {
break;
typedef enum {
INIT_SYSTEMD,
INIT_SYSVINIT,
INIT_OPENRC,
INIT_UNKNOWN
} init_system_t;
init_system_t detect_init_system() {
struct stat sb;
if (stat("/run/systemd/system", &sb) == 0) {
return INIT_SYSTEMD;
} else if (stat("/sbin/init", &sb) == 0) {
// Additional checks can be added here
return INIT_SYSVINIT;
} else if (stat("/run/openrc", &sb) == 0) {
return INIT_OPENRC;
}
return INIT_UNKNOWN;
}
// Function to find the battery path dynamically
char *get_battery_device_path(const char *file_name) {
glob_t glob_result;
char pattern[PATH_MAX];
snprintf(pattern, sizeof(pattern), "/sys/class/power_supply/BAT*/%s", file_name);
if (glob(pattern, 0, NULL, &glob_result) == 0) {
if (glob_result.gl_pathc > 0) {
char *battery_path = strdup(glob_result.gl_pathv[0]);
globfree(&glob_result);
return battery_path;
}
}
globfree(&glob_result);
return NULL;
}
// Function to get the battery level
int get_battery_level() {
char *capacity_path = get_battery_device_path("capacity");
if (capacity_path == NULL) {
log_message("Failed to find battery capacity file");
return -1;
}
FILE *file = fopen(capacity_path, "r");
free(capacity_path);
if (file == NULL) {
perror("Failed to open capacity file");
log_message("Failed to open capacity file");
return -1;
}
int battery_level;
if (fscanf(file, "%d", &battery_level) != 1) {
perror("Failed to read battery level");
log_message("Failed to read battery level");
@@ -78,10 +121,36 @@ void log_message(const char *message) {
}
}
char *get_backlight_device_path(const char *file_name) {
glob_t glob_result;
char pattern[PATH_MAX];
snprintf(pattern, sizeof(pattern), "/sys/class/backlight/*/%s", file_name);
if (glob(pattern, 0, NULL, &glob_result) == 0) {
if (glob_result.gl_pathc > 0) {
char *backlight_path = strdup(glob_result.gl_pathv[0]);
globfree(&glob_result);
return backlight_path;
}
}
globfree(&glob_result);
return NULL;
}
// Function to set the screen brightness
int set_brightness(int brightness) {
const char *brightness_path = "/sys/class/backlight/intel_backlight/brightness";
const char *max_brightness_path = "/sys/class/backlight/intel_backlight/max_brightness";
char *brightness_path = get_backlight_device_path("brightness");
char *max_brightness_path = get_backlight_device_path("max_brightness");
if (brightness_path == NULL || max_brightness_path == NULL) {
log_message("Failed to find backlight brightness files");
free(brightness_path);
free(max_brightness_path);
return -1;
}
int max_brightness = 100;
int new_brightness = 0;
char buffer[10];
@@ -124,6 +193,8 @@ int set_brightness(int brightness) {
return -1;
}
free(brightness_path);
free(max_brightness_path);
close(fd);
return 0;
}
@@ -135,58 +206,49 @@ int activate_battery_saving_mode() {
// Get the current PID of the running program
pid_t current_pid = getpid();
// Call the get_high_cpu_processes from process_monitor.c to get the list of high CPU-consuming processes
char *process_list[100];
int process_count = get_high_cpu_processes(process_list, 100);
if (process_count == -1) {
log_message("Failed to get high CPU processes");
// Suspend high CPU processes
log_message("Suspending high CPU processes");
if (run_battery_saving_mode(current_pid) == -1) {
log_message("Failed to suspend high CPU processes");
return -1;
}
// Loop through each high CPU process and kill it, excluding this program's own PID
for (int i = 0; i < process_count; i++) {
char command[300];
char pid[10];
char process_name[100];
sscanf(process_list[i], "%9s %99s", pid, process_name);
pid_t process_pid = atoi(pid);
if (process_pid == current_pid) {
char log_msg[200];
snprintf(log_msg, sizeof(log_msg), "Skipping own process: %s (PID: %s)", process_name, pid);
log_message(log_msg);
continue;
}
char log_msg[200];
snprintf(log_msg, sizeof(log_msg), "Killing process: %s (PID: %s)", process_name, pid);
log_message(log_msg);
snprintf(command, sizeof(command), "kill -9 %s", pid);
if (system(command) == -1) {
log_message("Failed to kill process");
free_process_list(process_list, process_count);
return -1;
}
// Suspend user daemons
log_message("Suspending user daemons");
if (suspend_user_daemons() == -1) {
log_message("Failed to suspend user daemons");
return -1;
}
free_process_list(process_list, process_count);
// Set the brightness to 50% for battery saving
if (set_brightness(50) == -1) {
log_message("Failed to set brightness to 50%");
return -1;
}
// Set the battery-saving mode active flag
battery_saving_mode_active = 1;
return 0;
}
// Function to enter sleep mode
int enter_sleep_mode() {
log_message("Entering sleep mode");
return system("systemctl suspend");
init_system_t init_sys = detect_init_system();
switch (init_sys) {
case INIT_SYSTEMD:
return system("systemctl suspend");
case INIT_SYSVINIT:
return system("pm-suspend");
case INIT_OPENRC:
return system("loginctl suspend");
default:
log_message("Unknown init system, cannot enter sleep mode");
return -1;
}
}
// Function to apply custom CSS styles to the GTK widgets
@@ -292,26 +354,22 @@ void show_notification(const char *message, const char *title) {
// Function to check if the battery is charging
int is_charging() {
const char *status_paths[] = {
"/sys/class/power_supply/BAT0/status",
"/sys/class/power_supply/BAT1/status"
};
FILE *file;
char status[16];
for (int i = 0; i < sizeof(status_paths) / sizeof(status_paths[0]); i++) {
file = fopen(status_paths[i], "r");
if (file != NULL) {
break;
}
char *status_path = get_battery_device_path("status");
if (status_path == NULL) {
log_message("Failed to find battery status file");
return -1;
}
FILE *file = fopen(status_path, "r");
free(status_path);
if (file == NULL) {
perror("Failed to open status file");
log_message("Failed to open status file");
return -1;
}
char status[16];
if (fscanf(file, "%15s", status) != 1) {
perror("Failed to read battery status");
log_message("Failed to read battery status");
@@ -322,4 +380,3 @@ int is_charging() {
fclose(file);
return (strcmp(status, "Charging") == 0);
}

View File

@@ -1,307 +0,0 @@
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <libgen.h>
#include "battery_monitor.h"
#include "process_monitor.h"
#include "log_message.h"
#define CSS_STYLE "\
* { \
background-color: #333333; \
color: white; \
} \
button { \
background-color: #555555; \
color: white; \
} \
"
// Function to get the battery level
int get_battery_level() {
const char *battery_paths[] = {
"/sys/class/power_supply/BAT0/capacity",
"/sys/class/power_supply/BAT1/capacity"
};
FILE *file;
int battery_level = -1;
for (int i = 0; i < sizeof(battery_paths) / sizeof(battery_paths[0]); i++) {
file = fopen(battery_paths[i], "r");
if (file != NULL) {
break;
}
}
if (file == NULL) {
perror("Failed to open capacity file");
log_message("Failed to open capacity file");
return -1;
}
if (fscanf(file, "%d", &battery_level) != 1) {
perror("Failed to read battery level");
log_message("Failed to read battery level");
fclose(file);
return -1;
}
fclose(file);
return battery_level;
}
// Function to get the base directory of the executable
char *get_base_directory() {
static char base_dir[PATH_MAX];
ssize_t count = readlink("/proc/self/exe", base_dir, PATH_MAX);
if (count != -1) {
dirname(base_dir);
}
return base_dir;
}
// Function to log messages to a file
void log_message(const char *message) {
char log_file[PATH_MAX];
snprintf(log_file, PATH_MAX, "/tmp/battery_monitor.log");
FILE *log_file_ptr = fopen(log_file, "a");
if (log_file_ptr) {
fprintf(log_file_ptr, "%s\n", message);
fclose(log_file_ptr);
} else {
perror("Failed to open log file");
}
}
// Function to set the screen brightness
int set_brightness(int brightness) {
const char *brightness_path = "/sys/class/backlight/intel_backlight/brightness";
const char *max_brightness_path = "/sys/class/backlight/intel_backlight/max_brightness";
int max_brightness = 100;
int new_brightness = 0;
char buffer[10];
// Open max brightness file
int fd = open(max_brightness_path, O_RDONLY);
if (fd == -1) {
perror("Failed to open max brightness file");
log_message("Failed to open max brightness file");
return -1; // Return failure if the file can't be opened
}
// Read max brightness value
if (read(fd, buffer, sizeof(buffer)) != -1) {
max_brightness = atoi(buffer);
} else {
perror("Failed to read max brightness");
log_message("Failed to read max brightness");
close(fd);
return -1; // Return failure if the file can't be read
}
close(fd);
// Calculate the new brightness
new_brightness = max_brightness * brightness / 100;
// Write the new brightness value to the brightness file
fd = open(brightness_path, O_WRONLY);
if (fd == -1) {
perror("Failed to open brightness file");
log_message("Failed to open brightness file");
return -1; // Return failure if the file can't be opened
}
snprintf(buffer, sizeof(buffer), "%d", new_brightness);
if (write(fd, buffer, strlen(buffer)) == -1) {
perror("Failed to write to brightness file");
log_message("Failed to write to brightness file");
close(fd);
return -1; // Return failure if the write fails
}
close(fd);
return 0; // Success
}
// Function to activate battery saving mode
int activate_battery_saving_mode() {
log_message("Activating battery saving mode");
// Get the current PID of the running program
pid_t current_pid = getpid();
// Call the get_high_cpu_processes from process_monitor.c to get the list of high CPU-consuming processes
char *process_list[100]; // Assuming a maximum of 100 processes to handle
int process_count = get_high_cpu_processes(process_list, 100);
if (process_count == -1) {
log_message("Failed to get high CPU processes");
return -1; // Return failure if processes couldn't be killed
}
// Loop through each high CPU process and kill it, excluding this program's own PID
for (int i = 0; i < process_count; i++) {
char command[300];
// Extract the PID and process name from process_list[i]
char pid[10];
char process_name[100];
sscanf(process_list[i], "%9s %99s", pid, process_name); // Assuming the format "PID ProcessName" in process_list
// Convert the PID string to a number for comparison
pid_t process_pid = atoi(pid);
// Check if the process PID matches the current program's PID, if so, skip it
if (process_pid == current_pid) {
char log_msg[200]; // Declare log_msg correctly
snprintf(log_msg, sizeof(log_msg), "Skipping own process: %s (PID: %s)", process_name, pid);
log_message(log_msg);
continue; // Skip killing the current program's own process
}
// Log the process name and PID before killing the process
char log_msg[200];
snprintf(log_msg, sizeof(log_msg), "Killing process: %s (PID: %s)", process_name, pid);
log_message(log_msg);
// Kill the process by PID
snprintf(command, sizeof(command), "kill -9 %s", pid);
if (system(command) == -1) {
log_message("Failed to kill process");
free_process_list(process_list, process_count);
return -1; // Return failure if the command fails
}
}
// Free the dynamically allocated process list
free_process_list(process_list, process_count);
// Set the brightness to 50% for battery saving
if (set_brightness(50) == -1) {
log_message("Failed to set brightness to 50%");
return -1; // Return failure if brightness couldn't be set
}
return 0; // Success
}
// Function to enter sleep mode
int enter_sleep_mode() {
log_message("Entering sleep mode");
return system("systemctl suspend"); // Return system command result
}
// Function to apply custom CSS styles to the GTK widgets
void apply_css(GtkWidget *widget, const char *css) {
GtkCssProvider *provider = gtk_css_provider_new();
gtk_css_provider_load_from_data(provider, css, -1, NULL);
GtkStyleContext *context = gtk_widget_get_style_context(widget);
gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
g_object_unref(provider);
}
// Function to check the battery status and close the dialog if charging
gboolean check_battery_status(gpointer user_data) {
GtkWidget *dialog = GTK_WIDGET(user_data);
if (is_charging()) {
log_message("Battery started charging, closing notification");
gtk_widget_destroy(dialog);
gtk_main_quit(); // Exit the GTK main loop
return FALSE; // Stop checking
}
return TRUE; // Continue checking
}
// Function to handle dialog response
void on_dialog_response(GtkDialog *dialog, gint response_id, gpointer user_data) {
switch (response_id) {
case GTK_RESPONSE_OK:
log_message("User clicked OK");
break;
case GTK_RESPONSE_APPLY:
log_message("User activated Battery Saving Mode");
activate_battery_saving_mode();
break;
case GTK_RESPONSE_CLOSE:
log_message("User triggered Sleep Mode");
enter_sleep_mode();
break;
default:
break;
}
gtk_widget_destroy(GTK_WIDGET(dialog));
gtk_main_quit(); // Exit the GTK main loop
}
// Function to show the notification dialog
void show_notification(const char *message, const char *title) {
log_message("Showing notification");
GtkWidget *dialog;
gtk_init(0, NULL);
dialog = gtk_message_dialog_new(NULL,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_NONE,
"%s", message);
gtk_dialog_add_button(GTK_DIALOG(dialog), "OK", GTK_RESPONSE_OK);
gtk_dialog_add_button(GTK_DIALOG(dialog), "Battery Saving Mode", GTK_RESPONSE_APPLY);
if (g_strcmp0(title, "Critical Battery Warning") == 0) {
gtk_dialog_add_button(GTK_DIALOG(dialog), "Sleep", GTK_RESPONSE_CLOSE);
}
gtk_window_set_title(GTK_WINDOW(dialog), title);
// Apply CSS styles
apply_css(dialog, CSS_STYLE);
// Set up the callback to check battery status and close if charging
g_timeout_add(1000, check_battery_status, dialog);
// Connect the dialog response to handle button clicks and ensure proper cleanup
g_signal_connect(dialog, "response", G_CALLBACK(on_dialog_response), NULL);
// Show the dialog and enter the GTK main loop
gtk_widget_show_all(dialog);
gtk_main(); // Start the GTK main loop
}
// Function to check if the battery is charging
int is_charging() {
const char *status_paths[] = {
"/sys/class/power_supply/BAT0/status",
"/sys/class/power_supply/BAT1/status"
};
FILE *file;
char status[16];
for (int i = 0; i < sizeof(status_paths) / sizeof(status_paths[0]); i++) {
file = fopen(status_paths[i], "r");
if (file != NULL) {
break;
}
}
if (file == NULL) {
perror("Failed to open status file");
log_message("Failed to open status file");
return -1;
}
if (fscanf(file, "%15s", status) != 1) {
perror("Failed to read battery status");
log_message("Failed to read battery status");
fclose(file);
return -1;
}
fclose(file);
return (strcmp(status, "Charging") == 0);
}

View File

@@ -1,18 +1,44 @@
// process_monitor.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <stdbool.h>
#include <signal.h>
#include "process_monitor.h"
#include "log_message.h"
#include <dirent.h>
#include <pwd.h>
#include <sys/stat.h>
#define BUFFER_SIZE 1024
#define CONFIG_FILE "/.config/battery_monitor/config.config"
#define CONFIG_FILE "/.config/battery_monitor/config.conf"
#define MAX_CRITICAL_PROCESSES 100
#define MAX_IGNORE_PROCESSES 100
#define MAX_SUSPENDED_PROCESSES 1024
pid_t suspended_pids[MAX_SUSPENDED_PROCESSES];
int suspended_count = 0;
pid_t suspended_high_cpu_pids[MAX_SUSPENDED_PROCESSES];
int suspended_high_cpu_count = 0;
bool dry_run = true; // Set to 'true' for dry run, 'false' for normal operation
// CPU usage threshold to consider a process as high CPU-consuming
#define CPU_USAGE_THRESHOLD 1.0
// List of default critical processes (expanded with more essential processes)
const char *default_critical_processes[] = {"systemd", "Xorg", "dbus-daemon", "NetworkManager", "dwm", "DWM", "sddm", "gdm", "fprintd", NULL};
const char *default_critical_processes[] = {
"systemd", "Xorg", "dbus-daemon", "NetworkManager", "dwm", "DWM",
"sddm", "gdm", "fprintd", "gnome-shell", "plasmashell", "kdeinit",
"kwin", "xfce4-session", "cinnamon", "mate-session", "pulseaudio",
"pipewire", "pipewire-pulse", "wireplumber", "lightdm", "udisksd",
"upowerd", "bash", "st", "picom", "python3", "gvfsd", "xdg-document-portal",
"at-spi-bus-launcher", "at-spi2-registr", "volumeicon", NULL
};
// Function to perform case-insensitive string comparison
int case_insensitive_compare(const char *a, const char *b) {
@@ -26,20 +52,17 @@ int case_insensitive_compare(const char *a, const char *b) {
return *a == *b;
}
// Function to dynamically build the list of critical processes
void build_critical_processes_list(char *critical_list[], int *count) {
int index = 0;
// Add default critical processes
for (int i = 0; default_critical_processes[i] != NULL; i++) {
critical_list[index++] = strdup(default_critical_processes[i]);
// Function to output messages based on dry run mode
void output_message(const char *message) {
if (dry_run) {
printf("%s\n", message);
} else {
log_message(message);
}
*count = index;
}
// Helper function to remove leading/trailing whitespace
char *trim_whitespace(char *str) {
static char *trim_whitespace(char *str) {
char *end;
// Trim leading space
@@ -57,15 +80,28 @@ char *trim_whitespace(char *str) {
return str;
}
// Function to dynamically build the list of critical processes
void build_critical_processes_list(char *critical_list[], int *count) {
int index = *count;
// Add default critical processes
for (int i = 0; default_critical_processes[i] != NULL && index < MAX_IGNORE_PROCESSES; i++) {
critical_list[index++] = strdup(default_critical_processes[i]);
}
*count = index;
}
// Function to dynamically get the user's home directory and build the config file path
char *get_config_file_path() {
const char *home_dir = getenv("HOME");
if (home_dir == NULL) {
log_message("Failed to get HOME environment variable");
output_message("Failed to get HOME environment variable");
return NULL;
}
char *config_file_path = malloc(strlen(home_dir) + strlen(CONFIG_FILE) + 1);
size_t path_len = strlen(home_dir) + strlen(CONFIG_FILE) + 1;
char *config_file_path = malloc(path_len);
if (config_file_path != NULL) {
strcpy(config_file_path, home_dir);
strcat(config_file_path, CONFIG_FILE);
@@ -75,26 +111,32 @@ char *get_config_file_path() {
}
// Function to parse the ignore list from the config file
int get_ignore_processes(char *ignore_list[], int max_ignores) {
int get_ignore_processes(char *ignore_list[], int max_ignores, const char *config_key) {
char *config_file_path = get_config_file_path();
if (config_file_path == NULL) {
log_message("Could not determine the config file path");
return -1;
output_message("Could not determine the config file path");
// Proceed with default critical processes
int ignore_count = 0;
build_critical_processes_list(ignore_list, &ignore_count);
return ignore_count;
}
FILE *config_file = fopen(config_file_path, "r");
free(config_file_path);
int ignore_count = 0;
if (config_file == NULL) {
log_message("Failed to open config file");
return -1;
output_message("Failed to open config file");
// Proceed with default critical processes
build_critical_processes_list(ignore_list, &ignore_count);
return ignore_count;
}
char buffer[BUFFER_SIZE];
int ignore_count = 0;
while (fgets(buffer, sizeof(buffer), config_file) != NULL) {
if (strstr(buffer, "ignore_processes_for_sleep") != NULL) {
if (strstr(buffer, config_key) != NULL) {
char *token = strtok(buffer, "=");
token = strtok(NULL, "="); // Get the processes list after '='
@@ -111,7 +153,7 @@ int get_ignore_processes(char *ignore_list[], int max_ignores) {
fclose(config_file);
// Add default critical processes like dwm and Xorg if not already included
// Add default critical processes if not already included
build_critical_processes_list(ignore_list, &ignore_count);
return ignore_count;
@@ -127,83 +169,237 @@ int is_process_critical(const char *process_name, char *ignore_list[], int ignor
return 0;
}
// Get the list of high CPU processes excluding ignored and root processes
int get_high_cpu_processes(char *process_list[], int max_processes) {
// Main function to run battery saving mode
int run_battery_saving_mode(pid_t current_pid) {
output_message("Running battery saving mode in process_monitor");
FILE *fp;
char buffer[BUFFER_SIZE];
int process_count = 0;
// Command to get top CPU-consuming processes excluding root processes
const char *command = "ps -eo user,pid,comm,%cpu --sort=-%cpu | grep -vE '^root'";
// Command to get high CPU-consuming processes excluding root processes
const char *command = "ps -eo user:32,pid,pcpu,comm --no-headers --sort=-pcpu";
fp = popen(command, "r");
if (fp == NULL) {
log_message("Failed to run command to get high CPU processes");
output_message("Failed to run command to get high CPU processes");
return -1;
}
// Load ignore processes from config file
char *ignore_list[100];
int ignore_count = get_ignore_processes(ignore_list, 100);
char *ignore_list[MAX_IGNORE_PROCESSES];
int ignore_count = get_ignore_processes(ignore_list, MAX_IGNORE_PROCESSES, "ignore_processes_for_kill");
if (ignore_count == -1) {
output_message("Failed to get ignore processes");
pclose(fp);
return -1;
}
// Parse each line from the process list
while (fgets(buffer, sizeof(buffer), fp) != NULL && process_count < max_processes) {
char user[50], command_name[100];
// Process the list and handle processes accordingly
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
// Remove leading and trailing whitespace from the buffer
char *line = trim_whitespace(buffer);
// Skip empty lines
if (strlen(line) == 0) {
continue;
}
char user[33]; // +1 for null terminator
char command_name[256];
int pid;
float cpu_usage;
if (sscanf(buffer, "%49s %d %99s %f", user, &pid, command_name, &cpu_usage) == 4) {
if (!is_process_critical(command_name, ignore_list, ignore_count)) {
// Allocate memory for the process info and store the PID
process_list[process_count] = malloc(BUFFER_SIZE);
snprintf(process_list[process_count], BUFFER_SIZE, "%d", pid); // Only storing the PID
process_count++;
} else {
char log_msg[200];
snprintf(log_msg, sizeof(log_msg), "Skipping critical process: %s (PID: %d)", command_name, pid);
log_message(log_msg); // Log critical processes skipped
int items = sscanf(line, "%32s %d %f %255[^\n]", user, &pid, &cpu_usage, command_name);
if (items == 4) {
// Exclude root processes
if (strcmp(user, "root") == 0) {
continue;
}
// Exclude processes with CPU usage below threshold
if (cpu_usage < CPU_USAGE_THRESHOLD) {
continue;
}
if (pid == current_pid) {
output_message("Skipping suspending the current process.");
continue;
}
if (!is_process_critical(command_name, ignore_list, ignore_count)) {
char message[512];
snprintf(message, sizeof(message), "Process to be suspended: %s (PID: %d, CPU Usage: %.2f%%)", command_name, pid, cpu_usage);
output_message(message);
if (dry_run) {
snprintf(message, sizeof(message), "Dry run mode active: Would suspend process %s (PID: %d)", command_name, pid);
output_message(message);
} else {
// Suspend the process
if (kill(pid, SIGSTOP) == -1) {
perror("Failed to suspend process");
output_message("Failed to suspend process");
} else {
// Add to the list of suspended processes
if (suspended_high_cpu_count < MAX_SUSPENDED_PROCESSES) {
suspended_high_cpu_pids[suspended_high_cpu_count++] = pid;
output_message("Process suspended successfully");
} else {
output_message("Maximum suspended processes limit reached.");
}
}
}
} else {
char message[512];
snprintf(message, sizeof(message), "Skipping critical process: %s (PID: %d)", command_name, pid);
output_message(message);
}
} else {
output_message("Failed to parse process information");
}
}
// Free ignore list
// Clean up
for (int i = 0; i < ignore_count; i++) {
free(ignore_list[i]);
}
pclose(fp);
return process_count;
return 0;
}
// Function to handle killing high CPU processes
void kill_high_cpu_processes(char *process_list[], int process_count, pid_t current_pid) {
for (int i = 0; i < process_count; i++) {
pid_t process_pid = atoi(process_list[i]);
int resume_high_cpu_processes() {
for (int i = 0; i < suspended_high_cpu_count; i++) {
pid_t pid = suspended_high_cpu_pids[i];
if (process_pid == current_pid) {
log_message("Skipping killing the current process.");
continue;
}
// Log the process PID before killing
char log_msg[200];
snprintf(log_msg, sizeof(log_msg), "Killing process (PID: %d)", process_pid);
log_message(log_msg);
// Kill the process by PID
char command[50];
snprintf(command, sizeof(command), "kill -9 %d", process_pid);
if (system(command) == -1) {
log_message("Failed to kill process");
if (dry_run) {
printf("Dry run: Would resume high CPU process PID: %d\n", pid);
} else {
log_message("Process killed successfully");
if (kill(pid, SIGCONT) == 0) {
output_message("Resumed high CPU process");
} else {
perror("Failed to resume high CPU process");
}
}
}
suspended_high_cpu_count = 0; // Reset the count after resuming
return 0;
}
// Free the process list
void free_process_list(char *process_list[], int count) {
for (int i = 0; i < count; i++) {
free(process_list[i]);
int suspend_user_daemons() {
DIR *proc_dir = opendir("/proc");
if (proc_dir == NULL) {
perror("Failed to open /proc directory");
return -1;
}
struct dirent *entry;
uid_t uid = getuid(); // Get the UID of the current user
// Load ignore processes from config file for suspending daemons
char *ignore_list[MAX_IGNORE_PROCESSES];
int ignore_count = get_ignore_processes(ignore_list, MAX_IGNORE_PROCESSES, "ignore_processes_for_sleep");
if (ignore_count == -1) {
output_message("Failed to get ignore processes");
closedir(proc_dir);
return -1;
}
while ((entry = readdir(proc_dir)) != NULL) {
if (!isdigit(entry->d_name[0]))
continue;
pid_t pid = atoi(entry->d_name);
if (pid == getpid()) // Skip current process
continue;
char status_file[256];
snprintf(status_file, sizeof(status_file), "/proc/%d/stat", pid);
FILE *status_fp = fopen(status_file, "r");
if (status_fp == NULL)
continue;
char comm[256];
char state;
int ppid, pgrp, session;
unsigned int tty_nr;
uid_t proc_uid = -1;
// Read necessary fields from /proc/[pid]/stat
fscanf(status_fp, "%*d (%255[^)]) %c %d %d %d %u", comm, &state, &ppid, &pgrp, &session, &tty_nr);
fclose(status_fp);
// Get UID of the process owner
char proc_status_file[256];
snprintf(proc_status_file, sizeof(proc_status_file), "/proc/%d/status", pid);
FILE *proc_status_fp = fopen(proc_status_file, "r");
if (proc_status_fp == NULL)
continue;
char line[256];
while (fgets(line, sizeof(line), proc_status_fp)) {
if (strncmp(line, "Uid:", 4) == 0) {
sscanf(line, "Uid:\t%u", &proc_uid);
break;
}
}
fclose(proc_status_fp);
// Check if process is owned by the user and has no controlling terminal
if (proc_uid == uid && tty_nr == 0) {
// Check if process is not critical
if (!is_process_critical(comm, ignore_list, ignore_count)) {
if (dry_run) {
printf("Dry run: Would suspend process PID: %d (%s)\n", pid, comm);
} else {
if (suspended_count < MAX_SUSPENDED_PROCESSES) {
if (kill(pid, SIGSTOP) == 0) {
suspended_pids[suspended_count++] = pid;
output_message("Suspended process");
} else {
perror("Failed to suspend process");
}
} else {
output_message("Maximum suspended processes limit reached.");
break;
}
}
} else {
// Log that we are skipping a critical process
if (dry_run) {
printf("Skipping critical process: PID: %d (%s)\n", pid, comm);
}
}
}
}
// Free ignore_list
for (int i = 0; i < ignore_count; i++) {
free(ignore_list[i]);
}
closedir(proc_dir);
return 0;
}
int resume_user_daemons() {
for (int i = 0; i < suspended_count; i++) {
pid_t pid = suspended_pids[i];
if (dry_run) {
printf("Dry run: Would resume process PID: %d\n", pid);
} else {
if (kill(pid, SIGCONT) == 0) {
output_message("Resumed process");
} else {
perror("Failed to resume process");
}
}
}
suspended_count = 0; // Reset the count after resuming
return 0;
}

View File

@@ -1,177 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include "log_message.h"
#define BUFFER_SIZE 1024
#define CONFIG_FILE "/.config/battery_monitor/config.config"
#define MAX_CRITICAL_PROCESSES 100
#define MAX_IGNORE_PROCESSES 100
// List of default critical processes
const char *default_critical_processes[] = {"systemd", "Xorg", "dbus-daemon", "NetworkManager", NULL};
// Helper function to remove leading/trailing whitespace
char *trim_whitespace(char *str) {
char *end;
// Trim leading space
while (isspace((unsigned char)*str)) str++;
if (*str == 0) return str; // All spaces?
// Trim trailing space
end = str + strlen(str) - 1;
while (end > str && isspace((unsigned char)*end)) end--;
// Write new null terminator character
end[1] = '\0';
return str;
}
// Function to dynamically get the user's home directory and build the config file path
char *get_config_file_path() {
const char *home_dir = getenv("HOME");
if (home_dir == NULL) {
log_message("Failed to get HOME environment variable");
return NULL;
}
char *config_file_path = malloc(strlen(home_dir) + strlen(CONFIG_FILE) + 1);
if (config_file_path != NULL) {
strcpy(config_file_path, home_dir);
strcat(config_file_path, CONFIG_FILE);
}
return config_file_path;
}
// Function to parse the ignore list from the config file
int get_ignore_processes(char *ignore_list[], int max_ignores) {
char *config_file_path = get_config_file_path();
if (config_file_path == NULL) {
log_message("Could not determine the config file path");
return -1;
}
FILE *config_file = fopen(config_file_path, "r");
free(config_file_path);
if (config_file == NULL) {
log_message("Failed to open config file");
return -1;
}
char buffer[BUFFER_SIZE];
int ignore_count = 0;
while (fgets(buffer, sizeof(buffer), config_file) != NULL) {
if (strstr(buffer, "ignore_processes_for_sleep") != NULL) {
char *token = strtok(buffer, "=");
token = strtok(NULL, "="); // Get the processes list after '='
if (token != NULL) {
token = strtok(token, ",");
while (token != NULL && ignore_count < max_ignores) {
ignore_list[ignore_count] = strdup(trim_whitespace(token));
ignore_count++;
token = strtok(NULL, ",");
}
}
}
}
fclose(config_file);
return ignore_count;
}
// Function to check if a process is critical
int is_process_critical(const char *process_name, char *ignore_list[], int ignore_count) {
for (int i = 0; i < ignore_count; i++) {
if (strcmp(process_name, ignore_list[i]) == 0) {
return 1; // Process is critical
}
}
return 0;
}
// Get the list of high CPU processes excluding ignored and root processes
int get_high_cpu_processes(char *process_list[], int max_processes) {
FILE *fp;
char buffer[BUFFER_SIZE];
int process_count = 0;
// Command to get top CPU-consuming processes excluding root processes
const char *command = "ps -eo user,pid,comm,%cpu --sort=-%cpu | grep -vE '^root'";
fp = popen(command, "r");
if (fp == NULL) {
log_message("Failed to run command to get high CPU processes");
return -1;
}
// Load ignore processes from config file
char *ignore_list[100];
int ignore_count = get_ignore_processes(ignore_list, 100);
// Parse each line from the process list
while (fgets(buffer, sizeof(buffer), fp) != NULL && process_count < max_processes) {
char user[50], command_name[100];
int pid;
float cpu_usage;
if (sscanf(buffer, "%49s %d %99s %f", user, &pid, command_name, &cpu_usage) == 4) {
if (!is_process_critical(command_name, ignore_list, ignore_count)) {
// Allocate memory for the process info and store the PID
process_list[process_count] = malloc(BUFFER_SIZE);
snprintf(process_list[process_count], BUFFER_SIZE, "%d", pid); // Only storing the PID
process_count++;
}
}
}
// Free ignore list
for (int i = 0; i < ignore_count; i++) {
free(ignore_list[i]);
}
pclose(fp);
return process_count;
}
// Function to handle killing high CPU processes
void kill_high_cpu_processes(char *process_list[], int process_count, pid_t current_pid) {
for (int i = 0; i < process_count; i++) {
pid_t process_pid = atoi(process_list[i]);
if (process_pid == current_pid) {
log_message("Skipping killing the current process.");
continue;
}
// Log the process PID before killing
char log_msg[200];
snprintf(log_msg, sizeof(log_msg), "Killing process (PID: %d)", process_pid);
log_message(log_msg);
// Kill the process by PID
char command[50];
snprintf(command, sizeof(command), "kill -9 %d", process_pid);
if (system(command) == -1) {
log_message("Failed to kill process");
} else {
log_message("Process killed successfully");
}
}
}
// Free the process list
void free_process_list(char *process_list[], int count) {
for (int i = 0; i < count; i++) {
free(process_list[i]);
}
}