/*****************************************************************************
 * Luces 1.12 (c)1999 Juan José Álvarez Martinez 
 * Code has been semi-stolen from wmdl of Ben Cohen who stole code from 
 * wmppp, wmload y wmtme (free software works!!!).
 * 
 * This program is distributed under the terms of the GPL license in his
 * version 2 o later
 * **************************************************************************/

#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/kd.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
// #include <paths.h>

#define VERSION "d12"
#define PIDFNAME "ledmpidf"
#define NUM 2 
#define CAPS 4
#define SCROLL 1
#define TIME_DEF 100000 // Default time between execs
/* scroll = xx1;
   num    = x1x;
   caps   = 1xx;*/

static int upp;

float toma_cpu(void);
float toma_mem(void);
void die(void);
void usage(void);
void version(void);
/*****************************************************************************/
void usage(void){
	printf("-c 		CPU Load\n");
	printf("-m 		Free memory\n");
	printf("-s[seconds]	Seconds between updates\n");
	printf("-M[miliseconds] Miliseconds between updates\n");
	printf("-v		Prints version\n");
	printf("-h		This help\n");
	version();
}
/*****************************************************************************/
void version(void){
	char *ver=VERSION;
	
	printf("Leds Monitor Version %s\n",ver);
	die();
}
/*****************************************************************************/
char getleds(void){ // Returns in a char the leds turned on
	char var;
	
	ioctl(0,KDGETLED,&var);
	return var;
}
void setleds(char luces){ // Turn on the leds specified by the luces byte
	ioctl(0,KDSETLED,luces);
}

void restore_leds(void){
	char var;
	
	if (ioctl(0,KDSETLED,&var)){
		perror("KDSETLED");
		fprintf(stderr, "Error reseting ledmode\n");
		exit(1);
	}
}
/******************************************************************************/
char kid(char status){ // Stupid&Nice thing...
	switch(status){
		case 1: //continue
		case 3: 
		case 4: 
		case 5: fprintf(stderr,
				"Error 01: Anormal value for -k\n",status);
				exit(1);
		case 0:
				setleds(NUM);
				upp = 1;
				status = 2;
				break;
		case 2: 
				if (upp) { 
					setleds(NUM|CAPS);
					status = 6;
				}
				else {
					setleds(0);
					status = 0;
				}
				break;
		case 6:
				if (upp) {
					setleds(NUM|CAPS|SCROLL);
					status = 7;
				}
				else {
					setleds(NUM);
					status = 2;
				}
				break;
		case 7:		if (upp) {
					setleds(NUM|CAPS);
					status = 6;
					upp = 0;
				}
				else {
					printf("Error 02: In -k case 7\n");
					exit(1);
				}
				break;
		default:
				printf("Error 03: In -k\n");
				exit(1);
		}
	return status;
}
/************************************************************************/
void carga_de_cpu(void){
	float carga;
	int sw;
	
	carga = toma_cpu();
	
	if ( ( carga >= 0 ) && ( carga <= 0.25 ) ) sw = 0;
	else if ( ( carga > 0.25 ) && ( carga <= 0.50 ) ) sw = 1;
	else if ( ( carga > 0.50 ) && ( carga <= 0.75 ) ) sw = 2;
	else if ( carga > 0.75 ) sw = 3;
	else {
		printf("Error 04: In cpu_load\n");
		exit(1);
	}
	
	switch(sw){
		case 0:
			setleds(0);
			break;
		case 1:
			setleds(NUM);
			break;
		case 2:
			setleds(NUM|CAPS);
			break;
		case 3:
			setleds(NUM|CAPS|SCROLL);
			break;
		default:
			printf("Error 05: In cpu_load");
			exit(1);
	}
	return;
}
			
/******************************************************************************/

