00b212710e63392a610d702523b9eac09c0fc83f
[ardour.git] / gtk2_ardour / system_exec.cc
1 /*
2     Copyright (C) 2010 Paul Davis
3     Author: Robin Gareus <robin@gareus.org>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20 #ifdef WITH_VIDEOTIMELINE
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <unistd.h>
27
28 #ifdef __WIN32__
29 #include <windows.h>
30 #else
31 #include <fcntl.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
34 #include <sys/socket.h>
35 #endif
36
37 #include "system_exec.h"
38
39 using namespace std;
40 void * interposer_thread (void *arg);
41
42 SystemExec::SystemExec (std::string c, std::string a)
43         : cmd(c)
44 {
45         pthread_mutex_init(&write_lock, NULL);
46         thread_active=false;
47         pid = 0;
48         pin[1] = -1;
49         nicelevel = 0;
50         envp = NULL;
51         argp = NULL;
52 #ifdef __WIN32__
53         stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE;
54         stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE;
55         stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE;
56 #endif
57         make_envp();
58         make_argp(a);
59 }
60
61 SystemExec::SystemExec (std::string c, char **a)
62         : cmd(c) , argp(a)
63 {
64         pthread_mutex_init(&write_lock, NULL);
65         thread_active=false;
66         pid = 0;
67         pin[1] = -1;
68         nicelevel = 0;
69         envp = NULL;
70 #ifdef __WIN32__
71         stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE;
72         stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE;
73         stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE;
74         make_wargs(a);
75 #endif
76         make_envp();
77 }
78
79 SystemExec::~SystemExec ()
80 {
81         terminate ();
82         if (envp) {
83                 for (int i=0;envp[i];++i) {
84                   free(envp[i]);
85                 }
86                 free (envp);
87         }
88         if (argp) {
89                 for (int i=0;argp[i];++i) {
90                   free(argp[i]);
91                 }
92                 free (argp);
93         }
94 #ifdef __WIN32__
95         if (w_args) free(w_args);
96 #endif
97         pthread_mutex_destroy(&write_lock);
98 }
99
100 void *
101 interposer_thread (void *arg) {
102         SystemExec *sex = static_cast<SystemExec *>(arg);
103         sex->output_interposer();
104         pthread_exit(0);
105         return 0;
106 }
107
108 #ifdef __WIN32__ /* Windows Process */
109
110 /* HELPER FUNCTIONS */
111
112 static void create_pipe (HANDLE *pipe, bool in) {
113         SECURITY_ATTRIBUTES secAtt = { sizeof( SECURITY_ATTRIBUTES ), NULL, TRUE };
114         HANDLE tmpHandle;
115         if (in) {
116                 if (!CreatePipe(&pipe[0], &tmpHandle, &secAtt, 1024 * 1024)) return;
117                 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), &pipe[1], 0, FALSE, DUPLICATE_SAME_ACCESS)) return;
118         } else {
119                 if (!CreatePipe(&tmpHandle, &pipe[1], &secAtt, 1024 * 1024)) return;
120                 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), &pipe[0], 0, FALSE, DUPLICATE_SAME_ACCESS)) return;
121         }
122         CloseHandle(tmpHandle);
123 }
124
125 static void destroy_pipe (HANDLE pipe[2]) {
126         if (pipe[0] != INVALID_HANDLE_VALUE) {
127                 CloseHandle(pipe[0]);
128                 pipe[0] = INVALID_HANDLE_VALUE;
129         }
130         if (pipe[1] != INVALID_HANDLE_VALUE) {
131                 CloseHandle(pipe[1]);
132                 pipe[1] = INVALID_HANDLE_VALUE;
133         }
134 }
135
136 static BOOL CALLBACK my_terminateApp(HWND hwnd, LPARAM procId)
137 {
138         DWORD currentProcId = 0;
139         GetWindowThreadProcessId(hwnd, &currentProcId);
140         if (currentProcId == (DWORD)procId)
141                 PostMessage(hwnd, WM_CLOSE, 0, 0);
142         return TRUE;
143 }
144
145 /* PROCESS API */
146
147 void
148 SystemExec::make_envp() {
149         ;/* environemt is copied over with CreateProcess(...,env=0 ,..) */
150 }
151
152 void
153 SystemExec::make_wargs(char **a) {
154         std::string wa = cmd;
155         if (cmd[0] != '"' && cmd[cmd.size()] != '"' && strchr(cmd.c_str(), ' ')) { wa = "\"" + cmd + "\""; }
156         std::replace(cmd.begin(), cmd.end(), '/', '\\' );
157         char **tmp = a;
158         while (tmp && *tmp) {
159                 wa.append(" \"");
160                 wa.append(*tmp);
161                 wa.append("\"");
162                 tmp++;
163         }
164         w_args = strdup(wa.c_str());
165 }
166
167 void
168 SystemExec::make_argp(std::string args) {
169         std::string wa = cmd;
170         if (cmd[0] != '"' && cmd[cmd.size()] != '"' && strchr(cmd.c_str(), ' ')) { wa = "\"" + cmd + "\""; }
171         std::replace(cmd.begin(), cmd.end(), '/', '\\' );
172         wa.append(" ");
173         wa.append(args);
174         w_args = strdup(wa.c_str());
175 }
176
177 void
178 SystemExec::terminate ()
179 {
180         ::pthread_mutex_lock(&write_lock);
181         if (pid) {
182                 /* terminate */
183                 EnumWindows(my_terminateApp, (LPARAM)pid->dwProcessId);
184                 PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0);
185
186                 /* kill ! */
187                 TerminateProcess(pid->hProcess, 0xf291);
188
189                 CloseHandle(pid->hThread);
190                 CloseHandle(pid->hProcess);
191                 destroy_pipe(stdinP);
192                 destroy_pipe(stdoutP);
193                 destroy_pipe(stderrP);
194                 delete pid;
195                 pid=0;
196         }
197         ::pthread_mutex_unlock(&write_lock);
198 }
199
200 int
201 SystemExec::wait (int options)
202 {
203         while (is_running()) {
204                 WaitForSingleObject(pid->hProcess, INFINITE);
205                 Sleep(20);
206         }
207         return 0;
208 }
209
210 bool
211 SystemExec::is_running ()
212 {
213         return pid?true:false;
214 }
215
216 int
217 SystemExec::start (int stderr_mode)
218 {
219         char* working_dir = 0;
220
221         if (pid) { return 0; }
222
223         pid = new PROCESS_INFORMATION;
224         memset(pid, 0, sizeof(PROCESS_INFORMATION));
225
226         create_pipe(stdinP, true);
227         create_pipe(stdoutP, false);
228
229         if (stderr_mode == 2) {
230         /* merge stout & stderr */
231                 DuplicateHandle(GetCurrentProcess(), stdoutP[1], GetCurrentProcess(), &stderrP[1], 0, TRUE, DUPLICATE_SAME_ACCESS);
232         } else if (stderr_mode == 1) {
233                 //TODO read/flush this pipe or close it...
234                 create_pipe(stderrP, false);
235         } else {
236                 //TODO: keep stderr of this process mode.
237         }
238
239         bool success = false;
240         STARTUPINFOA startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
241                 (unsigned long)CW_USEDEFAULT, (unsigned long)CW_USEDEFAULT,
242                 (unsigned long)CW_USEDEFAULT, (unsigned long)CW_USEDEFAULT,
243                 0, 0, 0,
244                 STARTF_USESTDHANDLES,
245                 0, 0, 0,
246                 stdinP[0], stdoutP[1], stderrP[1]
247         };
248
249         success = CreateProcess(0, w_args,
250                 0, 0, /* bInheritHandles = */ TRUE,
251                 (CREATE_NO_WINDOW&0) | CREATE_UNICODE_ENVIRONMENT | (0&CREATE_NEW_CONSOLE),
252                 /*env = */ 0,
253                 working_dir,
254                 &startupInfo, pid);
255
256         if (stdinP[0] != INVALID_HANDLE_VALUE) {
257                 CloseHandle(stdinP[0]);
258                 stdinP[0] = INVALID_HANDLE_VALUE;
259         }
260         if (stdoutP[1] != INVALID_HANDLE_VALUE) {
261                 CloseHandle(stdoutP[1]);
262                 stdoutP[1] = INVALID_HANDLE_VALUE;
263         }
264         if (stderrP[1] != INVALID_HANDLE_VALUE) {
265                 CloseHandle(stderrP[1]);
266                 stderrP[1] = INVALID_HANDLE_VALUE;
267         }
268
269         if (!success) {
270                 CloseHandle(pid->hThread);
271                 CloseHandle(pid->hProcess);
272                 destroy_pipe(stdinP);
273                 destroy_pipe(stdoutP);
274                 destroy_pipe(stderrP);
275                 delete pid;
276                 pid=0;
277                 return -1;
278         }
279
280         int rv = pthread_create(&thread_id_tt, NULL, interposer_thread, this);
281         thread_active=true;
282         if (rv) {
283                 thread_active=false;
284                 terminate();
285                 return -2;
286         }
287         Sleep(20);
288         return 0;
289 }
290
291 void
292 SystemExec::output_interposer()
293 {
294         DWORD bytesRead = 0;
295         char data[BUFSIZ];
296         while(1) {
297 #if 0 // for non-blocking pipes..
298                 DWORD bytesAvail = 0;
299                 PeekNamedPipe(stdoutP[0], 0, 0, 0, &bytesAvail, 0);
300                 if (bytesAvail < 1) {Sleep(500); printf("N/A\n"); continue;}
301 #endif
302                 if (stdoutP[0] == INVALID_HANDLE_VALUE) break;
303                 if (!ReadFile(stdoutP[0], data, BUFSIZ, &bytesRead, 0)) break;
304                 if (bytesRead < 1) continue; /* actually not needed; but this is safe. */
305                 data[bytesRead] = 0;
306                 ReadStdout(data, bytesRead);/* EMIT SIGNAL */
307         }
308         Terminated();/* EMIT SIGNAL */
309 }
310
311 void
312 SystemExec::close_stdin()
313 {
314         if (stdinP[0]!= INVALID_HANDLE_VALUE)  FlushFileBuffers(stdinP[0]);
315         if (stdinP[1]!= INVALID_HANDLE_VALUE)  FlushFileBuffers(stdinP[1]);
316         Sleep(200);
317         destroy_pipe(stdinP);
318 }
319
320 int
321 SystemExec::write_to_stdin(std::string d, size_t len)
322 {
323         const char *data;
324         DWORD r,c;
325
326         ::pthread_mutex_lock(&write_lock);
327
328         data=d.c_str();
329         if (len == 0) {
330                 len=(d.length());
331         }
332         c=0;
333         while (c < len) {
334                 if (!WriteFile(stdinP[1], data+c, len-c, &r, NULL)) {
335                         if (GetLastError() == 0xE8 /*NT_STATUS_INVALID_USER_BUFFER*/) {
336                                 Sleep(100);
337                                 continue;
338                         } else {
339                                 fprintf(stderr, "SYSTEM-EXEC: stdin write error.\n");
340                                 break;
341                         }
342                 }
343                 c += r;
344         }
345         ::pthread_mutex_unlock(&write_lock);
346         return c;
347 }
348
349
350 /* end windows process */
351 #else
352 /* UNIX/POSIX process */
353
354 extern char **environ;
355 void
356 SystemExec::make_envp() {
357         int i=0;
358         envp = (char **) calloc(1, sizeof(char*));
359         /* copy current environment */
360         for (i=0;environ[i];++i) {
361           envp[i] = strdup(environ[i]);
362           envp = (char **) realloc(envp, (i+2) * sizeof(char*));
363         }
364         envp[i] = 0;
365 }
366
367 void
368 SystemExec::make_argp(std::string args) {
369         int argn = 1;
370         char *cp1;
371         char *cp2;
372
373         char *carg = strdup(args.c_str());
374
375         argp = (char **) malloc((argn + 1) * sizeof(char *));
376         if (argp == (char **) 0) {
377                 free(carg);
378                 return; // FATAL
379         }
380
381         argp[0] = strdup(cmd.c_str());
382
383         /* TODO: quotations and escapes
384          * http://stackoverflow.com/questions/1511797/convert-string-to-argv-in-c
385          *
386          * It's actually not needed. All relevant invocations specify 'argp' directly.
387          * Only 'xjadeo -L -R' uses this function and that uses neither quotations
388          * nor arguments with white-space.
389          */
390         for (cp1 = cp2 = carg; *cp2 != '\0'; ++cp2) {
391                 if (*cp2 == ' ') {
392                         *cp2 = '\0';
393                         argp[argn++] = strdup(cp1);
394                         cp1 = cp2 + 1;
395             argp = (char **) realloc(argp, (argn + 1) * sizeof(char *));
396                 }
397         }
398         if (cp2 != cp1) {
399                 argp[argn++] = strdup(cp1);
400                 argp = (char **) realloc(argp, (argn + 1) * sizeof(char *));
401         }
402         argp[argn] = (char *) 0;
403         free(carg);
404 }
405
406
407
408 void
409 SystemExec::terminate ()
410 {
411         ::pthread_mutex_lock(&write_lock);
412         close_stdin();
413         if (pid) {
414                 ::usleep(100000);
415                 wait(WNOHANG);
416         }
417
418         if (pid) {
419                 ::fprintf(stderr, "Child process is running. trying SIGTERM\n");
420                 ::kill(pid, SIGTERM);
421                 ::usleep(10000);
422                 wait(WNOHANG);
423         }
424         if (pid) {
425                 ::fprintf(stderr, "Process is still running! trying SIGKILL\n");
426                 ::kill(pid, SIGKILL);
427         }
428
429         wait();
430         if (thread_active) pthread_join(thread_id_tt, NULL);
431         thread_active = false;
432         ::pthread_mutex_unlock(&write_lock);
433 }
434
435 int
436 SystemExec::wait (int options)
437 {
438         int status=0;
439         if (pid==0) return -1;
440         if (pid==::waitpid(pid, &status, options)) {
441                 pid=0;
442         }
443         if (errno == ECHILD) {
444                 pid=0;
445         }
446         return status;
447 }
448
449 bool
450 SystemExec::is_running ()
451 {
452         int status=0;
453         if (pid==0) return false;
454         if (::waitpid(pid, &status, WNOHANG)==0) return true;
455         return false;
456 }
457
458 int
459 SystemExec::start (int stderr_mode)
460 {
461         if (is_running()) {
462                 return 0; // mmh what to return here?
463         }
464         int r;
465
466         if (::pipe(pin) < 0 || ::pipe(pout) < 0 || ::pipe(pok) < 0) {
467                 /* Something unexpected went wrong creating a pipe. */
468                 return -1;
469         }
470
471         r = ::fork();
472         if (r < 0) {
473                 /* failed to fork */
474                 return -2;
475         }
476
477         if (r > 0) {
478                 /* main */
479                 pid=r;
480
481                 /* check if execve was successful. */
482                 ::close(pok[1]);
483                 char buf;
484                 for ( ;; ) {
485                         int n = ::read(pok[0], &buf, 1 );
486                         if ( n==1 ) {
487                                 /* child process returned from execve */
488                                 pid=0;
489                                 ::close(pok[0]);
490                                 ::close(pin[1]);
491                                 ::close(pin[0]);
492                                 ::close(pout[1]);
493                                 ::close(pout[0]);
494                                 pin[1] = -1;
495                                 return -3;
496                         } else if ( n==-1 ) {
497                                  if ( errno==EAGAIN || errno==EINTR )
498                                          continue;
499                         }
500                         break;
501                 }
502                 ::close(pok[0]);
503                 /* child started successfully */
504
505 #if 0
506 /* use fork for output-interposer
507  * it will run in a separated process
508  */
509                 /* catch stdout thread */
510                 r = ::fork();
511                 if (r < 0) {
512                         // failed to fork
513                         terminate();
514                         return -2;
515                 }
516                 if (r == 0) {
517                         /* 2nd child process - catch stdout */
518                         ::close(pin[1]);
519                         ::close(pout[1]);
520                         output_interposer();
521                         exit(0);
522                 }
523                 ::close(pout[1]);
524                 ::close(pin[0]);
525                 ::close(pout[0]);
526 #else /* use pthread */
527                 ::close(pout[1]);
528                 ::close(pin[0]);
529                 int rv = pthread_create(&thread_id_tt, NULL, interposer_thread, this);
530
531                 thread_active=true;
532                 if (rv) {
533                         thread_active=false;
534                         terminate();
535                         return -2;
536                 }
537 #endif
538                 return 0; /* all systems go - return to main */
539         }
540
541         /* child process - exec external process */
542         ::close(pok[0]);
543         ::fcntl(pok[1], F_SETFD, FD_CLOEXEC);
544
545         ::close(pin[1]);
546         if (pin[0] != STDIN_FILENO) {
547           ::dup2(pin[0], STDIN_FILENO);
548         }
549         ::close(pin[0]);
550         ::close(pout[0]);
551         if (pout[1] != STDOUT_FILENO) {
552                 ::dup2(pout[1], STDOUT_FILENO);
553         }
554
555         if (stderr_mode == 2) {
556                 /* merge STDERR into output */
557                 if (pout[1] != STDERR_FILENO) {
558                         ::dup2(pout[1], STDERR_FILENO);
559                 }
560         } else if (stderr_mode == 1) {
561                 /* ignore STDERR */
562                 ::close(STDERR_FILENO);
563         } else {
564                 /* keep STDERR */
565         }
566
567         if (pout[1] != STDOUT_FILENO && pout[1] != STDERR_FILENO) {
568                 ::close(pout[1]);
569         }
570
571         if (nicelevel !=0) {
572                 ::nice(nicelevel);
573         }
574
575 #if 0
576         /* chdir to executable dir */
577         char *directory;
578         directory = strdup(cmd.c_str());
579         if (strrchr(directory, '/') != (char *) 0) {
580                 ::chdir(directory);
581         }
582         free(directory);
583 #endif
584
585 #ifdef HAVE_SIGSET
586         sigset(SIGPIPE, SIG_DFL);
587 #else
588         signal(SIGPIPE, SIG_DFL);
589 #endif
590
591         ::execve(argp[0], argp, envp);
592         /* if we reach here something went wrong.. */
593         char buf = 0;
594         (void) ::write(pok[1], &buf, 1 );
595         (void) ::close(pok[1]);
596         exit(-1);
597         return -1;
598 }
599
600 void
601 SystemExec::output_interposer()
602 {
603         int rfd=pout[0];
604         char buf[BUFSIZ];
605         size_t r;
606         for (;fcntl(rfd, F_GETFL)!=-1;) {
607                 r = read(rfd, buf, sizeof(buf));
608                 if (r < 0 && (errno == EINTR || errno == EAGAIN)) {
609                         ::usleep(1000);
610                         continue;
611                 }
612                 if (r <= 0) {
613                         break;
614                 }
615                 buf[r]=0;
616                 std::string rv = std::string(buf,r); // TODO: check allocation strategy
617                 ReadStdout(rv, r);/* EMIT SIGNAL */
618         }
619         Terminated();/* EMIT SIGNAL */
620 }
621
622 void
623 SystemExec::close_stdin()
624 {
625         if (pin[1]<0) return;
626         ::close(pin[0]);
627         ::close(pin[1]);
628         ::close(pout[0]);
629         ::close(pout[1]);
630         pin[1] = - 1; // mark as closed
631 }
632
633 int
634 SystemExec::write_to_stdin(std::string d, size_t len)
635 {
636         const char *data;
637         size_t r,c;
638         ::pthread_mutex_lock(&write_lock);
639
640         data=d.c_str();
641         if (len == 0) {
642                 len=(d.length());
643         }
644         c=0;
645         while (c < len) {
646                 for (;;) {
647                         r=::write(pin[1], data+c, len-c);
648                         if (r < 0 && (errno == EINTR || errno == EAGAIN)) {
649                                 sleep(1);
650                                 continue;
651                         }
652                         if (r != (len-c)) {
653                                 ::pthread_mutex_unlock(&write_lock);
654                                 return c;
655                         }
656                         break;
657                 }
658                 c += r;
659         }
660         fsync(pin[1]);
661         ::pthread_mutex_unlock(&write_lock);
662         return c;
663 }
664
665 #endif // end UNIX process
666
667 #endif /* WITH_VIDEOTIMELINE */