|
// |
|
// main.c |
|
// appmonitor |
|
// |
|
// Created by Thomas Darimont on 14/10/14. |
|
// Copyright (c) 2014 training. All rights reserved. |
|
// |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <stdbool.h> |
|
#include <pthread.h> |
|
#include <sys/types.h> |
|
#include <unistd.h> |
|
#include <string.h> |
|
#include <time.h> |
|
#include <stdarg.h> |
|
#include <spawn.h> |
|
|
|
typedef enum { |
|
LOG_DEBUG = 0, |
|
LOG_WARNING, |
|
LOG_INFO, |
|
LOG_ERROR |
|
} |
|
log_level_t; |
|
|
|
typedef struct { |
|
const char* app_name; |
|
const char* app_start_cmd_line; |
|
const char* app_kill_cmd_line; |
|
const char* app_control_file_path; |
|
const int check_interval_seconds; |
|
} app_monitor_config; |
|
|
|
const int ONE_SECOND = 1; |
|
|
|
volatile bool stopMonitoring = false; |
|
volatile bool spawnApp = false; |
|
|
|
#ifndef HAVE_STRDUP |
|
char *strdup(char const* in){ if (!in) return NULL; char *out; |
|
asprintf(&out, "%s", in); return out; |
|
} |
|
#endif |
|
|
|
static log_level_t __g_loglevel = LOG_DEBUG; |
|
|
|
void log_msg(log_level_t level, const char *format, ... ){ |
|
|
|
char buffer[0xFF] = {0}, |
|
timestamp[0xFF] = {0}, |
|
*slevel; |
|
va_list ap; |
|
time_t rawtime; |
|
struct tm * timeinfo; |
|
|
|
if( level >= __g_loglevel ){ |
|
va_start( ap, format ); |
|
vsnprintf( buffer, 0xFF, format, ap ); |
|
va_end(ap); |
|
|
|
time( &rawtime ); |
|
timeinfo = localtime( &rawtime ); |
|
|
|
strftime( timestamp, 0xFF, "%d/%m/%Y %X", timeinfo ); |
|
|
|
switch( level ){ |
|
case LOG_DEBUG : slevel = "DEBUG"; break; |
|
case LOG_WARNING : slevel = "WARNING"; break; |
|
case LOG_INFO : slevel = "INFO"; break; |
|
case LOG_ERROR : slevel = "ERROR"; break; |
|
} |
|
|
|
printf("[%s] [%s] %s", timestamp, slevel, buffer ); |
|
} |
|
} |
|
|
|
bool is_app_running(app_monitor_config *config){ |
|
|
|
char *app_running_cmd = strdup("ps -A -o command | grep "); |
|
asprintf(&app_running_cmd, "%s ^%s$", app_running_cmd, config->app_start_cmd_line); |
|
asprintf(&app_running_cmd, "%s %s", app_running_cmd, "| grep -v grep"); |
|
|
|
log_msg(LOG_INFO, "checking app state via: '%s'\n", app_running_cmd); |
|
|
|
int exitCode = system(app_running_cmd); |
|
|
|
return exitCode == 0; |
|
} |
|
|
|
bool should_app_be_running(app_monitor_config *config){ |
|
|
|
FILE *controlFile = fopen(config->app_control_file_path, "r"); |
|
if (!controlFile) |
|
{ |
|
return false; |
|
} |
|
|
|
char line[4]; |
|
|
|
if(!feof(controlFile)){ |
|
fgets(line, sizeof (line), controlFile); |
|
} |
|
|
|
fclose(controlFile); |
|
|
|
if(strcmp(line,"OFF") == 0){ |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void launch_app_process(const char* command){ |
|
|
|
pid_t pid; |
|
extern char **environ; |
|
|
|
char *argv[] = {"sh", "-c", (char*)command, NULL}; |
|
int status = posix_spawn(&pid, "/bin/sh" , NULL, NULL, argv, environ); |
|
|
|
if (status == 0) { |
|
log_msg(LOG_INFO, "Child pid: %i\n", pid); |
|
if (waitpid(pid, &status, 0) != -1) { |
|
log_msg(LOG_INFO, "Child exited with status %i\n", status); |
|
} else { |
|
log_msg(LOG_INFO, "waitpid"); |
|
} |
|
} else { |
|
log_msg(LOG_INFO, "posix_spawn: %s\n", strerror(status)); |
|
} |
|
} |
|
|
|
void *app_launcher_loop(void* arg){ |
|
|
|
app_monitor_config *config = (app_monitor_config*)arg; |
|
|
|
while(!stopMonitoring){ |
|
|
|
if(spawnApp){ |
|
|
|
log_msg(LOG_INFO, "starting app: '%s' via: '%s'\n", config->app_name, config->app_start_cmd_line); |
|
|
|
launch_app_process(config->app_start_cmd_line); |
|
|
|
spawnApp=false; |
|
|
|
} else { |
|
sleep(ONE_SECOND); |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
void start_and_run_app_launcher_loop(app_monitor_config *config ){ |
|
|
|
pthread_t launcherThread; |
|
|
|
int status = pthread_create(&launcherThread, NULL, &app_launcher_loop, config); |
|
|
|
if (status != 0) { |
|
log_msg(LOG_INFO, "problem starting applauncher. exiting...\n"); |
|
} else { |
|
log_msg(LOG_INFO, "applauncher started.\n"); |
|
} |
|
} |
|
|
|
void kill_app(app_monitor_config *config){ |
|
|
|
log_msg(LOG_INFO, "killing app: %s via: '%s'\n", config->app_name, config->app_kill_cmd_line); |
|
system(config->app_kill_cmd_line); |
|
} |
|
|
|
void *app_monitoring_loop(void* arg) { |
|
|
|
app_monitor_config *config = (app_monitor_config*)arg; |
|
|
|
log_msg(LOG_INFO, "monitoring application: %s\n", config->app_name); |
|
|
|
while(!stopMonitoring){ |
|
|
|
bool appIsRunning = is_app_running(config); |
|
bool appShouldBeRunning = should_app_be_running(config); |
|
|
|
log_msg(LOG_INFO, "monitor status appIsRunning: '%d' appShouldBeRunning: '%d'\n", appIsRunning, appShouldBeRunning); |
|
|
|
if(appShouldBeRunning && !appIsRunning){ |
|
spawnApp = !appIsRunning; |
|
}else if(!appShouldBeRunning && appIsRunning){ |
|
kill_app(config); |
|
} |
|
|
|
sleep(config->check_interval_seconds); |
|
} |
|
|
|
log_msg(LOG_INFO, "stop monitoring.\n"); |
|
|
|
return NULL; |
|
} |
|
|
|
void waitForEnterKey(){ |
|
|
|
char c = -1; |
|
|
|
while(c != '\n'){ |
|
c = getchar(); |
|
} |
|
} |
|
|
|
void start_and_run_app_monitoring(app_monitor_config *config){ |
|
|
|
pthread_t monitorThread; |
|
|
|
int status = pthread_create(&monitorThread, NULL, &app_monitoring_loop, config); |
|
|
|
if (status != 0) { |
|
log_msg(LOG_INFO, "problem starting appmonitor. exiting...\n"); |
|
} else { |
|
log_msg(LOG_INFO, "appmonitor started.\n"); |
|
|
|
waitForEnterKey(); |
|
|
|
stopMonitoring = true; |
|
} |
|
} |
|
|
|
app_monitor_config process_arguments(int argc, const char * argv[]){ |
|
|
|
app_monitor_config config = { |
|
.app_name = argv[1], |
|
.app_start_cmd_line = argv[2], |
|
.app_kill_cmd_line = argv[3], |
|
.app_control_file_path = argv[4], |
|
.check_interval_seconds = 2 * ONE_SECOND |
|
}; |
|
|
|
return config; |
|
} |
|
|
|
int main(int argc, const char * argv[]) { |
|
|
|
app_monitor_config config = process_arguments(argc,argv); |
|
|
|
start_and_run_app_launcher_loop(&config); |
|
start_and_run_app_monitoring(&config); |
|
|
|
return 0; |
|
} |