-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.c
128 lines (117 loc) · 4.26 KB
/
main.c
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
#include <systemd/sd-daemon.h>
#include "parse_option.h"
#include "stream_match.h"
#include "subprocess.h"
#include "timeout_read.h"
#include "kill_process.h"
#define BUFFER_SIZE 10240
#define PROG_SUCCESS 1
#define PROG_STUCK -1
#define PROG_ERROR -2
int proc_pid = -1;
void exit_handler(int signum);
int wait_program_success(int fd_read, int timeout_ms, Matcher *matcher)
{
char buffer[BUFFER_SIZE + 1];
while (1)
{
ssize_t read_size = timeout_read(fd_read, buffer, BUFFER_SIZE, timeout_ms);
if (read_size == READ_TIMEOUT)
{
// 程序输出超时!可能卡死了!
return PROG_STUCK;
}
else if (read_size == SELECT_ERROR || read_size == READ_ERROR)
{
// 总之就是读错误
return PROG_ERROR;
}
else if (read_size == READ_EOF)
{
// EOF了,发送方主动关闭了写端?
return PROG_ERROR;
}
// 不论是读错误,还是写端被关闭,都不要再读了。
else
{
// 程序必须持续输出子进程的输出。这很重要。在程序顺利启动后,守护者必须也继续转发程序输出。
write(STDOUT_FILENO, buffer, read_size);
// 赶紧组装个字符串。
buffer[read_size] = '\0';
// 先看看是不是启动了?
MatchResult match_result = matcher_receive(matcher, buffer);
if (match_result == MATCH)
{
// 程序启动成功!
return PROG_SUCCESS;
}
}
// 不要等待,立刻开始新的读取。
}
}
int main(int argc, char **argv)
{
Options *opts = parse_options(argc, argv);
Matcher *matcher = matcher_init(opts->start_successful_output);
// 注册信号处理函数
if (signal(SIGINT, exit_handler) == SIG_ERR) {
perror("signal");
exit(EXIT_FAILURE);
}
ExecInfo *exec_thread = fork_new_thread(opts->arguments);
proc_pid = exec_thread->pid;
close(exec_thread->pipefd_write);
int wait_result = wait_program_success(exec_thread->pipefd_read, opts->max_stuck_time_sec * 1000, matcher);
// 这里退出,说明matcher不需要继续使用了。回收其内存空间。
matcher_destroy(matcher);
if (wait_result == PROG_SUCCESS)
{
fprintf(stderr, "program %d started successfully.\n", proc_pid);
// 发送sd-notify
sd_notify(0, "READY=1");
// 程序成功启动,开始转发程序的标准输出。此时的程序就是一个无情的转发机器。
int status;
ssize_t bytes_read;
char buffer[BUFFER_SIZE];
while ((bytes_read = read(exec_thread->pipefd_read, buffer, BUFFER_SIZE)) > 0)
{
write(STDOUT_FILENO, buffer, bytes_read);
}
// 如果程序读失败了……打印错误。
if (bytes_read < 0)
{
perror("read");
}
// 保护子进程,直到子进程退出。
waitpid(exec_thread->pid, &status, 0);
return status; // 保证主进程的输出结果和子进程相同。
}
else if (wait_result == PROG_STUCK)
{
// 判断程序卡死,给程序20秒的时间退出
fprintf(stderr, "program stuck! killing...\n");
ProcInfo *kill_result = kill_proc_grp_by_pid(exec_thread->pid, 20);
if (kill_result->status != KILL_ERROR)
{
fprintf(stderr, "successful killed program group of %d.\n", exec_thread->pid);
return kill_result->exit_code;
}
// 如果不能正确的杀死程序,那么只能等待程序自行退出了。
}
int status = 0;
// 如果读取失败或EOF,阻塞的等待子进程退出即可。
waitpid(exec_thread->pid, &status, 0);
return status;
}
void exit_handler(int _signum) {
// 主线程退出后,需要将子进程杀死。
fprintf(stderr, "master process exit, exiting...\n");
if (proc_pid > 0) {
ProcInfo *kill_result = kill_proc_grp_by_pid(proc_pid, 20);
if (kill_result->status != KILL_ERROR)
{
fprintf(stderr, "successful killed program group of %d.\n", proc_pid);
return ;
}
}
};