/*
Copyright (C) HWPORT.COM
All rights reserved.
Author: JAEHYUK CHO <mailto:minzkn@minzkn.com>
*/
#if !defined(_ISOC99_SOURCE)
# define _ISOC99_SOURCE (1L)
#endif
#if !defined(_GNU_SOURCE)
# define _GNU_SOURCE (1L)
#endif
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
int hwport_launcher(void);
static void mysignal_handler(int s_signum);
int main(int s_argc, char **s_argv);
static int g_is_break = 0;
int hwport_launcher(void)
{
pid_t s_pid;
for(;;) {
s_pid = fork(); /* 프로세스를 생성 */
if(s_pid == ((pid_t)(-1))) { /* fork 실패 */
return(-1);
}
else if(s_pid == ((pid_t)0)) { /* 자식프로세스 부분 - 실제 우리가 구현하는 목적의 코드들이 자식프로세스에서 실행되도록 합니다. */
/* ok. immortal process start */
/* need to default signal handler ! */
#if defined(SIGBUS)
(void)signal(SIGBUS, SIG_DFL);
#endif
#if defined(SIGSTKFLT)
(void)signal(SIGSTKFLT, SIG_DFL);
#endif
(void)signal(SIGILL, SIG_DFL);
(void)signal(SIGFPE, SIG_DFL);
(void)signal(SIGSEGV, SIG_DFL);
break;
}
else { /* 부모프로세스 - 자식프로세스를 감시하고 비정상 종료를 감시하도록 하며 정상종료시에는 부모프로세스도 함께 종료합니다. */
pid_t s_waitpid_check;
int s_status = 0;
int s_options = 0;
int s_signum = (-1);
#if 1L /* DEBUG - SIGINT */
(void)signal(SIGINT, SIG_IGN);
#endif
(void)fprintf(stdout, "Start monitoring by hwport_launcher ! (pid=%ld)\n", (long)s_pid);
/* 디버거에 의해서 step실행하게 될때 어떤 영향을 받는지 고민해봐야죠. */
#if defined(WUNTRACED)
s_options |= WUNTRACED;
#endif
#if defined(WCONTINUED)
s_options |= WCONTINUED;
#endif
do {
s_waitpid_check = waitpid(s_pid, (int *)(&s_status), s_options); /* 자식프로세스의 종료를 기다립니다. */
if(s_waitpid_check == ((pid_t)(-1))) { /* 뭔가 자식프로세스를 확인하기 어려운 상황 */
/* what happen ? */
(void)fprintf(stderr, "Waitpid failed by hwport_launcher ! (pid=%ld)\n", (long)s_pid);
exit(EXIT_SUCCESS);
}
if(WIFEXITED(s_status) != 0) { /* 일반적인 자식프로세스 종료상태 - main함수에서 반환 또는 exit함수에 의한 종료등 */
/* normal exit */
(void)fprintf(stdout, "Stop monitoring by hwport_launcher ! (pid=%long)\n", (long)s_pid);
exit(EXIT_SUCCESS);
}
else if(WIFSIGNALED(s_status) != 0) { /* Signal 발생에 의한 종료 */
s_signum = WTERMSIG(s_status);
if(
#if defined(SIGBUS)
(s_signum != SIGBUS) &&
#endif
#if defined(SIGSTKFLT)
(s_signum != SIGSTKFLT) &&
#endif
(s_signum != SIGILL) &&
(s_signum != SIGFPE) &&
(s_signum != SIGSEGV) &&
(s_signum != SIGPIPE)) { /* 비정상 종료로 볼 수 있는 signal을 제외한 나머지 signal에 의한 종료인 경우 */
/* normal exit */
(void)fprintf(stdout, "Stop monitoring by hwport_launcher ! (pid=%long, signum=%d)\n", (long)s_pid, s_signum);
exit(EXIT_SUCCESS);
}
}
}while((WIFEXITED(s_status) == 0) && (WIFSIGNALED(s_status) == 0)); /* 자식프로세스가 종료된게 아닌 상황이면 다시 waitpid하는 loop - 보통 디버거상태를 추적하는 중일때 이 loop가 회전합니다. */
(void)fprintf(stdout, "Restarting by hwport_launcher ! (pid=%ld, signum=%d)\n", (long)s_pid, s_signum);
sleep(3); /* 재시작 하기전에 약간 시간을 줘야 하는 경우가 프로그램 구현에 따라서 고려되어야 한다는 의미로... */
/* retry launch loop */
}
}
return(0);
}
static void mysignal_handler(int s_signum)
{
(void)fprintf(stderr, "CTRL+C\n");
g_is_break = 1;
(void)signal(s_signum, &mysignal_handler);
}
int main(int s_argc, char **s_argv)
{
int s_ticks;
(void)s_argc;
(void)s_argv;
(void)fprintf(stdout, "BEGIN TEST...\n");
if(hwport_launcher() == (-1)) {
(void)fprintf(stderr, "hwport_launcher failed !\n");
}
/* 이제부터 비정상 정료가 발생하면 hwport_launcher호출시점부터 다시 실행됩니다. */
(void)signal(SIGINT, &mysignal_handler); /* CTRL+C */
for(s_ticks = 0;(s_ticks < 60) && (g_is_break == 0);s_ticks++) {
(void)fprintf(stdout, "alive (ticks=%d)\n", s_ticks);
if(s_ticks == 10) {
*((unsigned long *)0) = 1234; /* segment fault ! - 강제로 비정상 종료를 위해서 Fault상황을 발생해보죠. */
}
sleep(1);
}
(void)fprintf(stdout, "END TEST...\n");
return(EXIT_SUCCESS);
}
/* vim: set expandtab: */
/* End of source */