float toma_cpu (void){
	FILE *datos;
	char buffer[256];
	char tokens[4] = " \t\n"; // I'll never forget that whitespace before  				          // the \t...

	static long last_user = 0;
	static long last_nice = 0;
	static long last_sys = 0;
	static long last_idle = 0;

	long user, nice, sys, idle, total;
	long d_user, d_nice, d_sys, d_idle;
	float cpu_use;
	user = nice = sys = idle = total = 0;

	if ( (datos = fopen("/proc/stat", "r")) == NULL) {
		return(0);
	}

	while ( fgets(buffer,256,datos) ) {
		if ( strstr(buffer,"cpu") != NULL ){
			strtok(buffer,tokens);
			user = atol(strtok(NULL,tokens));
			nice = atol(strtok(NULL,tokens));
              	        sys =  atol(strtok(NULL,tokens));
			idle = atol(strtok(NULL,tokens));
		}
	}

	fclose(datos);

	d_user = user - last_user;
	d_nice = nice - last_nice;
	d_sys  = sys  - last_sys;
	d_idle = idle - last_idle;

	last_user = user;
	last_nice = nice;
	last_sys  = sys;
	last_idle = idle;

	total = d_user + d_nice + d_sys + d_idle;

	if (total < 1)
		total = 1.0;

	cpu_use = 1.0 - ( (float) d_idle / (float) total );

	return(cpu_use);
}				
/******************************************************************************/

float toma_mem(void){
	FILE *datosmem;
	char buffer[256];
	char tokens[4] = " \t\n";

	long total,libre,buffers,cached,total_libre;
	float porcentaje;

	if ( (datosmem = fopen("/proc/meminfo","r") ) == NULL) {
		perror("Error 06:Opening /proc/meminfo\n");	
		return 0;
	}

	fgets(buffer,256,datosmem);
	fgets(buffer,256,datosmem);

	strtok(buffer,tokens);

	total = atol(strtok(NULL,tokens) );
	strtok(NULL,tokens); /* used */
        libre = atol(strtok(NULL,tokens) );
        strtok(NULL,tokens); /* shared */
        buffers = atol( strtok(NULL,tokens) );
        cached  = atol( strtok(NULL,tokens) );
	
	fclose(datosmem); // I will never forget this fclose...
	
	total_libre = libre + cached + buffers;
	porcentaje = (float)total_libre / (float) total;
	
	return porcentaje;
}
									
void carga_de_mem(void){

	float carga;
	int sw;

	carga = toma_mem();

	if      ( ( carga >= 0.00 ) && ( carga <= 0.25 ) ) sw = 0;
	else if ( ( carga > 0.25 ) && ( carga <= 0.50 ) ) sw = 1;
	else if ( ( carga > 0.50 ) && ( carga <= 0.75 ) ) sw = 2;
	else if   ( carga > 0.75 ) sw = 3;
	else {
		printf("Error 07:In mem_load\n");
		printf("load: %f sw:%d\n",carga,sw);		
		exit(1);
	}

	switch(sw){
		case 0:
			setleds(0);
			break;
		case 1:
			setleds(NUM);
			break;
		case 2:
			setleds(NUM|CAPS);
			break;
		case 3:
			setleds(NUM|CAPS|SCROLL);
			break;
		default:
			printf("Error 08: In mem_load\n");
			printf("Load: %f sw:%d",carga,sw);
			exit(1);
	}
	return;
}

/*****************************************************************************/
/* If we do a Ctrl-C or a civilizated kill ... */
void die(){
	char var;
	
	restore_leds();
	exit(0);
}

/*****************************************************************************/
void kill_other(){
	FILE *pidfile;
	char *pidstring,error;
	pid_t otherpid;
	char *errorf;

	pidfile = fopen("/tmp/ledmpidf", "r"); /**/ 
	if (pidfile == NULL){
		fprintf(stderr, "Error opening pidfile, sure is an ledm running?\n");
		die();
	}
	else{
		pidstring = (char *)malloc( (sizeof(char)) * 10);
		errorf = fgets(pidstring, 10,pidfile);
		if (errorf == NULL){
			fprintf(stderr, "Error reading pidfile\n");
			die();
		}
		else{
			otherpid = atoi(pidstring);
			kill(otherpid, SIGTERM);
			die();
		}
	}
}

/*****************************************************************************/
void create_pidf(){
	pid_t mipid;
	FILE *pidfile;
	
	mipid = getpid();
	pidfile = fopen("/tmp/ledmpidf","r"); /**/
	if (pidfile != NULL){
		fprintf(stderr, "File pids %s already exist, delete it manually");
		fprintf(stderr, " or kill the previous process with ledm -k /tmp/ledmpidf\n");
		fclose(pidfile);
		die();
	}
	else{		
		pidfile = fopen("/tmp/ledmpidf","w"); /**/
		if (pidfile == NULL){
			perror("Error opening pidfile\n");
			die();
		}
		fprintf(pidfile, "%ld\n",(long)mipid);
		fclose(pidfile);
	}
}
	
