|
1 #define _POSIX_C_SOURCE 199309L |
|
2 #include <stdio.h> |
|
3 #include <stdlib.h> |
|
4 #include <stdarg.h> |
|
5 #include <string.h> |
|
6 #include <unistd.h> |
|
7 #include <poll.h> |
|
8 #include <time.h> |
|
9 #include <signal.h> |
|
10 #include <sys/types.h> |
|
11 #include <sys/wait.h> |
|
12 #include <fcntl.h> |
|
13 #include <errno.h> |
|
14 #include <math.h> |
|
15 |
|
16 |
|
17 enum DPokeSourceType_en { |
|
18 DPOKE_PROGRAM, |
|
19 DPOKE_FILE, |
|
20 DPOKE_FUNCTION, |
|
21 }; |
|
22 typedef enum DPokeSourceType_en DPokeSourceType; |
|
23 |
|
24 #define DPOKE_BUFFER 32 |
|
25 struct DPokeSource_st { |
|
26 DPokeSourceType type; |
|
27 char *path; |
|
28 int arg; |
|
29 |
|
30 //FILE *stream; |
|
31 int fd; |
|
32 enum DPokeSourceStatus_en { |
|
33 DPOKE_STARTED, |
|
34 DPOKE_RUNNING, |
|
35 DPOKE_DIED, |
|
36 DPOKE_FAIL, |
|
37 DPOKE_FAILED, |
|
38 } status; |
|
39 char buffer[DPOKE_BUFFER+1]; |
|
40 char current[DPOKE_BUFFER+1]; |
|
41 int buffer_usage; |
|
42 |
|
43 // DPOKE_PROGRAM-specific fields: |
|
44 int pid; |
|
45 }; |
|
46 |
|
47 typedef struct DPokeSource_st DPokeSource; |
|
48 |
|
49 #define LENGTH(X) (sizeof X / sizeof X[0]) |
|
50 |
|
51 char* smprintf(char *fmt, ...) { |
|
52 va_list fmtargs; |
|
53 char *ret; |
|
54 int len; |
|
55 |
|
56 va_start(fmtargs, fmt); |
|
57 len = vsnprintf(NULL, 0, fmt, fmtargs); |
|
58 va_end(fmtargs); |
|
59 |
|
60 ret = malloc(++len); |
|
61 if (ret == NULL) { |
|
62 perror("malloc"); |
|
63 exit(1); |
|
64 } |
|
65 |
|
66 va_start(fmtargs, fmt); |
|
67 vsnprintf(ret, len, fmt, fmtargs); |
|
68 va_end(fmtargs); |
|
69 |
|
70 return ret; |
|
71 } |
|
72 |
|
73 |
|
74 void display(); |
|
75 |
|
76 extern char* v[]; |
|
77 #include "config.h" |
|
78 #ifdef DEBUG |
|
79 #define errprintf(...) fprintf(stderr,...) |
|
80 #else |
|
81 #define errprintf(...) |
|
82 #endif |
|
83 |
|
84 char* v[LENGTH(sources)]; |
|
85 |
|
86 static struct pollfd source_fds[LENGTH(sources)]; |
|
87 |
|
88 void source_open(int source_id) { |
|
89 DPokeSource *src = &sources[source_id]; |
|
90 //FILE* stream; |
|
91 int p_stdout[2]; |
|
92 switch (src->type) { |
|
93 case DPOKE_PROGRAM: |
|
94 //stream = popen(src->path, "r"); |
|
95 if (pipe(p_stdout)!=0) { |
|
96 perror("pipe"); |
|
97 exit(7); |
|
98 } |
|
99 pid_t pid = fork(); |
|
100 if (pid < 0) { |
|
101 perror("fork"); |
|
102 exit(7); |
|
103 } else if (pid == 0) { |
|
104 dup2(open("/dev/null",O_RDONLY),0); |
|
105 close(p_stdout[0]); |
|
106 dup2(p_stdout[1],1); |
|
107 execl("/bin/sh", "sh", "-c", src->path, NULL); |
|
108 perror("execl"); |
|
109 exit(1); |
|
110 } |
|
111 src->fd = p_stdout[0]; |
|
112 src->pid = pid; |
|
113 break; |
|
114 default: |
|
115 errprintf("Don't know how to handle source type %d.\n",src->type); |
|
116 exit(2); |
|
117 } |
|
118 //src->stream = stream; |
|
119 //src->fd = fileno(stream); |
|
120 src->buffer_usage = 0; |
|
121 src->status = DPOKE_STARTED; |
|
122 source_fds[source_id].fd = sources[source_id].fd; |
|
123 source_fds[source_id].events = POLLIN; |
|
124 } |
|
125 |
|
126 static void sigchld_hdl (int sig) |
|
127 { |
|
128 pid_t pid; |
|
129 while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) { |
|
130 for (int i=0;i<LENGTH(sources);i++) |
|
131 if ((sources[i].type == DPOKE_PROGRAM) && (sources[i].pid == pid)) { |
|
132 sources[i].pid = -1; |
|
133 errprintf("#%d pid %d died.\n",i,pid); |
|
134 switch (sources[i].status) { |
|
135 case DPOKE_STARTED: |
|
136 sources[i].status = DPOKE_FAIL; break; |
|
137 case DPOKE_RUNNING: |
|
138 sources[i].status = DPOKE_DIED; break; |
|
139 default: |
|
140 errprintf("#%d died in state %d. Whaddafuck?!\n",i,sources[i].status); |
|
141 exit(8); |
|
142 } |
|
143 } |
|
144 } |
|
145 } |
|
146 |
|
147 void source_close(int source_id) { |
|
148 DPokeSource *src = &sources[source_id]; |
|
149 if (src->pid != -1) kill(src->pid,SIGTERM); |
|
150 if (src->fd != -1) close(src->fd); |
|
151 source_fds[source_id].fd = -1; |
|
152 } |
|
153 |
|
154 double dpoke_time() { |
|
155 struct timespec tp; |
|
156 if (clock_gettime(CLOCK_MONOTONIC,&tp)) { |
|
157 perror("clock_gettime"); |
|
158 exit(5); |
|
159 } |
|
160 return tp.tv_sec + tp.tv_nsec*1E-9; |
|
161 } |
|
162 |
|
163 int main(int argc, char* argv[]) { |
|
164 struct sigaction act; |
|
165 |
|
166 memset (&act, 0, sizeof(act)); |
|
167 act.sa_handler = sigchld_hdl; |
|
168 if (sigaction(SIGCHLD, &act, 0)) { |
|
169 perror ("sigaction"); |
|
170 exit(6); |
|
171 } |
|
172 |
|
173 for (int i = 0; i < LENGTH(sources); i++ ) { |
|
174 source_open(i); |
|
175 v[i] = sources[i].current; |
|
176 } |
|
177 int status; |
|
178 int sleeptime = 10000; |
|
179 double prevtime=dpoke_time(); |
|
180 while (1) { |
|
181 status = poll(source_fds,LENGTH(sources), sleeptime); |
|
182 if (status < 0) { |
|
183 if (errno == EINTR) |
|
184 continue; |
|
185 } |
|
186 for (int i = 0; i < LENGTH(sources); i++) { |
|
187 DPokeSource *src = sources+i; |
|
188 if (source_fds[i].revents & POLLIN) { |
|
189 int data_read = read(src->fd,src->buffer+src->buffer_usage,DPOKE_BUFFER-src->buffer_usage); |
|
190 if (data_read>0) { |
|
191 src->buffer_usage += data_read; |
|
192 if (src->buffer_usage==DPOKE_BUFFER) { |
|
193 errprintf("Buffer is full for #%d.\n",i); |
|
194 } |
|
195 src->buffer[src->buffer_usage] = '\0'; |
|
196 char* eolpos = strchr(src->buffer + src->buffer_usage - data_read,'\n'); |
|
197 if (eolpos) { |
|
198 memcpy(src->current,src->buffer,eolpos-src->buffer); |
|
199 src->current[eolpos-src->buffer] = '\0'; |
|
200 src->buffer_usage -= (eolpos - src->buffer + 1); |
|
201 memmove(src->buffer,eolpos + 1,src->buffer_usage + 1); |
|
202 } |
|
203 if (src->status == DPOKE_STARTED) |
|
204 src->status = DPOKE_RUNNING; |
|
205 } else { |
|
206 perror("read"); |
|
207 exit(3); |
|
208 } |
|
209 } else if ((source_fds[i].revents & POLLHUP)||(src->status==DPOKE_DIED)) { |
|
210 errprintf("#%d HUP\n",i); |
|
211 source_close(i); |
|
212 if (src->status==DPOKE_STARTED) |
|
213 src->status = DPOKE_FAIL; |
|
214 else |
|
215 source_open(i); |
|
216 } else if (source_fds[i].revents) { |
|
217 errprintf("#%d revents: %d\n",i,source_fds[i].revents); |
|
218 } |
|
219 if (src->status==DPOKE_FAIL) { |
|
220 src->status = DPOKE_FAILED; |
|
221 errprintf("#%d Marked as failure\n",i); |
|
222 strcpy(src->current,FAILURE_MSG); |
|
223 } |
|
224 } |
|
225 double curtime = dpoke_time(); |
|
226 if ((curtime-prevtime)>MINTIME) { |
|
227 display(); |
|
228 prevtime = curtime; |
|
229 sleeptime = 10000; |
|
230 } else { |
|
231 double sleepd = (prevtime+MINTIME - curtime)*1000; |
|
232 sleeptime = ceil(sleepd); |
|
233 errprintf("%f %f %f %f\n",prevtime,curtime,MINTIME,sleepd); |
|
234 } |
|
235 errprintf("Will sleep for %d\n",sleeptime); |
|
236 } |
|
237 return 0; |
|
238 } |
|
239 /* |
|
240 ABCnDEF0 |
|
241 01234567 |
|
242 eolpos = 3 |
|
243 bu = 7 |
|
244 3-0+1 = 4 |
|
245 bu = 3 |
|
246 memmove: |
|
247 01234567 |
|
248 34534567 |
|
249 nDEn |
|
250 */ |