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