greatly simplify it to report idle seconds to a script
Αυτή η υποβολή περιλαμβάνεται σε:
		
							
								
								
									
										24
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								README
									
									
									
									
									
								
							@@ -1,4 +1,24 @@
 | 
				
			|||||||
afkcron
 | 
					afkcron
 | 
				
			||||||
=======
 | 
					=======
 | 
				
			||||||
afkcron reads a configuration which specifies programs to launch after n seconds of afk time. 
 | 
					afkcron simply reports the idle time of the X11 server to a shell script.
 | 
				
			||||||
Once the user is afk for those seconds, it then starts the program. 
 | 
					
 | 
				
			||||||
 | 
					Originally, it was reading config files and also handling the return of the
 | 
				
			||||||
 | 
					user by decreasing priority of launched processes or killing them etc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Now I considered it's best for it to be rather stupid and delegate this 
 | 
				
			||||||
 | 
					to a shell script for example. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Launching afkcron
 | 
				
			||||||
 | 
					=================
 | 
				
			||||||
 | 
					afkcron [path to script] [how often idle time should be polled, in seconds]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example: afkcron script.sh 30 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Shellscript
 | 
				
			||||||
 | 
					===========
 | 
				
			||||||
 | 
					First argument: "active" (if idle time has stopped), "idle": When idling
 | 
				
			||||||
 | 
					Second argument: the number of seconds X11 is idling.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Dependencies
 | 
				
			||||||
 | 
					============
 | 
				
			||||||
 | 
					libX11, libXss. Should be a given.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										447
									
								
								afkcron.c
									
									
									
									
									
								
							
							
						
						
									
										447
									
								
								afkcron.c
									
									
									
									
									
								
							@@ -1,7 +1,7 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * afkcron launches a program depending on the time the user is not using his machine
 | 
					 * afkcron launches a script depending on the time the user is not using his machine
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
 * Copyright (c) 2014-2017 Albert S. <launchutils at quitesimple dot org>
 | 
					 * Copyright (c) 2014-2018 Albert S. <launchutils at quitesimple dot org>
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Permission to use, copy, modify, and distribute this software for any
 | 
					 * Permission to use, copy, modify, and distribute this software for any
 | 
				
			||||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
					 * purpose with or without fee is hereby granted, provided that the above
 | 
				
			||||||
@@ -19,137 +19,17 @@
 | 
				
			|||||||
#define _XOPEN_SOURCE 700
 | 
					#define _XOPEN_SOURCE 700
 | 
				
			||||||
#include <stdio.h>
 | 
					#include <stdio.h>
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
#include <signal.h>
 | 
					#include <signal.h>
 | 
				
			||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
#include <stdarg.h>
 | 
					 | 
				
			||||||
#include <time.h>
 | 
					 | 
				
			||||||
#include <sys/types.h>
 | 
					 | 
				
			||||||
#include <sys/wait.h>
 | 
					 | 
				
			||||||
#include <sys/time.h>
 | 
					 | 
				
			||||||
#include <sys/resource.h>
 | 
					 | 
				
			||||||
#include <X11/Xlib.h>
 | 
					#include <X11/Xlib.h>
 | 
				
			||||||
#include <X11/extensions/scrnsaver.h>
 | 
					#include <X11/extensions/scrnsaver.h>
 | 
				
			||||||
#include <ctype.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define CONFIG_DEFAULT_PATH "/etc/afkcron"
 | 
					 | 
				
			||||||
#define CONFIG_DELIMER ':'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//return actions flags
 | 
					 | 
				
			||||||
#define FLAG_RETURN_KILL (1 << 0)
 | 
					 | 
				
			||||||
#define FLAG_RETURN_PRIO (1 << 1)
 | 
					 | 
				
			||||||
#define FLAG_RETURN_STOP (1 << 2)
 | 
					 | 
				
			||||||
#define FLAG_RETURN_STAY (1 << 3)
 | 
					 | 
				
			||||||
