Commit 8f39550cea77eef1054b1febe7e647f96ac523c2

Authored by 김태훈
1 parent 3061c73f69
Exists in master and in 2 other branches fhd, fhd-demo

superdaemon 추가

app/superdaemon/Makefile
... ... @@ -0,0 +1,15 @@
  1 +CC = arm-buildroot-linux-gnueabihf-gcc
  2 +OBJS = main.o projectinc.o
  3 +TARGET = superdaemon
  4 +
  5 +all: $(TARGET)
  6 +
  7 +$(TARGET): $(OBJS)
  8 + $(CC) -lm -o $@ $^
  9 +
  10 +%.o:%.c
  11 + @echo "Compiling $< ..."
  12 + $(CC) -c $<
  13 +
  14 +clean:
  15 + rm $(OBJS) $(TARGET)
... ...
app/superdaemon/main.c
... ... @@ -0,0 +1,392 @@
  1 +/*********************************************************************
  2 + 이 름 : superdaemon - 등록된 프로그램을 실행 및 관리
  3 + 버 전 : 1.0.2
  4 + - get_execinfo() 함수 추가
  5 + - read_task_list() 함수에서 task_t 정보를 모두 구성하도록 수정
  6 + 1.0.1
  7 + - 자시 프로세스에서 프로그램을 식행하기 전에
  8 + 부모 프로세스의 타스크 정보에 생성한 프로세스의 ID를
  9 + 지정하는 시간을 벌어 주기 위해 sleep()함수를 호출
  10 + 1.0.0
  11 +*********************************************************************/
  12 +#include <stdio.h>
  13 +#include <stdlib.h>
  14 +#include <signal.h>
  15 +#include <string.h>
  16 +#include <sys/types.h>
  17 +#include <sys/wait.h>
  18 +#include <unistd.h>
  19 +#include "main.h"
  20 +#include "projectinc.h"
  21 +
  22 +#define PATH_SIZE 255
  23 +#define TASK_ARRAY_SIZE 20
  24 +
  25 +task_t ary_task[TASK_ARRAY_SIZE]; // 타스크 배열
  26 +int cnt_task; // 실행한 타스크의 개수
  27 +
  28 +//--------------------------------------------------------------------
  29 +// desc 차일드 프로세스가 종료되면 발생하는 이벤트 핸들러
  30 +// params sig
  31 +// ref 종료된 차일드 프로세스에 해당하는 ary_task 의 아이템 값을
  32 +// 수정한다.
  33 +//
  34 +// ary_task 아이템 값을 수정하면, main() 에서 check_task_list()
  35 +// 를 호출함으로써 다시 실행하게 된다.
  36 +//--------------------------------------------------------------------
  37 +void on_check_child( int sig)
  38 +{
  39 + pid_t pid;
  40 + int result;
  41 + int ndx;
  42 +
  43 + // 종료된 차일드 프로세스에 해당하는 ary_task 아이템을 찾는다.
  44 + // task_t 아이템의 pid 를 0 으로 설정하면 check_task_lisk() 에서
  45 + // count_down 값을 감소하고, 0 이되면 다시 실행하게 된다.
  46 +
  47 + while( 0 < ( pid = waitpid( -1, &result, WNOHANG)))
  48 + { dp( "killed child pid=%d", pid);
  49 + for ( ndx= 0; ndx < cnt_task; ndx++)
  50 + { dp( "checked child pid=%d %s", ary_task[ndx].pid, ary_task[ndx].full_filename);
  51 + if ( pid == ary_task[ndx].pid)
  52 + {
  53 + ary_task[ndx].count_down = ary_task[ndx].interval; // 다시 실행하기 까지의 대기 시간(초)
  54 + ary_task[ndx].pid = 0; // check_task_list() 의 대상이 되도록 0 으로 초기화
  55 + }
  56 + }
  57 + }
  58 +}
  59 +
  60 +//--------------------------------------------------------------------
  61 +// desc _str 에서 첫번째 공백 문자 전까지
  62 +// 또는 공백 문자가 없을 때에는 문자열 끝까지를 _rst
  63 +// 에 복사한다.
  64 +// params _str 원본 문자열을 가지고 있는 변수
  65 +// _rst 문자열을 복사 받을 변수
  66 +// ret 복사 문자열 다음의 문자 위치의 포인터
  67 +//--------------------------------------------------------------------
  68 +char *str_spc( char *_str, char *_rst)
  69 +{
  70 + char *pos;
  71 +
  72 + *_rst = '\0'; // NULL 을 대입
  73 + pos = index(_str, ' ');
  74 + if ( !pos) // 공백 구분자가 없다면 다음 루프로
  75 + {
  76 + strcpy(_rst, _str);
  77 + }
  78 + else
  79 + {
  80 + strncpy(_rst, _str, pos-_str);
  81 + _rst[pos-_str] = '\0';
  82 + while ( ' ' == *pos) // 앞에 있는 공백 삭제
  83 + pos++;
  84 + }
  85 + return( pos);
  86 +}
  87 +
  88 +//--------------------------------------------------------------------
  89 +// desc _task 의 내용에 따라 새 프로세스를 만들어서 실행한다.
  90 +// params _task : 실행 파일에 대한 정보
  91 +// ref 1.
  92 +// _task 의 task 로부터 실행파일과 인수를 구한다.
  93 +// 실행이나 인수에서 문제가 있다면 에러를 출력하고,
  94 +// _task 의 error 에 에러코드를 넣어 다음 호출에도 실행이
  95 +// 되지 않도록 한다.
  96 +// 2.
  97 +// 인수는 20개까지 처리한다.
  98 +//
  99 +// 3.
  100 +// 인수의 개수는 프로그램에 따라 다르므로,
  101 +// *[] 를 사용하는 execv() 를 사용한다.
  102 +//--------------------------------------------------------------------
  103 +void exec_task( task_t *_task)
  104 +{
  105 + pid_t pid;
  106 +
  107 + if ( ERR_NONE != _task->error)
  108 + {
  109 + return;
  110 + }
  111 +
  112 + pid = fork();
  113 + if ( 0 == pid) // 자식 프로세서 라면
  114 + { dp( "1 sec wait and exec %s", _task->full_filename);
  115 + sleep( 1); // 프모 프로세스에서 _task->pid = pid 가 실행이 되도록 sleep
  116 + execv(_task->full_filename, _task->params); // 프로그램을 실행한다.
  117 + }
  118 + else if ( 0 < pid) // fork 실행에 이상이 없었다면
  119 + { dp( "********** task= %s New Pid= %d", _task->full_filename, pid);
  120 + _task->pid = pid;
  121 + }
  122 + else // 만일 fork() 실행에 실패했다면
  123 + { dp( "Reset Counter %d", _task->interval);
  124 + _task->count_down = _task->interval;
  125 + }
  126 +}
  127 +
  128 +//--------------------------------------------------------------------
  129 +// desc ary_task 목록을 확인해서 종료된 차일드가 있는지를 확인한다.
  130 +// 종료된 차일드가 있다면 실행여부(에러 상태)를 확인하고,
  131 +// 재 실행 시킨다.
  132 +//--------------------------------------------------------------------
  133 +void check_task_list( void)
  134 +{
  135 + int ndx;
  136 +
  137 + // ary_task 목록에서 pid 가 0 인 항목은
  138 + // 종료된 차일드 이다.
  139 + //
  140 +
  141 + for ( ndx = 0; ndx < cnt_task; ndx++)
  142 + {
  143 + if ( ( !ary_task[ndx].pid) // 타스크가 실행 전이고
  144 + && ( ERR_NONE == ary_task[ndx].error)) // 에러가 없다면
  145 + {
  146 + if ( 0 < ary_task[ndx].count_down) // 아직 카우트 다운 중이라면
  147 + { dp( "count down :%d", ary_task[ndx].count_down);
  148 + ary_task[ndx].count_down--;
  149 + }
  150 + else // 카운트 다운을 완료
  151 + {
  152 + exec_task( &ary_task[ndx]);
  153 + }
  154 + }
  155 + }
  156 + return;
  157 +}
  158 +
  159 +//--------------------------------------------------------------------
  160 +// 파일의 문자열에서 task_t 의 정보중 프로그램 이름과 인수를 구한다.
  161 +//
  162 +// _data : 파일에서 읽어 들인 내용중 디렉토리 위치부터의 문자열
  163 +// _task : 실행 파일에 대한 정보
  164 +//
  165 +// typedef struct
  166 +// {
  167 +// char *full_filename; <- 디렉토리 포함 전체 파일 이름을 구한다.
  168 +// char *params[20]; <- 프로그램 실행을 위한 인수 배열을 구한다.
  169 +// :
  170 +// int error; <- 내용을 구성 중에 에러가 있다면 에러 정보
  171 +// :
  172 +// } task_t;
  173 +
  174 +
  175 +
  176 +// ref 1.
  177 +// _task 의 task 로부터 실행파일과 인수를 구한다.
  178 +// 실행이나 인수에서 문제가 있다면 에러를 출력하고,
  179 +// _task 의 error 에 에러코드를 넣어 다음 호출에도 실행이
  180 +// 되지 않도록 한다.
  181 +// 2.
  182 +// 인수는 20개까지 처리한다.
  183 +//
  184 +// 3.
  185 +// 인수의 개수는 프로그램에 따라 다르므로,
  186 +// *[] 를 사용하는 execv() 를 사용한다.
  187 +//--------------------------------------------------------------------
  188 +void get_execinfo( char *_data, task_t *_task)
  189 +{
  190 + char buf[PACKET_SIZE];
  191 + char exe_path[PATH_SIZE+5]; // 에러 대비 +5
  192 + char exe_name[PATH_SIZE+5]; // 에러 대비 +5
  193 + int nparam;
  194 + int sz_str;
  195 + pid_t pid;
  196 +
  197 + // 경로 명을 구한다.
  198 + // 경로 명의 끝에 '/' 이 없다면 추가한다.
  199 +
  200 + _data = str_spc(_data, buf);
  201 + if ( PATH_SIZE < strlen( buf)) // 경로명의 길이가 너무 길다.
  202 + {
  203 + printf( "%s : path is too long!!\n", buf);
  204 + _task->error = ERR_PATH_TOO_LONG;
  205 + return;
  206 + }
  207 + strcpy( exe_path, buf); // exe_path <- 경로명
  208 +
  209 + // 경로 명의 끝의 '/' 를 확인한다.
  210 +
  211 + sz_str = strlen( exe_path);
  212 + if ( '/' != exe_path[sz_str-1]) // 끝에 / 를 반드시 포함
  213 + {
  214 + exe_path[sz_str ] = '/';
  215 + exe_path[sz_str+1] = '\0';
  216 + }
  217 +
  218 + // 실행 파일의 전체 이름을 구한다.
  219 +
  220 + _data = str_spc(_data, buf);
  221 + if ( PATH_SIZE < strlen( exe_path)+strlen( buf)) // 경로와 실행파일의 이름 길이 합이 너무 길다.
  222 + {
  223 + printf( "%s/%s : full name is too long!!\n", exe_path, buf);
  224 + _task->error = ERR_PATH_TOO_LONG;
  225 + return;
  226 + }
  227 + strcpy( exe_name, buf);
  228 + strcat( exe_path, exe_name); // exe_path <- 경로/실행파일이름
  229 + _task->full_filename = malloc( strlen( exe_path)+1);
  230 + strcpy( _task->full_filename, exe_path);
  231 +
  232 + // 실행 파일의 존재 유무를 확인한다.
  233 +
  234 + if ( 0 != access(_task->full_filename, F_OK))
  235 + {
  236 + printf( "%s is not exists!!\n",_task->full_filename);
  237 + _task->error = ERR_PATH_TOO_LONG;
  238 + return;
  239 + }
  240 +
  241 + // 실행에 필요한 인수를 배열로 구성한다.
  242 + // 첫번째 인수는 실행파일 이름으로 한다.
  243 + // 마지막 인수는 NULL 이 되도록 한다.
  244 +
  245 + _task->params[0] = malloc( strlen( exe_name)+1);
  246 + strcpy(_task->params[0], exe_name);
  247 +
  248 + // [0] 인수는 실행 파일의 이름이므로,
  249 + // [1] 부터 프로그램의 인수를 대입한다.
  250 + // params는 *[20]로 선언되어 있으므로
  251 + // 인수 개수가 19 개를 넘지 못하게 한다.
  252 +
  253 + nparam = 1;
  254 + while (_data)
  255 + {
  256 + _data = str_spc(_data, buf); // 다음 인수값을 구한다.
  257 + _task->params[nparam] = malloc( strlen( buf)+1); // param[nparam] <- 인수값
  258 + strcpy(_task->params[nparam], buf);
  259 + nparam++;
  260 + if ( 19 == nparam) // 인수가 너무 많다면
  261 + {
  262 + printf( "%s has too many params!!\n", exe_name);
  263 + _task->error = ERR_TOO_MANY_PARAMS;
  264 + return;
  265 + }
  266 + }
  267 + _task->params[nparam] = ( char *)0; // 마지막 아이템은 NULL
  268 + _task->error = ERR_NONE; // 에러 없음
  269 +}
  270 +
  271 +//--------------------------------------------------------------------
  272 +// ini 파일에서 실행할 파일의 정보를 읽어 들이고,
  273 +// ary_task 목록을 작성한다.
  274 +//--------------------------------------------------------------------
  275 +void read_task_list( void)
  276 +{
  277 + FILE *fp;
  278 + char *tag;
  279 + char *pos;
  280 + char buf[PACKET_SIZE];
  281 + char item[255];
  282 +
  283 + cnt_task = 0; // 실행할 작업 개수 초기화
  284 +
  285 + fp = fopen( "./superdaemon.ini", "r"); // 환경파일 open;
  286 + if ( NULL == fp)
  287 + { // 읽어 들일 타스크가 없음
  288 + return;
  289 + }
  290 + else
  291 + {
  292 + while( NULL != fgets( buf, PACKET_SIZE, fp))
  293 + {
  294 + tag = buf;
  295 + remove_white_char( tag); // 문장에 라인피드와 같은 화이트 문자를 없앤다.
  296 +
  297 + // 프로그램 종료 시 다음 시작까지의 시간 간격을 구한다.
  298 +
  299 + while ( ' ' == *tag) // 앞에 있는 공백 삭제
  300 + {
  301 + tag++;
  302 + }
  303 +
  304 + if ( ( ' ' > *tag) || // 문장의 끝이거나
  305 + ( !strncmp( tag, "//", 2)) ) // 주석문이라면 다음 행으로
  306 + {
  307 + continue;
  308 + }
  309 + // 행의 첫 번째 데이터는 대기 시간 정보이다.
  310 + // 첫번째 문자열에서 시간 정보를 구한다.
  311 +
  312 + tag = str_spc( tag, item);
  313 + ary_task[cnt_task].interval = atoi( item);
  314 +
  315 + // 프로그램의 실행 정보를 구한다.
  316 + // 실행 정보에는 경로와 프로그램 명이 있어야 하므로
  317 + // 최소 공백 문자가 하나 있어야 한다.
  318 + // 인수에 공백 문자가 없다면 취소한다.
  319 +
  320 + if ( ( ' ' > *tag) || // 문장의 끝이거나
  321 + ( !strncmp( tag, "//", 2)) ) // 주석문이라면 다음 행으로
  322 + {
  323 + continue;
  324 + }
  325 +
  326 + pos = index( tag, ' '); // 공백 문자가 없다면 취소한다.
  327 + if (!pos)
  328 + {
  329 + continue;
  330 + }
  331 +
  332 + get_execinfo( tag, &ary_task[cnt_task]);
  333 +
  334 + // task 의 초기값을 초기화 한다.
  335 +
  336 + ary_task[cnt_task].count_down = 0;
  337 + ary_task[cnt_task].pid = 0;
  338 + cnt_task++;
  339 + if ( TASK_ARRAY_SIZE == cnt_task)
  340 + {
  341 + printf( "***** Task list is over %d. *****\n", TASK_ARRAY_SIZE);
  342 + break;
  343 + }
  344 + }
  345 + }
  346 + fclose( fp);
  347 +}
  348 +
  349 +//--------------------------------------------------------------------
  350 +// 차일드 프로세서의 죽음을 확인하는 시그널 등록
  351 +//--------------------------------------------------------------------
  352 +void reg_child_signal( void)
  353 +{
  354 + struct sigaction sig_child; // 차일드의 죽음을 알기 위한 시그털
  355 +
  356 + sig_child.sa_handler = on_check_child;
  357 + sigemptyset( &sig_child.sa_mask);
  358 + sig_child.sa_flags = 0;
  359 + sigaction( SIGCHLD, &sig_child, 0);
  360 +}
  361 +
  362 +//--------------------------------------------------------------------
  363 +// main procedure
  364 +//--------------------------------------------------------------------
  365 +int main( int argc, char **argv)
  366 +{
  367 +#ifdef NDEBUG
  368 +
  369 + pid_t pid_daemon; // release 모드일 때만 endif 사이의 내용이 실행된다.
  370 +
  371 + pid_daemon = fork();
  372 + if ( 0 > pid_daemon) // fork() 실행에 실패했다면
  373 + {
  374 + printf( "ERR: fork() failed\n"); // 실행 에러를 알린다.
  375 + exit( 1);
  376 + }
  377 + else if( 0 != pid_daemon) // 부모 프로세스 라면
  378 + {
  379 + exit( 0); // 종료한다.
  380 + }
  381 +
  382 +#endif
  383 +
  384 + reg_child_signal(); // child 시그널 등록
  385 + read_task_list(); // 실행할 정보 목록을 작성한다.
  386 +
  387 + while( 1 )
  388 + {
  389 + check_task_list(); // 작업 상태를 확인한다.
  390 + sleep( 1); // 1 초를 대기
  391 + }
  392 +}
