Code:
#include<dirent.h>
#include<errno.h>
#include<sys/stat.h>
#include<signal.h>
#include<fcntl.h>
#include<setjmp.h>
#include<stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/wait.h>
#include "memwatch.h"
#include "logger.h"
#define MAX_PROC_COUNT 128
#define MAX_LINE_SIZE 1024
typedef struct
{
pid_t child_pid; //child's own PID
pid_t process_pid; //the PID of the montiroed process
char process_name[MAX_LINE_SIZE]; //the name of the monitored process
int p2c_pipe[2]; //pipe for parent to child communication.
int c2p_pipe[2]; //pipe for child to parent communication.
int proc_killed; //number of processes killed
int alive_time; //alive tim for the given process
int isBusy; //indicates whether the child is busy or not
int firstTime; //used to indicate whether the child is monitoring for the first time
} processInfo;
processInfo processes[MAX_PROC_COUNT];
int childCount = 0; /*Number of child process (i.e., number of valid entries in the array above)*/
/*Some helper variables*/
FILE * grepFile;
char grepLine [MAX_LINE_SIZE];
char logfileMessage[MAX_LINE_SIZE];
char bash_command[MAX_LINE_SIZE];
char * config_file_name;
int reloaded = 0; //flag to indicate whether SIGHUP has been received
sigjmp_buf env;
//sigjmp_buf env1; //needed to handle "-1" message from parent
int indexChild; //used to know the index of the child process
fd_set read_from_child; //the read file descriptor set for parent
void cleanUpProcnanny();
void readConfigFile(char* configFileName);
void runChildProcess(int childCount, int monitoredPID, int alive_time);
void waitForChildren();
void cleanExit(int status);
void SIGINT_handler(int sig_num);
void SIGHUP_handler(int sig_num);
void reload_config_file(char* configFileName);
void monitor5Seconds();
void monitorProcess(int child_proc_count);
int number_of_process_killed=0; //stores the total number fo proces killed.
pid_t own_id; /*The parent process' own pid*/
int number_of_free_children = 0; //used to keep info about the number of free children
//int monitoredPIDs[400]; //buffer that holds motiored PIDs as specified in the last read configuration
//int number_of_monitored_pids = 0; //number of monitored PIDs
FILE* config_file; //variables
char configLine[MAX_LINE_SIZE]; //needed
char proc_name[100]; //while
char alive_time[10]; //reading the
int alive_time_int = 0; //config file
int main(int argc, char * argv[])
{
int ret2, ret3;
if(argc!=2)
{
printf("usage: ./procnanny <path to configuration file> \n");
exit(0);
}
signal(SIGHUP, SIGHUP_handler);
signal(SIGINT, SIGINT_handler);
/*Start the logging process*/
initLogger();
/*Clean up previous procnanny processes*/
cleanUpProcnanny();
/*Read the config file*/
readConfigFile(argv[1]);
config_file_name = argv[1];
sigsetjmp(env, 1);
if(reloaded == 1)
{
//only parent reloads the configuration file
if(own_id == getpid())
{
reload_config_file(config_file_name);
reloaded = 0;
}
}
if(own_id == getpid())
{
while(1)
{
sleep(5);
monitor5Seconds();
//also listen for child's messages
int maxdesc = getdtablesize();
struct timeval tv;
tv.tv_sec = 0;
ret2 = select(maxdesc, &read_from_child, NULL, NULL, &tv);
//printf("%d FDs ready \n ", ret);
if (ret2 < 0) //error
{
printf("Got a ha ha : \n");
perror("select");
continue;
}
else
{
//got a message from child
int i = 0;
char buf[30];
for (i = 0; i < childCount; i++)
{
if (FD_ISSET(processes[i].c2p_pipe[0], &read_from_child)) {
memset(buf, 0, MAX_LINE_SIZE);
ret3 = read(processes[i].c2p_pipe[0], &buf, sizeof(buf));
if (ret3 <= 0){printf("return error \n");}
else
{
printf("Child %d Sent %s\n",i,buf);
processes[i].isBusy = 0; //the child becomes free
number_of_free_children++; //increment the number of free children
processes[i].process_pid = 0; //clear the monitored PID of that process
}//end of else
}//end of if (FD_ISSET(processes[childIndex].p2c_pipe[0], &read_from))
}//end of for
}//end of else
}
}
return 0;
}//end of main
void cleanUpProcnanny(){
pid_t process_pid; /*The pid of a previous procnanny processes*/
grepFile = popen("ps -eo pid,comm | grep ./procnanny","r");
if (grepFile == NULL){ //popen error
perror("popen");
printLog(LOG_ERROR, "Could not check for previous procnanny processes. Pipe open failed!\n");
}
else //success in popen
{
own_id = getpid();
while ( fgets(grepLine, MAX_LINE_SIZE, grepFile) )
{
sscanf (grepLine,"%d", &process_pid);
//To make sure it does not kill itself
if (own_id!=process_pid)
{
//kill the process
sprintf( bash_command, "kill %d", (int)process_pid);
if ( system(bash_command) < 0) //kill failed
{
perror("system() error");
sprintf(logfileMessage, "failed to kill previous procnanny process with PID %d\n",process_pid);
printLog(LOG_ERROR, logfileMessage);
}
else //kill succeeded
{
sprintf(logfileMessage, "previous procnanny process PID %d killed\n",process_pid);
printLog(LOG_INFO, logfileMessage);
}
}//end of if
}//end of while
}//end of else and end of checking whether procnanny exists
pclose(grepFile); // Done processing the output of ps -eo pid,comm | grep
}
void readConfigFile(char* configFileName){
FD_ZERO(&read_from_child);
int procExists; /*Checks whether there has been an instance of a specific process running*/
pid_t child_pid;
pid_t proc_pid;
config_file = fopen( configFileName, "r" );
if ( config_file == NULL ) // fopen failed
{
printf( "Could not open configurationfile\n" );
cleanExit(0);
}
//get the name of the processes and their alive time as mentioned in the conf file
while ( fgets(configLine, MAX_LINE_SIZE, config_file) )
{
//configLine[strlen(configLine)-1]='\0'; /*remove the \n at the end*/
//extract the process name and alive_time from the line
sscanf(configLine,"%s %d",proc_name, &alive_time_int);
printf ("Alive time = %d \n", alive_time_int);
//convert the alive_time array to alive_time_int
//alive_time_int = atoi(alive_time);
/*Prepare and run the grep command for the current line of config file*/
sprintf(bash_command,"ps -eo pid,comm | grep %s", proc_name);
grepFile = popen(bash_command , "r");
if(grepFile==NULL)
{
perror("popen");
sprintf(logfileMessage, "Failed to check for processes running as %s", proc_name);
printLog(LOG_ERROR, logfileMessage);
cleanExit(0);
}
procExists=0;
//initialize array to store the currently monitored PIDs
//monitoredPIDs = (int *)malloc(sizeof(int)*200);
//int index_in_monitored_PIDs = 0; //used to store PIDs in the buffer monitoredPIDs
//iterating through the output of ps aux|grep x(the line read from config file)
while (fgets(grepLine, MAX_LINE_SIZE, grepFile))
{
procExists=1; /*There has been at least one process with this name*/
sscanf (grepLine,"%d", &proc_pid);
//monitoredPIDs[index_in_monitored_PIDs] = proc_pid;
//index_in_monitored_PIDs++;
processes[childCount].process_pid = proc_pid;
strcpy(processes[childCount].process_name, proc_name);
processes[childCount].alive_time = alive_time_int;
//create the pipes for communication
pipe(processes[childCount].p2c_pipe);
pipe(processes[childCount].c2p_pipe);
//create a child
child_pid=fork();
if ((child_pid) < 0) {
perror("fork");
cleanExit(0);
}
else
{
processes[childCount].child_pid = child_pid;
processes[childCount].isBusy = 1;
processes[childCount].firstTime = 1;
//if parent, close the unused end of the pipes
if (child_pid != 0)
{
close(processes[childCount].p2c_pipe[0]);
close(processes[childCount].c2p_pipe[1]);
//add the read end of the pipe to fd_set
FD_SET (processes[childCount].c2p_pipe[0], &read_from_child);
}
if (child_pid == 0) {
//child creation successful
//close unused end of the pipes
close(processes[childCount].p2c_pipe[1]);
close(processes[childCount].c2p_pipe[0]);
//write(processes[child_proc_count].p2c_pipe[1], proc_pid , 10);
runChildProcess(childCount, proc_pid, alive_time_int);
//cleanExit(procKilled); //return whether the process has been killed
}//end of child
}
childCount++;
}//end of while
if(procExists == 0)
{
sprintf(logfileMessage, "No Process with name '%s'.\n",proc_name);
printLog(LOG_INFO, logfileMessage);
}
}//end of outer while
fclose(config_file);
}
void reload_config_file(char* configFileName)
{
printf("Reread successful \n");
//int procKilled; /*Return value from a child process, giving the number of processes it has killed, or 0 if nothing has been killed*/
int procExists; /*Checks whether there has been an instance of a specific process running*/
pid_t child_pid;
pid_t proc_pid;
//open the configuration file again
config_file = fopen( configFileName, "r" );
if ( config_file == NULL ) // fopen failed
{
printf( "Could not open configurationfile\n" );
cleanExit(0);
}
//go through the config file
//get the name of the processes and their alive time as mentioned in the conf file
while ( fgets(configLine, MAX_LINE_SIZE, config_file) )
{
//configLine[strlen(configLine)-1]='\0'; /*remove the \n at the end*/
//extract the process name and alive_time from the line
sscanf(configLine,"%s %d",proc_name, &alive_time_int);
printf ("Alive time = %d \n", alive_time_int);
/*Prepare and run the grep command for the current line of config file*/
sprintf(bash_command,"ps -eo pid,comm | grep %s", proc_name);
grepFile = popen(bash_command , "r");
if(grepFile==NULL)
{
perror("popen");
sprintf(logfileMessage, "Failed to check for processes running as %s", proc_name);
printLog(LOG_ERROR, logfileMessage);
cleanExit(0);
}
//clear the buffer array that stored the previously monitored PIDs
//free(monitoredPIDs);
//reinitialize array to store the currently monitored PIDs
//monitoredPIDs = (int *)malloc(sizeof(int)*200);
//int index_in_monitored_PIDs = 0; //used to store PIDs in the new buffer monitoredPIDs
//iterating through the output of ps aux|grep x(the line read from config file)
while (fgets(grepLine, MAX_LINE_SIZE, grepFile))
{
procExists=1; /*There has been at least one process with this name*/
sscanf (grepLine,"%d", &proc_pid);
//check if there is any free children. If yes, assign the PID to them
//int j = 0;
int k = 0;
//int assigned = 0; //flag to know that process has been assigned so that go to the next iteration and
//assign another PID
printf ("%d \n", number_of_free_children);
if(number_of_free_children > 0)
{
//find the child who is free
for (k = 0; k < childCount; k++)
{
if (processes[k].isBusy == 0)
{
//free child
//assign the PID and the process name to the child
processes[k].process_pid = proc_pid;
strcpy(processes[k].process_name, proc_name);
processes[k].alive_time = alive_time_int;
processes[k].isBusy = 1;
//write the process ID and the time to monitor in the pipe
printf("Writing to child again..\n");
write(processes[k].p2c_pipe[1], &proc_pid, sizeof(proc_pid));
write(processes[k].p2c_pipe[1], &alive_time_int, sizeof(alive_time_int));
write(processes[k].p2c_pipe[1], proc_name, strlen(proc_name));
//add this PID to the monitored PID buffer
//monitoredPIDs[index_in_monitored_PIDs] = proc_pid;
//index_in_monitored_PIDs++;
//we have lost one free children
number_of_free_children--;
break;
}//end of if
}//end of for
}
else
{
//create a new child process
processes[childCount].process_pid = proc_pid;
strcpy(processes[childCount].process_name, proc_name);
processes[childCount].alive_time = alive_time_int;
//create the pipes for communication
pipe(processes[childCount].p2c_pipe);
pipe(processes[childCount].c2p_pipe);
//create a child
child_pid=fork();
if ((child_pid) < 0) {
perror("fork");
cleanExit(0);
}
else
{
processes[childCount].child_pid = child_pid;
processes[childCount].isBusy = 1;
processes[childCount].firstTime = 1;
//if parent, close the unused end of the pipes
if (child_pid != 0)
{
close(processes[childCount].p2c_pipe[0]);
close(processes[childCount].c2p_pipe[1]);
//add the read end of the pipe to fd_set
FD_SET (processes[childCount].c2p_pipe[0], &read_from_child);
}
if (child_pid == 0) {
//child creation successful
//close unused end of the pipes
close(processes[childCount].p2c_pipe[1]);
close(processes[childCount].c2p_pipe[0]);
//write(processes[child_proc_count].p2c_pipe[1], proc_pid , 10);
runChildProcess(childCount, proc_pid, alive_time_int);
}//end of child
}//end of else
childCount++;
}//end of else (creating a new child)
}//end of while
if(procExists == 0)
{
sprintf(logfileMessage, "No Process with name '%s'.\n",proc_name);
printLog(LOG_INFO, logfileMessage);
}
}//end of outer while
fclose(config_file);
}
void runChildProcess(int childIndex, int monitoredPID, int alive_time){
//only the children execute the following code
if(getpid() != own_id)
{
printf("In Child %d\n", childIndex);
indexChild = childIndex;
fd_set read_from;
FD_ZERO(&read_from);
//set the parent to child read end of file
FD_SET (processes[childIndex].p2c_pipe[0], &read_from);
int ret = 0;
char buf[MAX_LINE_SIZE];
char buf2[MAX_LINE_SIZE];
int maxdesc = getdtablesize();
pid_t pid_given;
if (processes[childIndex].firstTime ==1)
{
monitorProcess(childIndex);
processes[indexChild].firstTime = 0;
}
//wait in infinite while loop for instruction from parent
//if instuction is a PID, monitor that process
//if the instruction is PID = -1, exit
while(1)
{
//sigsetjmp(env1, 1);
ret = select(maxdesc, &read_from, NULL, NULL, NULL);
if (ret < 0)
{
printf("Got a ha ha : \n");
perror("select");
continue;
}
else
{
//got a message from parent
if (FD_ISSET(processes[childIndex].p2c_pipe[0], &read_from)) {
memset(buf, 0, MAX_LINE_SIZE);
ret = read(processes[childIndex].p2c_pipe[0], &pid_given, sizeof(pid_given));
if (ret <= 0)continue;
else
{
//check whether a process ID is given or -1
printf("Parent sent: %s \n", buf);
//convert the message sent by parent to a pid_t type
// = atoi(buf);
if (pid_given == -1)
{
//it's time to quit
printf("Parent sent -1 \n");
exit(processes[indexChild].proc_killed);
}
else
//monitor that process
{
//get the alive time from parent
printf("Parent said to continue \n");
int new_alive_time;
memset(buf2, 0, MAX_LINE_SIZE);
ret = read(processes[childIndex].p2c_pipe[0], &new_alive_time, sizeof(new_alive_time));
ret = read(processes[childIndex].p2c_pipe[0], &buf2, sizeof(buf2));
// = atoi(buf1);
printf("New alive time %d \n", new_alive_time);
printf("New process name %s \n", buf2);
processes[indexChild].process_pid = pid_given;
processes[indexChild].alive_time = new_alive_time;
monitorProcess(childIndex);
}
}//end of else
}//end of if (FD_ISSET(processes[childIndex].p2c_pipe[0], &read_from))
}//end of else
}//end of main while loop in child
}//end of if (own_id != getpid())
}
void monitorProcess(int child_proc_count)
{
//firsttime monitor the assigned process
pid_t proc_id;
sprintf(logfileMessage, "Initializing monitoring of process '%s' PID[%d]\n",
processes[child_proc_count].process_name,processes[child_proc_count].process_pid);
printLog(LOG_INFO, logfileMessage);
//go to sleep for the time specified in logfile
sleep(processes[child_proc_count].alive_time);
//wake up and check for still alive processes
sprintf(bash_command, "ps -eo pid,comm | grep %s", processes[child_proc_count].process_name);
FILE * grepFile = popen(bash_command , "r");
while(fgets(grepLine, MAX_LINE_SIZE, grepFile))
{
sscanf (grepLine,"%d", &proc_id);
if(processes[child_proc_count].process_pid == proc_id) //process with the pid still exists
{
sprintf (bash_command, "kill %d", processes[child_proc_count].process_pid);
if(system(bash_command) < 0)
{
perror("system");
sprintf(logfileMessage, "Failed to kill process with PID %d (%s). \n",
processes[child_proc_count].process_pid,
processes[child_proc_count].process_name);
printLog(LOG_ERROR, logfileMessage);
}
else //killing successful
{
processes[child_proc_count].proc_killed++;
//printf("Processes killed %d\n",processes[child_proc_count].proc_killed);
number_of_process_killed++;
char buf1[20]="Killed";
printf("Child %d writing to parent ..\n",child_proc_count);
write(processes[child_proc_count].c2p_pipe[1], buf1, strlen(buf1));
}
}//end of if
}//end of while
}
void waitForChildren(){
//only parent is allowed to execute the functionality
if (getpid() == own_id)
{
pid_t pid4;
int i = 0;
int status;
for(i = 0; i < childCount; i++)
{
pid4 = waitpid(processes[i].child_pid,&status,0);
//pid4 = wait(status);
status = WEXITSTATUS(status);
number_of_process_killed += status;
printf("Child with PID %ld exited with status %d.\n", (long)pid4, status);
}
sprintf(logfileMessage, "Exiting. %d process(es) killed.\n",number_of_process_killed);
printLog(LOG_INFO, logfileMessage);
}
}
void monitor5Seconds()
{
printf("Parent woke up \n");
}
//signal handler for sighup
void SIGHUP_handler(int sig_num) {
//only parent executes the following
//if (getpid() == own_id)
//{
printf("Signal caught \n");
reloaded = 1;
siglongjmp(env,1);
//}
}
//SIGINT handler
void SIGINT_handler(int sig_num) {
//free the buffer containing Monitored PIDs
//free(monitoredPIDs);
//parent send the quit signal to everyone
if(getpid() == own_id)
{
printf("Parent Quitting ..\n");
/*int i = 0;
char buf[4] = "-1";
//send "-1" to all the existing childnanys to ask them to quit
for (i = 0; i < childCount; i++)
{
printf("Writing to child..");
write(processes[i].p2c_pipe[1], buf, strlen(buf));
}*/
/*Wait for the children to return*/
waitForChildren();
cleanExit(0);
}
else
{
//child quits with the number of processes killed
printf("Child Quitting ..\n");
//siglongjmp(env,1);
exit(processes[indexChild].proc_killed);
}
}
void cleanExit(int status)
{
cleanupLogger();
exit(status);
}