#define FLAG_RETURN_TERM (1 << 4)
 | 
					 | 
				
			||||||
#define FLAG_RETURN_TERMKILL (FLAG_RETURN_TERM | FLAG_RETURN_KILL)
 | 
					 | 
				
			||||||
//general flags
 | 
					 | 
				
			||||||
#define FLAG_SINGLE_SHOT (1 << 0) // fire only once during program lifetime
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//consider merging return action flags and general flags
 | 
					 | 
				
			||||||
struct entry 
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	pid_t pid; // 0 = can be ran, -1 single shot entry which finished, pid > 0 = currently running
 | 
					 | 
				
			||||||
	bool stopped;
 | 
					 | 
				
			||||||
	const char *path;
 | 
					 | 
				
			||||||
	const char *args;
 | 
					 | 
				
			||||||
	int comeback_action; //What to do, when the user returns, starts using the computer again?
 | 
					 | 
				
			||||||
	int idle_seconds;
 | 
					 | 
				
			||||||
	int flags;
 | 
					 | 
				
			||||||
	struct entry *next;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
FILE *logfp = NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct entry *head_entry = NULL; 
 | 
					 | 
				
			||||||
struct entry *tail_entry = NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void *xmalloc(size_t n)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	char *tmp = malloc(n);
 | 
					 | 
				
			||||||
	if(tmp == NULL)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		perror("malloc");
 | 
					 | 
				
			||||||
		exit(EXIT_FAILURE);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return tmp;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void *xstrdup(const char *str)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	char *result = strdup(str);
 | 
					 | 
				
			||||||
	if(result == NULL)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		perror("strdup");
 | 
					 | 
				
			||||||
		exit(EXIT_FAILURE);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool strempty(const char *str)
 | 
					bool strempty(const char *str)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return str == NULL || *str == '\0';
 | 
						return str == NULL || *str == '\0';
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int strcnt(const char *str, const char c)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int result = 0;
 | 
					 | 
				
			||||||
	while(*str)
 | 
					 | 
				
			||||||
		if(*str++ == c) ++result;
 | 
					 | 
				
			||||||
	return result; 
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int split_string(char ***out, const char *str, char delim)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int current = 0;
 | 
					 | 
				
			||||||
	int items = strcnt(str, delim) + 1;
 | 
					 | 
				
			||||||
	*out = xmalloc(items * sizeof(char *));
 | 
					 | 
				
			||||||
	char *temp = xstrdup(str);
 | 
					 | 
				
			||||||
	char *portion = temp;
 | 
					 | 
				
			||||||
	while(*temp)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if(*temp == delim) 
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			*temp=0;
 | 
					 | 
				
			||||||
			(*out)[current] = portion;
 | 
					 | 
				
			||||||
			++current;
 | 
					 | 
				
			||||||
			portion = temp+1;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		++temp;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	(*out)[current] = portion;
 | 
					 | 
				
			||||||
	return ++current;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const char **create_execv_args(const char *name, const char *str, char delim)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if(strempty(name))
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	const char **result = NULL;
 | 
					 | 
				
			||||||
	if(strempty(str))
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		result = xmalloc(2 * sizeof(char *));
 | 
					 | 
				
			||||||
		result[0] = name;
 | 
					 | 
				
			||||||
		result[1] = NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		int items=2;
 | 
					 | 
				
			||||||
		char **args = NULL;
 | 
					 | 
				
			||||||
		int n_args = split_string(&args, str, delim);
 | 
					 | 
				
			||||||
		items += n_args;
 | 
					 | 
				
			||||||
		result = xmalloc(items * sizeof(char *)); 
 | 
					 | 
				
			||||||
		result[0] = name;
 | 
					 | 
				
			||||||
		for(int i=0; i < n_args; i++)
 | 
					 | 
				
			||||||
			result[i+1] = args[i];
 | 
					 | 
				
			||||||
		result[items-1] = NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return result;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void print_err_and_exit(const char *s)
 | 
					void print_err_and_exit(const char *s)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	fputs(s, stderr);
 | 
						fputs(s, stderr);
 | 
				
			||||||