... ...
app/superdaemon/main.h
... ... @@ -0,0 +1,27 @@
  1 +#ifndef MAIN_H
  2 +#define MAIN_H
  3 +
  4 +#include <sys/types.h>
  5 +
  6 +#define TRUE 1
  7 +#define FALSE 0
  8 +
  9 +#define ERR_NONE 0
  10 +#define ERR_PATH_TOO_LONG -1
  11 +#define ERR_EXE_TOO_LONG -2
  12 +#define ERR_PARAMS_TO0_LONG -3;
  13 +#define ERR_TOO_MANY_PARAMS -4;
  14 +#define ERR_EXECUTE -5;
  15 +
  16 +typedef struct
  17 +{
  18 + char *full_filename;
  19 + char *params[20];
  20 + int interval;
  21 + int count_down;
  22 + int error;
  23 + pid_t pid;
  24 +} task_t;
  25 +
  26 +
  27 +#endif
... ...
app/superdaemon/projectinc.c
... ... @@ -0,0 +1,33 @@
  1 +#include <stdio.h>
  2 +#include <stdlib.h>
  3 +#include <string.h>
  4 +#include "projectinc.h"
  5 +
  6 +//--------------------------------------------------------------------
  7 +// desc 문자열에서 /t /n /f 와 같은 화이트 문자를 NULL 코드로
  8 +// 바꾸어 준다.
  9 +//--------------------------------------------------------------------
  10 +void remove_white_char( char *_str)
  11 +{
  12 + int ndx;
  13 +
  14 + for ( ndx = 0; ndx < strlen(_str); ndx++)
  15 + {
  16 + if (' ' > _str[ndx])
  17 + {
  18 + _str[ndx] = '\0';
  19 + }
  20 + }
  21 +}
  22 +
  23 +//--------------------------------------------------------------------
  24 +// desc 메시지를 전송하고 실행을 중지한다.
  25 +//--------------------------------------------------------------------
  26 +void error_handling(char *message)
  27 +{
  28 + fputs( message, stderr);
  29 + fputc( '\n', stderr);
  30 + exit(1);
  31 +}
  32 +
  33 +
