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