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