... ...
app/superdaemon/projectinc.h
... ... @@ -0,0 +1,21 @@
  1 +#ifndef _PROJECTINC_
  2 +#define _PROJECTINC_
  3 +
  4 +#define NDEBUG
  5 +
  6 +#ifndef NDEBUG
  7 +#define dp(fmt,args...) printf( fmt, ## args ); putchar( '\n')
  8 +#define dlp(fmt,args...) printf( "[%s %d]" fmt, __FILE__,__LINE__, ## args )
  9 +#else
  10 +#define dp(fmt,args...)
  11 +#define dlp(fmt,args...)
  12 +#endif
  13 +
  14 +extern void remove_white_char( char *);
  15 +extern void error_handling(char *message);
  16 +
  17 +//----- SysManager ---------------------------------------------------
  18 +
  19 +#define PACKET_SIZE 1024
  20 +
  21 +#endif
... ...
app/superdaemon/readme.txt
... ... @@ -0,0 +1,29 @@
  1 +prj name : superdaemon - 등록된 프로그램을 실행 및 관리
  2 +descript :
  3 +
  4 + 1. superdaemon.ini 에 등록된 프로그램을 실행하고
  5 + 프로그램 종료를 계속 확인하면서,
  6 + 종료되면 지정된 시간 후 다시 실행하게 한다.
  7 +
  8 + 2. 추후, watch dog 기능을 추가한다.
  9 +
  10 +version :
  11 + 0.0.1 superdaemon.ini 에 등록된 프로그램을 실행
  12 + 0.0.2 execl() 사용하여 구성
  13 + 0.0.3 1.
  14 + execl() 을 execv() 로 재 구성
  15 + ref
  16 + execl() 은 인수가 ( )
  17 + 2.
  18 + 실행파일 정보 문자열에 이상이 없을 때에만
  19 + fork() 실행하도록 수정
  20 + 1.0.0 1.
  21 + 파일을 실행할 때, 파일의 존재 유무를 확인하는
  22 + 코드를 추가
  23 + 2.
  24 + 환경 파일에서 경로명에 '/' 이 없을 때 발생하던
  25 + 오류를 잡음
  26 +
  27 +
  28 + 장길석, goguryeo@gmail.com
  29 +