@@ -163,335 +43,116 @@ void perror_and_exit(const char *s)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void logit(const char *format, ...)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if(logfp != NULL)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		va_list args;
 | 
					 | 
				
			||||||
		va_start(args, format);
 | 
					 | 
				
			||||||
		time_t now = time(0);
 | 
					 | 
				
			||||||
		char *timestr = ctime(&now);
 | 
					 | 
				
			||||||
		size_t len = strlen(timestr);
 | 
					 | 
				
			||||||
		if(timestr[len-1] == '\n')
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			timestr[len-1] = 0;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		fprintf(logfp, "%s: ", timestr);
 | 
					 | 
				
			||||||
		vfprintf(logfp, format, args);
 | 
					 | 
				
			||||||
		fflush(logfp);
 | 
					 | 
				
			||||||
		va_end(args);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
int get_idle_seconds(Display *display)
 | 
					int get_idle_seconds(Display *display)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	XScreenSaverInfo info;
 | 
						XScreenSaverInfo info;
 | 
				
			||||||
	if(XScreenSaverQueryInfo(display, DefaultRootWindow(display), &info) == 0)
 | 
						if(XScreenSaverQueryInfo(display, DefaultRootWindow(display), &info) == 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
		return -1; 
 | 
							return -1; 
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return info.idle / 1000; 
 | 
						return info.idle / 1000; 
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void add_entry(struct entry *e)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if(head_entry == NULL)
 | 
					 | 
				
			||||||
		head_entry = e;
 | 
					 | 
				
			||||||
	if(tail_entry != NULL)
 | 
					 | 
				
			||||||
		tail_entry->next = e;
 | 
					 | 
				
			||||||
	tail_entry = e;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int comeback_action_from_string(const char *str)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
		int result = 0;
 | 
					 | 
				
			||||||
		if(! strempty(str))
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			if(strcmp(str, "kill") == 0)
 | 
					 | 
				
			||||||
				result |= FLAG_RETURN_KILL;
 | 
					 | 
				
			||||||
			else if(strcmp(str, "stop") == 0)
 | 
					 | 
				
			||||||
				result |= FLAG_RETURN_STOP;
 | 
					 | 
				
			||||||
			else if(strcmp(str, "prio") == 0)
 | 
					 | 
				
			||||||
				result |= FLAG_RETURN_PRIO;
 | 
					 | 
				
			||||||
			else if(strcmp(str, "term") == 0)
 | 
					 | 
				
			||||||
				result |= FLAG_RETURN_TERM;
 | 
					 | 
				
			||||||
			else if(strcmp(str, "termkill") == 0)
 | 
					 | 
				
			||||||
				result |= FLAG_RETURN_TERMKILL;
 | 
					 | 
				
			||||||
			else
 | 
					 | 
				
			||||||
				result |= FLAG_RETURN_STAY;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int flags_from_string(const char *str)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int result = 0;
 | 
					 | 
				
			||||||
	if(strstr(str, "oneshot"))
 | 
					 | 
				
			||||||
		result |= FLAG_SINGLE_SHOT;
 | 
					 | 
				
			||||||
	return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
//TODO: check integer overflow
 | 
					 | 
				
			||||||
int secs_from_string(char *unitstr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if(unitstr == NULL) 
 | 
					 | 
				
			||||||
		return -1;
 | 
					 | 
				
			||||||
	size_t len = strlen(unitstr);
 | 
					 | 
				
			||||||
	char unit = unitstr[len-1];
 | 
					 | 
				
			||||||
	if(! isdigit(unit))
 | 
					 | 
				
			||||||
		unitstr[len-1] = '\0';
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	int secs = atoi(unitstr);
 | 
					 | 
				
			||||||
	if(secs < 0)
 | 
					 | 
				
			||||||
		return -1;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	switch(unit)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
			case 'd':
 | 
					 | 
				
			||||||
				secs *= 24;
 | 
					 | 
				
			||||||
			case 'h':
 | 
					 | 
				
			||||||
				secs *= 60;
 | 
					 | 
				
			||||||
			case 'm':
 | 
					 | 
				
			||||||
				secs *= 60;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	return secs;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct entry *entry_from_line(const char *line)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	char *l = xstrdup(line);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	char **fields = NULL;
 | 
					 | 
				
			||||||
	int n_fields = split_string(&fields, l, CONFIG_DELIMER);
 | 
					 | 
				
			||||||
	if(n_fields < 5)
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct entry *result = xmalloc(sizeof(struct entry));
 | 
					 | 
				
			||||||
	result->path = fields[0];
 | 
					 | 
				
			||||||
	result->args = fields[1];
 | 
					 | 
				
			||||||
	result->comeback_action = comeback_action_from_string(fields[2]);
 | 
					 | 
				
			||||||
	result->idle_seconds = secs_from_string(fields[3]);
 | 
					 | 
				
			||||||
	result->flags = flags_from_string(fields[4]);
 | 
					 | 
				
			||||||
	return result;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline bool check_entry(struct entry *e)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return ( ! strempty(e->path) ) && ( e->idle_seconds > 0 ) && ( e->comeback_action > 0 ); 
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void read_config(const char *configfile)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	FILE *fp = fopen(configfile, "r");
 | 
					 | 
				
			||||||
	if(fp == NULL)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		perror_and_exit("fopen");
 | 
					 | 
				
			||||||
	}	
 | 
					 | 
				
			||||||
	char *line;
 | 
					 | 
				
			||||||
	size_t n = 0;
 | 
					 | 
				
			||||||
	ssize_t r;
 | 
					 | 
				
			||||||
	while((r = getline(&line, &n, fp)) != -1 )
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if(line[r-1] == '\n') 
 | 
					 | 
				
			||||||
			line[r-1] = '\0';
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
		struct entry *e = entry_from_line(line);
 | 
					 | 
				
			||||||
		if(e == NULL)
 | 
					 | 
				
			||||||
			print_err_and_exit("error reading from file");
 | 
					 | 
				
			||||||
				
 | 
					 | 
				
			||||||
		if(! check_entry(e))
 | 
					 | 
				
			||||||
			print_err_and_exit("Invalid values for entry");
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		add_entry(e);
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	if(ferror(fp))
 | 
					 | 
				
			||||||
		perror_and_exit("error reading from config file");
 | 
					 | 
				
			||||||
	fclose(fp);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void handle_comeback()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	for(struct entry *current = head_entry; current != NULL; current = current->next)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if(current->pid > 0)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			int comeback_action = current->comeback_action;
 | 
					 | 
				
			||||||
			bool wants_term = (comeback_action & FLAG_RETURN_TERM) == FLAG_RETURN_TERM;	
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			if(wants_term)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				logit("Terminating %z\n", current->pid);
 | 
					 | 
				
			||||||
				kill(current->pid, SIGTERM);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
			if(comeback_action & FLAG_RETURN_KILL)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				if(wants_term)
 | 
					 | 
				
			||||||
					while(sleep(2));
 | 
					 | 
				
			||||||
				if(kill(current->pid, SIGKILL) == 0)
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					logit("Killed %z\n", current->pid);
 | 
					 | 
				
			||||||
					current->pid = 0;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				//TODO: and if fail?...
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if(comeback_action & FLAG_RETURN_STOP)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				if(kill(current->pid, SIGSTOP) == 0)
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					logit("Stopped %z\n", current->pid);
 | 
					 | 
				
			||||||
					current->stopped = true;
 | 
					 | 
				
			||||||
					//TODO: ...
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			if(comeback_action & FLAG_RETURN_PRIO)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				logit("Lowering priority for %z\n", current->pid);
 | 
					 | 
				
			||||||
				setpriority(PRIO_PROCESS, current->pid, 19);
 | 
					 | 
				
			||||||
				//TODO: IO prio?
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void handle_finished_pid(pid_t pid)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	for(struct entry *current = head_entry; current != NULL; current = current->next)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if(current->pid == pid)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			if( (current->flags & FLAG_SINGLE_SHOT) )
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				current->pid = -1;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			else
 | 
					 | 
				
			||||||
				current->pid = 0;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void child_handler(int signum, siginfo_t *info, void *context)
 | 
					void child_handler(int signum, siginfo_t *info, void *context)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if(signum != SIGCHLD) 
 | 
						if(signum != SIGCHLD) 
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
		
 | 
						}
 | 
				
			||||||
	pid_t pid = info->si_pid;
 | 
					 | 
				
			||||||
	//TODO
 | 
					 | 
				
			||||||
	int status;
 | 
						int status;
 | 
				
			||||||
	int x = waitpid(pid, &status, WNOHANG);
 | 
						int ret = waitpid(info->si_pid, &status, WNOHANG);
 | 
				
			||||||
	if(x == -1)
 | 
						if(ret == -1)
 | 
				
			||||||
		print_err_and_exit("waitpid failed");
 | 
						{
 | 
				
			||||||
	handle_finished_pid(pid);
 | 
							perror_and_exit("waitpid failed");
 | 
				
			||||||
	
 | 
						}
 | 
				
			||||||
 | 
						if(status == EXIT_FAILURE)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							print_err_and_exit("script did not succeed, exiting");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void set_signals()
 | 
					void set_signals()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct sigaction action;
 | 
						struct sigaction action;
 | 
				
			||||||
	action.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
 | 
						action.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
 | 
				
			||||||
	action.sa_sigaction = &child_handler;
 | 
						action.sa_sigaction = &child_handler;
 | 
				
			||||||
	if(sigaction(SIGCHLD, &action, NULL) == -1)
 | 
						if(sigaction(SIGCHLD, &action, NULL) == -1)
 | 
				
			||||||
		print_err_and_exit("sigaction failed");
 | 
						{
 | 
				
			||||||
 | 
							perror_and_exit("sigaction failed");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void run_script(const char *path, const char *type, int sleepseconds)
 | 
				
			||||||
bool run_entry(struct entry *e)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const char **args = create_execv_args(e->path, e->args, ' ');
 | 
					 | 
				
			||||||
	pid_t child = fork();
 | 
						pid_t child = fork();
 | 
				
			||||||
	if(child == 0)
 | 
						if(child == 0)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		logit("Starting execution of %s\n", e->path);
 | 
							char sleepstr[20];
 | 
				
			||||||
		execv(e->path, (char * const *)args);
 | 
							snprintf(sleepstr, sizeof(sleepstr), "%i", sleepseconds);
 | 
				
			||||||
		fclose(logfp);	
 | 
							const char *args[4];
 | 
				
			||||||
	}
 | 
							args[0] = path;
 | 
				
			||||||
	else if(child > 0)
 | 
							args[1] = type;
 | 
				
			||||||
		e->pid = child;
 | 
							args[2] = sleepstr;
 | 
				
			||||||
	return child != -1;
 | 
							args[3] = NULL;
 | 
				
			||||||
}
 | 
							execv(path, (char * const *)args);
 | 
				
			||||||
 | 
							perror_and_exit("failed launching script");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void run_entries(int idle_seconds)
 | 
						}
 | 
				
			||||||
 | 
						if(child == -1)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
	for(struct entry *e = head_entry; e != NULL; e = e->next)
 | 
							perror_and_exit("fork failed");
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if(e->idle_seconds > idle_seconds) 
 | 
					 | 
				
			||||||
			continue; 
 | 
					 | 
				
			||||||
		if(e->pid == 0)
 | 
					 | 
				
			||||||
			run_entry(e);
 | 
					 | 
				
			||||||
		if(e->pid > 0 && e->stopped)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			logit("Continuing %z\n", e->pid);
 | 
					 | 
				
			||||||
			if(kill(e->pid, SIGCONT) == 0)
 | 
					 | 
				
			||||||
				e->stopped = false;
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int main(int argc, char *argv[])
 | 
					int main(int argc, char *argv[])
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						if(argc < 3)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							printf("Usage: afkcron script.sh interval\n");
 | 
				
			||||||
 | 
							exit(EXIT_FAILURE);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						const char *scriptpath = argv[1];
 | 
				
			||||||
 | 
						unsigned int interval = strtol(argv[2], NULL, 10);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	Display *display = XOpenDisplay(NULL);
 | 
						Display *display = XOpenDisplay(NULL);
 | 
				
			||||||
	if(display == NULL)
 | 
						if(display == NULL)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
		print_err_and_exit("Couldn't open DISPLAY");
 | 
							print_err_and_exit("Couldn't open DISPLAY");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	int event_base, error_base;
 | 
						int event_base, error_base;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (! XScreenSaverQueryExtension(display, &event_base, &error_base)) 
 | 
						if (! XScreenSaverQueryExtension(display, &event_base, &error_base)) 
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
		print_err_and_exit("No XScreenSaver Extension available on this display");
 | 
							print_err_and_exit("No XScreenSaver Extension available on this display");
 | 
				
			||||||
		
 | 
						}
 | 
				
			||||||
	set_signals();
 | 
						set_signals();
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
	int option;
 | 
					 | 
				
			||||||
	while((option = getopt(argc, argv, "c:l:")) != -1)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		switch(option)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			case 'c':
 | 
					 | 
				
			||||||
				read_config(optarg);
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
			case 'l':
 | 
					 | 
				
			||||||
				logfp = fopen(optarg, "a");
 | 
					 | 
				
			||||||
				if(logfp == NULL)
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					print_err_and_exit("Error opening log file");
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if(head_entry == NULL)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		read_config(CONFIG_DEFAULT_PATH); 
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	int previous_seconds = 0;
 | 
						int previous_seconds = 0;
 | 
				
			||||||
	while(1)
 | 
						while(1)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		int idle_seconds = get_idle_seconds(display);
 | 
							int idle_seconds = get_idle_seconds(display);
 | 
				
			||||||
		if(idle_seconds == -1)
 | 
							if(idle_seconds == -1)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
			print_err_and_exit("X11 Screen Saver Extension not supported?");
 | 
								print_err_and_exit("X11 Screen Saver Extension not supported?");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		if(previous_seconds > idle_seconds)
 | 
							if(previous_seconds > idle_seconds)
 | 
				
			||||||
				handle_comeback();
 | 
							{
 | 
				
			||||||
 | 
								run_script(scriptpath, "active", idle_seconds);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
				run_entries(idle_seconds);
 | 
							{
 | 
				
			||||||
			
 | 
								run_script(scriptpath, "idle", idle_seconds);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		previous_seconds = idle_seconds;
 | 
							previous_seconds = idle_seconds;
 | 
				
			||||||
		while(sleep(10)); // Well this sleep approach is suboptimal, do it with events somehow if possible.
 | 
							while(sleep(interval));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +0,0 @@
 | 
				
			|||||||
/bin/touch:/tmp/works:kill:5:oneshot
 | 
					 | 
				
			||||||
							
								
								
									
										2
									
								
								script.sh
									
									
									
									
									
										Εκτελέσιμο αρχείο
									
								
							
							
						
						
									
										2
									
								
								script.sh
									
									
									
									
									
										Εκτελέσιμο αρχείο
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					#!/bin/sh
 | 
				
			||||||
 | 
					echo "State is: $1. Been idling for $2"
 | 
				
			||||||
		Αναφορά σε νέο ζήτημα
	
	Block a user