/*****************************************************************************/
void delete_pidf(){
	int err;
	char *myname;
	
	err = unlink("/tmp/ledmpidf"); /**/
	if (err){
		fprintf(stderr, "Error in delete pidf\n");
		die();
	}
	else if (!err)
		die();				
}

/********************************************************************/	
int main(int argc, char **argv){

//struct led *lp;
pid_t mypid;
char status,car;
char *tpo;
long tponum;
int opck,opcm,opccpu,opct,tiempo,tiemposec,code; //tiempo in microseconds
int inicio=1;


opck=opcm=opccpu=opct=0;
tiempo = TIME_DEF;
tiemposec = 0;


signal(SIGINT, delete_pidf); // Turn off leds at exit
signal(SIGTERM, delete_pidf);
if (inicio){ 
	ioctl(0,KDSETLED,0); 
	inicio = 0;
}
/* Parameters:
 * -d	      : Close the previously opened program
 * -k         : Stupid && Nice 
 * -m         : Free memory
 * -c	      : CPU load
 * -snumero   : Time between in seconds
 * -Mnumero   : Time between in microseconds */

while (argc > 1){
	if (!strcmp("-h", argv[1]))
		usage();
	if (!strcmp("-v", argv[1]))
		version();
	if (!strcmp("-d", argv[1]))
		kill_other();
	if (!strcmp("-k", argv[1]))
		opck = 1;
	else if (!strcmp("-r", argv[1])){
		setleds(0);
		exit(0);
	}
	else if (!strcmp("-m", argv[1]))
		opcm = 1;	
	else if (!strcmp("-c",argv[1]))
		opccpu = 1;
	else if ( (strlen(argv[1]) >=2) && (  (car = argv[1][1]) == 's') || (car == 'M') ) {		
		car = argv[1][1];
		if ( car == 's' ){
			tpo = (char *)malloc( strlen(argv[1]-2 ) * sizeof(char));
			tpo = strtok(argv[1],"-s");
			if (tpo == NULL){
				printf("You must specify a time");
				printf("(in seconds) after the -s\n");
				exit(1);
			}
			else{
				tponum = atol(tpo);
				if (tponum == 0){
					fprintf(stderr,"Invalid time value\n");
					exit(1);
				}
				else
					tiemposec = tponum;
			}
		}
		else if ( car == 'M' ){
			tpo = (char *)malloc( strlen(argv[1]-2 ) * sizeof(char));
			tpo = strtok(argv[1],"-M");
			if (tpo == NULL){
				printf("You must specify a time ");
				printf("(in miliseconds) after the -M\n");
				exit(1);
			}
			else{
				tponum = atol(tpo);
				if (tponum == 0){
					fprintf(stderr,"Invalid time\n");
					exit(1);
				}
				else
					tiempo = tponum*1000;
			}
		}
	}
	else 
		break;
argc--;
argv++;
}

if ( !(opck + opccpu + opcm) ){
	fprintf(stderr, "Error in arguments: You must select one in");
	fprintf(stderr, "  -kcm\n");
	usage();
}
else if ( (opck + opccpu + opcm) > 1 ){
	fprintf(stderr, "Error in arguments: You must select ONLY one in");
	fprintf(stderr, " -kcm\n");
	usage();
}
/* Aquí creamos el archivo pidf y comprobamos que efectivamente somos la
 * máquina y lo hemos hecho bien create_pidf, delete_pidf, kill_other*/

create_pidf();

status = getleds();
upp = 1;
while(1) {
	
	if (opck)
		status = kid(status);  
	else if (opcm)
		carga_de_mem(); 
	else if (opccpu)
		carga_de_cpu(); 	
	else 	{
		fprintf(stderr,"Error 09: In args parsing\n");
		exit(1);
	}
	if (tiemposec)
		sleep(tiemposec);
	else 
		usleep(tiempo);
}
}

