initial commit
[goodguy/history.git] / cinelerra-5.0 / cinelerra / forkwrapper.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2009 Adam Williams <broadcast at earthling dot net>
5  * 
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * 
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  * 
20  */
21
22
23
24 #include "bcsignals.h"
25 #include "bcwindowbase.inc"
26 #include "forkwrapper.h"
27 #include "format.inc"
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include <sys/wait.h>
35 #include <unistd.h>
36
37 #define EXIT_CODE 0x7fff
38
39
40 ForkWrapper::ForkWrapper()
41 {
42         done = 0;
43         command_data = 0;
44         command_allocated = 0;
45         result_data = 0;
46         result_allocated = 0;
47         parent_fd = 0;
48         child_fd = 0;
49         pid = 0;
50         is_dummy = 0;
51 }
52
53 ForkWrapper::~ForkWrapper()
54 {
55         int status;
56         if(!is_dummy && pid) 
57         {
58                 waitpid(pid, &status, 0);
59         }
60
61         delete [] command_data;
62         if(parent_fd) close(parent_fd);
63         if(child_fd) close(child_fd);
64 }
65
66 void ForkWrapper::start()
67 {
68 // Create the process & socket pair.
69         int sockets[2];
70         socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
71         parent_fd = sockets[0];
72         child_fd = sockets[1];
73
74         pid = fork();
75
76 // Child process
77         if(!pid)
78         {
79 //printf("ForkWrapper::start %d %d\n", __LINE__, getpid());
80                 BC_Signals::reset_locks();
81                 BC_Signals::set_sighup_exit(1);
82                 init_child();
83                 run();
84                 _exit(0);
85         }
86 }
87
88 void ForkWrapper::stop()
89 {
90         send_command(EXIT_CODE, 
91                 0,
92                 0);
93 }
94
95 void ForkWrapper::start_dummy(int parent_fd, int pid)
96 {
97         this->parent_fd = parent_fd;
98         this->pid = pid;
99         is_dummy = 1;
100 }
101
102 void ForkWrapper::init_child()
103 {
104         printf("ForkWrapper::init_child %d\n", __LINE__);
105 }
106
107 int ForkWrapper::handle_command()
108 {
109         printf("ForkWrapper::handle_command %d\n", __LINE__);
110         return 0;
111 }
112
113 void ForkWrapper::run()
114 {
115         int result = 0;
116         const int debug = 0;
117
118         while(!done)
119         {
120                 if(debug) printf("ForkWrapper::run %d this=%p parent_fd=%d child_fd=%d\n", 
121                         __LINE__, this, parent_fd, child_fd);
122
123                 result = read_command();
124
125
126
127                 if(debug) printf("ForkWrapper::run %d this=%p result=%d command_token=%d\n", 
128                         __LINE__, this, result, command_token);
129
130                 if(!result && command_token == EXIT_CODE) 
131                         done = 1;
132                 else
133                 if(!result)
134                 {
135                         handle_command();
136                 }
137         }
138 }
139
140
141 int ForkWrapper::send_command(int token, 
142                 unsigned char *data,
143                 int bytes)
144 {
145         unsigned char buffer[sizeof(int) * 2];
146         this->command_token = token;
147         this->command_bytes = bytes;
148 // printf("ForkWrapper::send_command %d parent_fd=%d token=%d data=%p bytes=%d\n", 
149 // __LINE__, 
150 // parent_fd, 
151 // token,
152 // data,
153 // bytes);
154         int *ibfr = (int *)buffer;
155         ibfr[0] = token;
156         ibfr[1] = bytes;
157         (void)write(parent_fd, buffer, sizeof(buffer));
158         if(data && bytes) (void)write(parent_fd, data, bytes);
159         return 0;
160 }
161
162 int ForkWrapper::read_command()
163 {
164         unsigned char buffer[sizeof(int) * 2];
165 //printf("ForkWrapper::read_command %d child_fd=%d\n", __LINE__, child_fd);
166         (void)read(child_fd, buffer, sizeof(buffer));
167 //printf("ForkWrapper::read_command %d child_fd=%d\n", __LINE__, child_fd);
168         int *ibfr = (int *)buffer;
169         command_token = ibfr[0];
170         command_bytes = ibfr[1];
171
172 //      printf("ForkWrapper::read_command %d command_token=%d command_bytes=%d\n", 
173 //              __LINE__, 
174 //              command_token, 
175 //              command_bytes);
176         if(command_bytes && command_allocated < command_bytes)
177         {
178                 delete [] command_data;
179                 command_data = new unsigned char[command_bytes];
180                 command_allocated = command_bytes;
181         }
182         if(command_bytes) (void)read(child_fd, command_data, command_bytes);
183         return 0;
184 }
185
186 int ForkWrapper::send_result(int64_t value, unsigned char *data, int data_size)
187 {
188         unsigned char buffer[sizeof(int64_t) + sizeof(int)];
189         int64_t *lbfr = (int64_t *)buffer;
190         lbfr[0] = value;
191         int *ibfr = (int *)&lbfr[1];
192         ibfr[0] = data_size;
193         (void)write(child_fd, buffer, sizeof(buffer));
194         if(data && data_size) (void)write(child_fd, data, data_size);
195         return 0;
196 }
197
198 // Return 1 if the child is dead
199 int ForkWrapper::read_timeout(unsigned char *data, int size)
200 {
201         fd_set rfds;
202         struct timeval timeout_struct;
203         int bytes_read = 0;
204
205 // Poll child status while doing timed reads
206         while(bytes_read < size)
207         {
208                 timeout_struct.tv_sec = 1;
209                 timeout_struct.tv_usec = 0;
210                 FD_ZERO(&rfds);
211                 FD_SET(parent_fd, &rfds);
212                 int result = select(parent_fd + 1, &rfds, 0, 0, &timeout_struct);
213
214                 if(result <= 0 && !child_running()) return 1;
215
216
217                 if(result > 0)
218                 {
219                         int fragment = read(parent_fd, data + bytes_read, size - bytes_read);
220                         if(fragment > 0) bytes_read += fragment;
221                 }
222         }
223
224         return 0;
225 }
226
227 int64_t ForkWrapper::read_result()
228 {
229         unsigned char buffer[sizeof(int64_t) + sizeof(int)];
230 //printf("ForkWrapper::read_result %d  parent_fd=%d\n", __LINE__, parent_fd);
231
232         if(read_timeout(buffer, sizeof(buffer))) return 1;
233 //printf("ForkWrapper::read_result %d  parent_fd=%d\n", __LINE__, parent_fd);
234         int64_t *lbfr = (int64_t *)buffer;
235         int64_t result = lbfr[0];
236         int *ibfr = (int *)&lbfr[1];
237         result_bytes = ibfr[0];
238
239         if(result_bytes && result_allocated < result_bytes)
240         {
241                 delete [] result_data;
242                 result_data = new unsigned char[result_bytes];
243                 result_allocated = result_bytes;
244         }
245 //printf("ForkWrapper::read_result %d  parent_fd=%d result=" _LD " result_bytes=%d\n", 
246 //__LINE__, 
247 //parent_fd,
248 //result,
249 //result_bytes);
250
251         if(result_bytes) 
252                 if(read_timeout(result_data, result_bytes)) return 1;
253 //printf("ForkWrapper::read_result %d  parent_fd=%d\n", __LINE__, parent_fd);
254
255         return result;
256 }
257
258 // return 1 if the child is running
259 int ForkWrapper::child_running()
260 {
261         char string[BCTEXTLEN];
262         sprintf(string, "/proc/%d/stat", pid);
263         FILE *fd = fopen(string, "r");
264         if(fd)
265         {
266                 while(!feof(fd) && fgetc(fd) != ')')
267                         ;
268
269                 fgetc(fd);
270                 int status = fgetc(fd);
271
272 //printf("ForkWrapper::child_running '%c'\n", status);
273                 fclose(fd);
274                 if(status == 'Z') 
275                 {
276                         printf("ForkWrapper::child_running %d: process %d dead\n", __LINE__, pid);
277                         return 0;
278                 }
279                 else
280                         return 1;
281         }
282         else
283         {
284                 printf("ForkWrapper::child_running %d: process %d not found\n", __LINE__, pid);
285                 return 0;
286         }
287 }
288
289
290 // From libancillary
291 #define ANCIL_FD_BUFFER(n) \
292 struct { \
293         struct cmsghdr h; \
294         int fd[n]; \
295 }
296
297 void ForkWrapper::send_fd(int fd)
298 {
299         ANCIL_FD_BUFFER(1) buffer;
300     struct msghdr msghdr;
301     char nothing = '!';
302     struct iovec nothing_ptr;
303     struct cmsghdr *cmsg;
304
305     nothing_ptr.iov_base = &nothing;
306     nothing_ptr.iov_len = 1;
307     msghdr.msg_name = NULL;
308     msghdr.msg_namelen = 0;
309     msghdr.msg_iov = &nothing_ptr;
310     msghdr.msg_iovlen = 1;
311     msghdr.msg_flags = 0;
312     msghdr.msg_control = &buffer;
313     msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int);
314     cmsg = CMSG_FIRSTHDR(&msghdr);
315     cmsg->cmsg_len = msghdr.msg_controllen;
316     cmsg->cmsg_level = SOL_SOCKET;
317     cmsg->cmsg_type = SCM_RIGHTS;
318     int *ifd = (int *)CMSG_DATA(cmsg);
319     *ifd = fd;
320     sendmsg(child_fd, &msghdr, 0);
321 }
322
323 int ForkWrapper::get_fd()
324 {
325         ANCIL_FD_BUFFER(1) buffer;
326     struct msghdr msghdr;
327     char nothing;
328     struct iovec nothing_ptr;
329     struct cmsghdr *cmsg;
330
331     nothing_ptr.iov_base = &nothing;
332     nothing_ptr.iov_len = 1;
333     msghdr.msg_name = NULL;
334     msghdr.msg_namelen = 0;
335     msghdr.msg_iov = &nothing_ptr;
336     msghdr.msg_iovlen = 1;
337     msghdr.msg_flags = 0;
338     msghdr.msg_control = &buffer;
339     msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int);
340     cmsg = CMSG_FIRSTHDR(&msghdr);
341     cmsg->cmsg_len = msghdr.msg_controllen;
342     cmsg->cmsg_level = SOL_SOCKET;
343     cmsg->cmsg_type = SCM_RIGHTS;
344     int *ifd = (int *)CMSG_DATA(cmsg);
345     *ifd = -1;
346     
347     if(recvmsg(parent_fd, &msghdr, 0) < 0) return(-1);
348     return *ifd;
349 }
350
351
352