fix ssize_t/size_t confusion
[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
419         if (pid) {
420                 ::usleep(50000);
421                 sched_yield();
422                 wait(WNOHANG);
423         }
424
425         if (pid) {
426                 ::kill(pid, SIGTERM);
427                 ::usleep(50000);
428                 sched_yield();
429                 wait(WNOHANG);
430         }
431         if (pid) {
432                 ::fprintf(stderr, "Process is still running! trying SIGKILL\n");
433                 ::kill(pid, SIGKILL);
434         }
435
436         wait();
437         if (thread_active) pthread_join(thread_id_tt, NULL);
438         thread_active = false;
439         ::pthread_mutex_unlock(&write_lock);
440 }
441
442 int
443 SystemExec::wait (int options)
444 {
445         int status=0;
446         if (pid==0) return -1;
447         if (pid==::waitpid(pid, &status, options)) {
448                 pid=0;
449         }
450         if (errno == ECHILD) {
451                 pid=0;
452         }
453         return status;
454 }
455
456 bool
457 SystemExec::is_running ()
458 {
459         int status=0;
460         if (pid==0) return false;
461         if (::waitpid(pid, &status, WNOHANG)==0) return true;
462         return false;
463 }
464
465 int
466 SystemExec::start (int stderr_mode)
467 {
468         if (is_running()) {
469                 return 0; // mmh what to return here?
470         }
471         int r;
472
473         if (::pipe(pin) < 0 || ::pipe(pout) < 0 || ::pipe(pok) < 0) {
474                 /* Something unexpected went wrong creating a pipe. */
475                 return -1;
476         }
477
478         r = ::fork();
479         if (r < 0) {
480                 /* failed to fork */
481                 return -2;
482         }
483
484         if (r > 0) {
485                 /* main */
486                 pid=r;
487
488                 /* check if execve was successful. */
489                 close_fd(&pok[1]);
490                 char buf;
491                 for ( ;; ) {
492                         ssize_t n = ::read(pok[0], &buf, 1 );
493                         if ( n==1 ) {
494                                 /* child process returned from execve */
495                                 pid=0;
496                                 close_fd(&pok[0]);
497                                 close_fd(&pin[1]);
498                                 close_fd(&pin[0]);
499                                 close_fd(&pout[1]);
500                                 close_fd(&pout[0]);
501                                 pin[1] = -1;
502                                 return -3;
503                         } else if ( n==-1 ) {
504                                  if ( errno==EAGAIN || errno==EINTR )
505                                          continue;
506                         }
507                         break;
508                 }
509                 close_fd(&pok[0]);
510                 /* child started successfully */
511
512 #if 0
513 /* use fork for output-interposer
514  * it will run in a separated process
515  */
516                 /* catch stdout thread */
517                 r = ::fork();
518                 if (r < 0) {
519                         // failed to fork
520                         terminate();
521                         return -2;
522                 }
523                 if (r == 0) {
524                         /* 2nd child process - catch stdout */
525                         close_fd(&pin[1]);
526                         close_fd(&pout[1]);
527                         output_interposer();
528                         exit(0);
529                 }
530                 close_fd(&pout[1]);
531                 close_fd(&pin[0]);
532                 close_fd(&pout[0]);
533 #else /* use pthread */
534                 close_fd(&pout[1]);
535                 close_fd(&pin[0]);
536                 int rv = pthread_create(&thread_id_tt, NULL, interposer_thread, this);
537
538                 thread_active=true;
539                 if (rv) {
540                         thread_active=false;
541                         terminate();
542                         return -2;
543                 }
544 #endif
545                 return 0; /* all systems go - return to main */
546         }
547
548         /* child process - exec external process */
549         close_fd(&pok[0]);
550         ::fcntl(pok[1], F_SETFD, FD_CLOEXEC);
551
552         close_fd(&pin[1]);
553         if (pin[0] != STDIN_FILENO) {
554           ::dup2(pin[0], STDIN_FILENO);
555         }
556         close_fd(&pin[0]);
557         close_fd(&pout[0]);
558         if (pout[1] != STDOUT_FILENO) {
559                 ::dup2(pout[1], STDOUT_FILENO);
560         }
561
562         if (stderr_mode == 2) {
563                 /* merge STDERR into output */
564                 if (pout[1] != STDERR_FILENO) {
565                         ::dup2(pout[1], STDERR_FILENO);
566                 }
567         } else if (stderr_mode == 1) {
568                 /* ignore STDERR */
569                 ::close(STDERR_FILENO);
570         } else {
571                 /* keep STDERR */
572         }
573
574         if (pout[1] != STDOUT_FILENO && pout[1] != STDERR_FILENO) {
575                 close_fd(&pout[1]);
576         }
577
578         if (nicelevel !=0) {
579                 ::nice(nicelevel);
580         }
581
582 #if 0
583         /* chdir to executable dir */
584         char *directory;
585         directory = strdup(cmd.c_str());
586         if (strrchr(directory, '/') != (char *) 0) {
587                 ::chdir(directory);
588         }
589         free(directory);
590 #endif
591
592 #ifdef HAVE_SIGSET
593         sigset(SIGPIPE, SIG_DFL);
594 #else
595         signal(SIGPIPE, SIG_DFL);
596 #endif
597
598         ::execve(argp[0], argp, envp);
599         /* if we reach here something went wrong.. */
600         char buf = 0;
601         (void) ::write(pok[1], &buf, 1 );
602         close_fd(&pok[1]);
603         exit(-1);
604         return -1;
605 }
606
607 void
608 SystemExec::output_interposer()
609 {
610         int rfd=pout[0];
611         char buf[BUFSIZ];
612         ssize_t r;
613         unsigned long l = 1;
614
615   ioctl(rfd, FIONBIO, &l); // set non-blocking I/O
616
617         for (;fcntl(rfd, F_GETFL)!=-1;) {
618                 r = read(rfd, buf, sizeof(buf));
619                 if (r < 0 && (errno == EINTR || errno == EAGAIN)) {
620                         ::usleep(1000);
621                         continue;
622                 }
623                 if (r <= 0) {
624                         break;
625                 }
626                 buf[r]=0;
627                 std::string rv = std::string(buf,r); // TODO: check allocation strategy
628                 ReadStdout(rv, r);/* EMIT SIGNAL */
629         }
630         Terminated();/* EMIT SIGNAL */
631 }
632
633 void
634 SystemExec::close_stdin()
635 {
636         if (pin[1]<0) return;
637         close_fd(&pin[0]);
638         close_fd(&pin[1]);
639         close_fd(&pout[0]);
640         close_fd(&pout[1]);
641 }
642
643 int
644 SystemExec::write_to_stdin(std::string d, size_t len)
645 {
646         const char *data;
647         ssize_t r;
648         size_t c;
649         ::pthread_mutex_lock(&write_lock);
650
651         data=d.c_str();
652         if (len == 0) {
653                 len=(d.length());
654         }
655         c=0;
656         while (c < len) {
657                 for (;;) {
658                         r=::write(pin[1], data+c, len-c);
659                         if (r < 0 && (errno == EINTR || errno == EAGAIN)) {
660                                 sleep(1);
661                                 continue;
662                         }
663                         if ((size_t) r != (len-c)) {
664                                 ::pthread_mutex_unlock(&write_lock);
665                                 return c;
666                         }
667                         break;
668                 }
669                 c += r;
670         }
671         fsync(pin[1]);
672         ::pthread_mutex_unlock(&write_lock);
673         return c;
674 }
675
676 #endif // end UNIX process