0 30 \ No newline at end of file
... ...
app/superdaemon/superdaemon.ini
... ... @@ -0,0 +1,30 @@
  1 +// superdaemon 에서 사용되는 환경파일입니다.
  2 +
  3 +// 아래의 리스트는 superdaemon 에서 실행되며,
  4 +// 프로그램이 종료되더라도 지정된 시간(초) 후에 다시
  5 +// 자동으로 실행됩니다.
  6 +
  7 +// 작성 방법은 공백 문자로 분리가 되며,
  8 +// [시간] [경로] [실행파일 이름] [인수1] [인수2]....
  9 +// 식으로 한개의 행에 하나의 프로그램 실행 정보를 넣어주면 됩니다.
  10 +
  11 +// 실행 경로와 실행파일의 이름을 합쳐서 255자 이내이어야 하며,
  12 +// 인수는 20 개 이상이 될 수 없습니다.
  13 +
  14 +1 /bin/ ls -al 123 234 456
  15 +3 /bin/ ls -al /tmp 678 90
  16 +5 /bin/ ls -al /usr abc def
  17 +
  18 +//2 ./ tcp2rx /dev/ttyS05 22549
  19 +//2 ./ tcp2rx /dev/ttyS04 22559
  20 +//2 ./ tcp2rx /dev/ttyS03 22569
  21 +
  22 +//2 ./ udp2rx /dev/ttyS06 22539 192.168.10.2
  23 +//2 ./ udp2rx /dev/ttyS05 22549 192.168.10.2
  24 +//2 ./ udp2rx /dev/ttyS04 22559 192.168.10.2
  25 +//2 ./ udp2rx /dev/ttyS03 22569 192.168.10.2
  26 +
  27 +//5 . child 1
  28 +//5 ./ child 1 2
  29 +//5 ./ child 1 2 3
  30 +
... ...