#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <rc/start_stop.h>
#include <rc/time.h>
static enum rc_state_t rc_state = UNINITIALIZED;
static void shutdown_signal_handler(int signum);
static void segfault_handler(int signum, siginfo_t *info, void *context);
rc_state_t rc_get_state(void)
{
return rc_state;
}
void rc_set_state(rc_state_t new_state)
{
rc_state = new_state;
return;
}
int rc_print_state(void)
{
switch(rc_state){
case UNINITIALIZED:
printf("UNINITIALIZED");
break;
case PAUSED:
printf("PAUSED");
break;
case RUNNING:
printf("RUNNING");
break;
case EXITING:
printf("EXITING");
break;
default:
fprintf(stderr,"ERROR: invalid state\n");
return -1;
}
return 0;
}
int rc_make_pid_file(void)
{
FILE *fd;
DIR* dir;
pid_t current_pid;
if(access(RC_PID_FILE, F_OK ) == 0){
fprintf(stderr,"ERROR: in rc_make_pid_file, file already exists, a new one was not written\n");
fprintf(stderr,"You have either called this function twice, or you need to \n");
fprintf(stderr,"call rc_kill_existing_process BEFORE rc_make_pid_file\n");
return 1;
}
errno=0;
dir = opendir(RC_PID_DIR);
if(dir) closedir(dir); else{
perror("ERROR in rc_make_pid_file trying to open /run/shm/");
return -1;
}
fd = fopen(RC_PID_FILE, "w");
if(fd == NULL){
perror("ERROR in rc_make_pid_file");
return -1;
}
current_pid = getpid();
fprintf(fd,"%d",(int)current_pid);
fflush(fd);
fclose(fd);
return 0;
}
int rc_kill_existing_process(float timeout_s)
{
FILE* fd;
int old_pid, i, ret, num_checks;
if(timeout_s<0.1f){
fprintf(stderr, "ERROR in rc_kill_existing_process, timeout_s must be >= 0.1f\n");
return -4;
}
if(access(RC_PID_FILE, F_OK)){
return 0;
}
if(access(RC_PID_FILE, W_OK)){
fprintf(stderr, "ERROR, in rc_kill_existing_process, don't have write access \n");
fprintf(stderr, "to PID file. Existing process is probably running as root.\n");
fprintf(stderr, "Try running 'sudo rc_kill'\n");
return -3;
}
fd = fopen(RC_PID_FILE, "r");
if(fd==NULL){
fprintf(stderr, "WARNING, in rc_kill_existing_process, PID file exists but is not\n");
fprintf(stderr, "readable. Attempting to delete it.\n");
remove(RC_PID_FILE);
return -2;
}
ret=fscanf(fd,"%d", &old_pid);
fclose(fd);
if(ret!=1){
fprintf(stderr, "WARNING, in rc_kill_existing_process, PID file exists but contains\n");
fprintf(stderr, "invalid contents. Attempting to delete it.\n");
remove(RC_PID_FILE);
return -2;
}
if(old_pid == 0){
fprintf(stderr, "WARNING, in rc_kill_existing_process, PID file exists but contains\n");
fprintf(stderr, "invalid contents. Attempting to delete it.\n");
remove(RC_PID_FILE);
return -2;
}
if(old_pid == (int)getpid()) return 0;
if(getpgid(old_pid) < 0){
remove(RC_PID_FILE);
return 0;
}
if(kill((pid_t)old_pid, SIGINT)==-1){
if(errno==EPERM){
fprintf(stderr, "ERROR in rc_kill_existing_process, insufficient permissions to stop\n");
fprintf(stderr, "en existing process which is probably running as root.\n");
fprintf(stderr, "Try running 'sudo rc_kill' to stop it.\n\n");
return -3;
}
remove(RC_PID_FILE);
return -2;
}
num_checks=timeout_s/0.1f;
for(i=0; i<=num_checks; i++){
if(getpgid(old_pid)==-1){
remove(RC_PID_FILE);
return 1;
}
else rc_usleep(100000);
}
kill((pid_t)old_pid, SIGKILL);
for(i=0; i<=num_checks; i++){
if(getpgid(old_pid)==-1){
remove(RC_PID_FILE);
return 1;
}
else rc_usleep(100000);
}
remove(RC_PID_FILE);
fprintf(stderr, "WARNING in rc_kill_existing_process, process failed to\n");
fprintf(stderr, "close cleanly and had to be killed.\n");
return -1;
}
int rc_remove_pid_file(void)
{
if(access(RC_PID_FILE, F_OK ) == 0) return remove(RC_PID_FILE);
return 0;
}
int rc_enable_signal_handler(void)
{
struct sigaction action;
action.sa_sigaction = NULL;
action.sa_handler = shutdown_signal_handler;
if(sigaction(SIGINT, &action, NULL) < 0){
fprintf(stderr, "ERROR: failed to set sigaction\n");
return -1;
}
if(sigaction(SIGTERM, &action, NULL) < 0){
fprintf(stderr, "ERROR: failed to set sigaction\n");
return -1;
}
if(sigaction(SIGHUP, &action, NULL) < 0){
fprintf(stderr, "ERROR: failed to set sigaction\n");
return -1;
}
action.sa_handler = NULL;
action.sa_sigaction = segfault_handler;
action.sa_flags = SA_SIGINFO | SA_RESETHAND;
if(sigaction(SIGSEGV, &action, NULL) < 0){
fprintf(stderr, "ERROR: failed to set sigaction\n");
return -1;
}
return 0;
}
int rc_disable_signal_handler(void)
{
struct sigaction action;
action.sa_handler = SIG_DFL;
if(sigaction(SIGINT, &action, NULL)<0){
fprintf(stderr, "ERROR: failed to set sigaction\n");
return -1;
}
if(sigaction(SIGTERM, &action, NULL)<0){
fprintf(stderr, "ERROR: failed to set sigaction\n");
return -1;
}
if(sigaction(SIGHUP, &action, NULL)<0){
fprintf(stderr, "ERROR: failed to set sigaction\n");
return -1;
}
if(sigaction(SIGSEGV, &action, NULL)<0){
fprintf(stderr, "ERROR: failed to set sigaction\n");
return -1;
}
return 0;
}
static void segfault_handler(__attribute__ ((unused)) int signum, siginfo_t *info, __attribute__ ((unused)) void *context)
{
fprintf(stderr, "ERROR: Segmentation Fault\n");
fprintf(stderr, "Fault address: %p\n", info->si_addr);
switch (info->si_code){
case SEGV_MAPERR:
fprintf(stderr, "Address not mapped.\n");
break;
case SEGV_ACCERR:
fprintf(stderr, "Access to this address is not allowed.\n");
break;
default:
fprintf(stderr, "Unknown reason.\n");
break;
}
rc_set_state(EXITING);
return;
}
static void shutdown_signal_handler(int signo)
{
switch(signo){
case SIGINT: rc_set_state(EXITING);
printf("\nreceived SIGINT Ctrl-C\n");
break;
case SIGTERM: rc_set_state(EXITING);
printf("\nreceived SIGTERM\n");
break;
case SIGHUP: break;
default:
break;
}
return;
}