LocalProcess.cpp

Zbigniew Rebacz, 07/19/2014 01:27 PM

Download (12.6 KB)

 
1
#include "Core.h"
2

    
3
NAMESPACE_UPP
4

    
5
#ifdef PLATFORM_POSIX
6
//#BLITZ_APPROVE
7
#include <signal.h>
8
#include <sys/types.h>
9
#include <sys/wait.h>
10
#endif
11

    
12
#define LLOG(x) // DLOG(x)
13

    
14
void LocalProcess::Init() {
15
#ifdef PLATFORM_WIN32
16
        hProcess = hOutputRead = hInputWrite = NULL;
17
#endif
18
#ifdef PLATFORM_POSIX
19
        pid = 0;
20
        rpipe[0] = rpipe[1] = wpipe[0] = wpipe[1] = epipe[0] = epipe[1] = -1;
21
#endif
22
        exit_code = Null;
23
        convertcharset = true;
24
}
25

    
26
void LocalProcess::Free() {
27
#ifdef PLATFORM_WIN32
28
        if(hProcess) {
29
                CloseHandle(hProcess);
30
                hProcess = NULL;
31
        }
32
#endif
33
#ifdef PLATFORM_POSIX
34
        LLOG("\nLocalProcess::Free, pid = " << (int)getpid());
35
        if(pid) waitpid(pid, 0, WNOHANG | WUNTRACED);
36
        pid = 0;
37
#endif
38
        CloseInputAndOutput();
39
}
40

    
41
void LocalProcess::CloseInputAndOutput() {
42
#ifdef PPLATFORM_WIN32
43
        if(hOutputRead) {
44
                CloseHandle(hOutputRead);
45
                hOutputRead = NULL;
46
        }
47
        if(hInputWrite) {
48
                CloseHandle(hInputWrite);
49
                hInputWrite = NULL;
50
        }
51
#endif
52
#ifdef PLATFORM_POSIX
53
        LLOG("rpipe[" << rpipe[0] << ", " << rpipe[1] << "]");
54
        LLOG("wpipe[" << wpipe[0] << ", " << wpipe[1] << "]");
55
        if(rpipe[0] >= 0) { close(rpipe[0]); rpipe[0] = -1; }
56
        if(rpipe[1] >= 0) { close(rpipe[1]); rpipe[1] = -1; }
57
        if(wpipe[0] >= 0) { close(wpipe[0]); wpipe[0] = -1; }
58
        if(wpipe[1] >= 0) { close(wpipe[1]); wpipe[1] = -1; }
59
        if(epipe[0] >= 0) { close(epipe[0]); epipe[0] = -1; }
60
        if(epipe[1] >= 0) { close(epipe[1]); epipe[1] = -1; }
61
#endif
62
}
63

    
64
bool LocalProcess::DoStart(const char *command, bool spliterr, const char *envptr)
65
{
66
        LLOG("LocalProcess::Start(\"" << command << "\")");
67

    
68
        Kill();
69

    
70
        while(*command && (byte)*command <= ' ')
71
                command++;
72

    
73
#ifdef PLATFORM_WIN32
74
        HANDLE hOutputReadTmp, hInputRead;
75
        HANDLE hInputWriteTmp, hOutputWrite;
76
        HANDLE hErrorWrite;
77
        SECURITY_ATTRIBUTES sa;
78

    
79
        sa.nLength = sizeof(SECURITY_ATTRIBUTES);
80
        sa.lpSecurityDescriptor = NULL;
81
        sa.bInheritHandle = TRUE;
82

    
83
        HANDLE hp = GetCurrentProcess();
84

    
85
        ASSERT(!spliterr); //spliterr NOT IMPLEMENTED (yet)
86

    
87
        CreatePipe(&hOutputReadTmp, &hOutputWrite, &sa, 0);
88
        DuplicateHandle(hp, hOutputWrite, hp, &hErrorWrite, 0, TRUE, DUPLICATE_SAME_ACCESS);
89
        CreatePipe(&hInputRead, &hInputWriteTmp, &sa, 0);
90
        DuplicateHandle(hp, hOutputReadTmp, hp, &hOutputRead, 0, FALSE, DUPLICATE_SAME_ACCESS);
91
        DuplicateHandle(hp, hInputWriteTmp, hp, &hInputWrite, 0, FALSE, DUPLICATE_SAME_ACCESS);
92
        CloseHandle(hOutputReadTmp);
93
        CloseHandle(hInputWriteTmp);
94

    
95
        PROCESS_INFORMATION pi;
96
        STARTUPINFO si;
97
        ZeroMemory(&si, sizeof(STARTUPINFO));
98
        si.cb = sizeof(STARTUPINFO);
99
        si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
100
        si.wShowWindow = SW_HIDE;
101
        si.hStdInput  = hInputRead;
102
        si.hStdOutput = hOutputWrite;
103
        si.hStdError  = hErrorWrite;
104
        int n = (int)strlen(command) + 1;
105
        Buffer<char> cmd(n);
106
        memcpy(cmd, command, n);
107
        bool h = CreateProcess(NULL, cmd, &sa, &sa, TRUE,
108
                               NORMAL_PRIORITY_CLASS, (void *)envptr, NULL, &si, &pi);
109
        LLOG("CreateProcess " << (h ? "succeeded" : "failed"));
110
        CloseHandle(hErrorWrite);
111
        CloseHandle(hInputRead);
112
        CloseHandle(hOutputWrite);
113
        if(h) {
114
                hProcess = pi.hProcess;
115
                CloseHandle(pi.hThread);
116
        }
117
        else {
118
                Free();
119
                return false;
120
//                throw Exc(NFormat("Error running process: %s\nCommand: %s", GetErrorMessage(GetLastError()), command));
121
        }
122
        return true;
123
#endif
124
#ifdef PLATFORM_POSIX
125
        // parse command line for execve
126
        cmd_buf.Alloc(strlen(command) + 1);
127
        char *cmd_out = cmd_buf;
128
        const char *p = command;
129
        const char *b = p;
130
        while(*p && (byte)*p > ' ')
131
                if(*p++ == '\"')
132
                        while(*p && *p++ != '\"')
133
                                ;
134
        const char *app = cmd_out;
135
        args.Add(cmd_out);
136
        memcpy(cmd_out, b, p - b);
137
        cmd_out += p - b;
138
        *cmd_out++ = '\0';
139

    
140
        while(*p)
141
                if((byte)*p <= ' ')
142
                        p++;
143
                else {
144
                        args.Add(cmd_out);
145
                        while(*p && (byte)*p > ' ') {
146
                                int c = *p;
147
                                if(c == '\\') {
148
                                        if(*++p)
149
                                                *cmd_out++ = *p++;
150
                                }
151
                                else if(c == '\"' || c == '\'') {
152
                                        p++;
153
                                        while(*p && *p != c)
154
                                                if(*p == '\\') {
155
                                                        if(*++p)
156
                                                                *cmd_out++ = *p++;
157
                                                }
158
                                                else
159
                                                        *cmd_out++ = *p++;
160
                                        if(*p == c)
161
                                                p++;
162
                                }
163
                                else
164
                                        *cmd_out++ = *p++;
165
                        }
166
                        *cmd_out++ = '\0';
167
                }
168

    
169
        args.Add(NULL);
170

    
171
        String app_full = GetFileOnPath(app, getenv("PATH"), true);
172
        if(IsNull(app_full))
173
                return false;
174

    
175
        if(pipe(rpipe) || pipe(wpipe))
176
                return false;
177

    
178
        if(spliterr && pipe(epipe))
179
                return false;
180

    
181
        LLOG("\nLocalProcess::Start");
182
        LLOG("rpipe[" << rpipe[0] << ", " << rpipe[1] << "]");
183
        LLOG("wpipe[" << wpipe[0] << ", " << wpipe[1] << "]");
184
        LLOG("epipe[" << epipe[0] << ", " << epipe[1] << "]");
185

    
186
#ifdef CPU_BLACKFIN
187
        pid = vfork(); //we *can* use vfork here, since exec is done later or the parent will exit
188
#else
189
        pid = fork();
190
#endif
191
        LLOG("\tfork, pid = " << (int)pid << ", getpid = " << (int)getpid());
192
        if(pid < 0)
193
                return false;
194
//                throw Exc(NFormat(t_("fork() error; error code = %d"), errno));
195

    
196
        if(pid) {
197
                LLOG("parent process - continue");
198
                close(rpipe[0]); rpipe[0]=-1;
199
                close(wpipe[1]); wpipe[1]=-1;
200
                if (spliterr) {
201
                        close(epipe[1]); epipe[1]=-1;
202
                }
203
                return true;
204
        }
205
        LLOG("child process - execute application");
206
//        rpipe[1] = wpipe[0] = -1;
207
        dup2(rpipe[0], 0);
208
        dup2(wpipe[1], 1);
209
        dup2(spliterr ? epipe[1] : wpipe[1], 2);
210
        close(rpipe[0]);
211
        close(rpipe[1]);
212
        close(wpipe[0]);
213
        close(wpipe[1]);
214
        if (spliterr) {
215
                close(epipe[0]);
216
                close(epipe[1]);
217
        }
218
        rpipe[0] = rpipe[1] = wpipe[0] = wpipe[1] = epipe[0] = epipe[1] = -1;
219

    
220
#if DO_LLOG
221
        LLOG(args.GetCount() << "arguments:");
222
        for(int a = 0; a < args.GetCount(); a++)
223
                LLOG("[" << a << "]: <" << (args[a] ? args[a] : "NULL") << ">");
224
#endif//DO_LLOG
225

    
226
        LLOG("running execve, app = " << app << ", #args = " << args.GetCount());
227
        if(envptr) {
228
                const char *from = envptr;
229
                Vector<const char *> env;
230
                while(*from) {
231
                        env.Add(from);
232
                        from += strlen(from) + 1;
233
                }
234
                env.Add(NULL);
235
                execve(app_full, args.Begin(), (char *const *)env.Begin());
236
        }
237
        else
238
                execv(app_full, args.Begin());
239
        LLOG("execve failed, errno = " << errno);
240
//        printf("Error running '%s', error code %d\n", command, errno);
241
        exit(-errno);
242
        return true;
243
#endif
244
}
245

    
246
#ifdef PLATFORM_POSIX
247
bool LocalProcess::DecodeExitCode(int status)
248
{
249
        if(WIFEXITED(status)) {
250
                exit_code = (byte)WEXITSTATUS(status);
251
                return true;
252
        }
253
        else if(WIFSIGNALED(status) || WIFSTOPPED(status)) {
254
                static const struct {
255
                        const char *name;
256
                        int         code;
257
                }
258
                signal_map[] = {
259
#define SIGDEF(s) { #s, s },
260
SIGDEF(SIGHUP) SIGDEF(SIGINT) SIGDEF(SIGQUIT) SIGDEF(SIGILL) SIGDEF(SIGABRT)
261
SIGDEF(SIGFPE) SIGDEF(SIGKILL) SIGDEF(SIGSEGV) SIGDEF(SIGPIPE) SIGDEF(SIGALRM)
262
SIGDEF(SIGPIPE) SIGDEF(SIGTERM) SIGDEF(SIGUSR1) SIGDEF(SIGUSR2) SIGDEF(SIGTRAP)
263
SIGDEF(SIGURG) SIGDEF(SIGVTALRM) SIGDEF(SIGXCPU) SIGDEF(SIGXFSZ) SIGDEF(SIGIOT)
264
SIGDEF(SIGIO) SIGDEF(SIGWINCH)
265
#ifndef PLATFORM_BSD
266
//SIGDEF(SIGCLD) SIGDEF(SIGPWR)
267
#endif
268
//SIGDEF(SIGSTKFLT) SIGDEF(SIGUNUSED) // not in Solaris, make conditional if needed
269
#undef SIGDEF
270
                };
271

    
272
                int sig = (WIFSIGNALED(status) ? WTERMSIG(status) : WSTOPSIG(status));
273
                exit_code = (WIFSIGNALED(status) ? 1000 : 2000) + sig;
274
                exit_string << "\nProcess " << (WIFSIGNALED(status) ? "terminated" : "stopped") << " on signal " << sig;
275
                for(int i = 0; i < __countof(signal_map); i++)
276
                        if(signal_map[i].code == sig)
277
                        {
278
                                exit_string << " (" << signal_map[i].name << ")";
279
                                break;
280
                        }
281
                exit_string << "\n";
282
                return true;
283
        }
284
        return false;
285
}
286
#endif//PLATFORM_POSIX
287

    
288
void LocalProcess::Kill() {
289
#ifdef PLATFORM_WIN32
290
        if(hProcess && IsRunning()) {
291
                TerminateProcess(hProcess, (DWORD)-1);
292
                exit_code = 255;
293
        }
294
#endif
295
#ifdef PLATFORM_POSIX
296
        if(IsRunning()) {
297
                LLOG("\nLocalProcess::Kill, pid = " << (int)pid);
298
                exit_code = 255;
299
                kill(pid, SIGTERM);
300
                GetExitCode();
301
                int status;
302
                if(pid && waitpid(pid, &status, 0) == pid)
303
                        DecodeExitCode(status);
304
                exit_string = "Child process has been killed.\n";
305
        }
306
#endif
307
        Free();
308
}
309

    
310
void LocalProcess::Detach()
311
{
312
        Free();
313
}
314

    
315
void LocalProcess::WaitForExit()
316
{
317
        if(!IsRunning())
318
                return;
319
        
320
        bool isSuccess = false;
321
#ifdef PLATFORM_POSIX
322
        int status;
323
        if(waitpid(pid, &status, 0) == pid) {
324
                isSuccess = true;
325
                pid = 0;
326
        }
327
#endif
328
#ifdef PLATFORM_WIN32
329
        if(WaitForSingleObject(hProcess, INFINITE)) {
330
                isSuccess = true;
331
                hProcess = NULL;
332
        }
333
#endif
334
        if(isSuccess)
335
                CloseInputAndOutput();
336
}
337

    
338
bool LocalProcess::IsRunning() {
339
#ifdef PLATFORM_WIN32
340
        dword exitcode;
341
        if(!hProcess)
342
                return false;
343
        if(GetExitCodeProcess(hProcess, &exitcode) && exitcode == STILL_ACTIVE)
344
                return true;
345
        dword n;
346
        if(PeekNamedPipe(hOutputRead, NULL, 0, NULL, &n, NULL) && n)
347
                return true;
348
        exit_code = exitcode;
349
        return false;
350
#endif
351
#ifdef PLATFORM_POSIX
352
        if(!pid || !IsNull(exit_code)) {
353
                LLOG("IsRunning() -> no");
354
                return false;
355
        }
356
        int status = 0, wp;
357
        if(!( (wp = waitpid(pid, &status, WNOHANG | WUNTRACED)) == pid && 
358
              DecodeExitCode(status) ))
359
                return true;
360
        LLOG("IsRunning() -> no, just exited, exit code = " << exit_code);
361
        return false;
362
#endif
363
}
364

    
365
int  LocalProcess::GetExitCode() {
366
#ifdef PLATFORM_WIN32
367
        return IsRunning() ? (int)Null : exit_code;
368
#endif
369
#ifdef PLATFORM_POSIX
370
        if(!IsRunning())
371
                return Nvl(exit_code, -1);
372
        int status;
373
        if(!( waitpid(pid, &status, WNOHANG | WUNTRACED) == pid && 
374
              DecodeExitCode(status) ))
375
                return -1;
376
        LLOG("GetExitCode() -> " << exit_code << " (just exited)");
377
        return exit_code;
378
#endif
379
}
380

    
381
String LocalProcess::GetExitMessage() {
382
#ifdef PLATFORM_POSIX
383
        if (!IsRunning() && GetExitCode() == -1)
384
                return exit_string;
385
        else
386
#endif
387
                return String();
388
}
389

    
390
bool LocalProcess::Read(String& res) {
391
#ifdef PLATFORM_POSIX
392
        return Read2(res, res);
393
#endif
394
#ifdef PLATFORM_WIN32
395
        LLOG("LocalProcess::Read");
396
        res = Null;
397
        if(!hOutputRead) return false;
398
        dword n;
399
        if(!PeekNamedPipe(hOutputRead, NULL, 0, NULL, &n, NULL) || n == 0)
400
                return IsRunning();
401
        char buffer[1024];
402
        if(!ReadFile(hOutputRead, buffer, sizeof(buffer), &n, NULL))
403
                return false;
404
        res = String(buffer, n);
405
        if(convertcharset)
406
                res = FromSystemCharset(res);
407
        return true;
408
#endif
409
}
410

    
411
bool LocalProcess::Read2(String& reso, String& rese)
412
{
413
        LLOG("LocalProcess::Read2");
414
        String res[2] = {Null, Null};
415
#ifdef PLATFORM_WIN32
416
        NEVER(); //Not implemented
417
        return false;
418
#endif
419
#ifdef PLATFORM_POSIX
420
        bool was_running = IsRunning() || wpipe[0] >= 0 || epipe[0] >= 0;
421
        for (int wp=0; wp<2;wp++) {
422
                int *pipe = wp ? epipe : wpipe;
423
                if (pipe[0] < 0) {
424
                        LLOG("Pipe["<<wp<<"] closed");
425
                        continue;
426
                }
427
                fd_set set[1];
428
                FD_ZERO(set);
429
                FD_SET(pipe[0], set);
430
                timeval tval = { 0, 0 };
431
                int sv;
432
                while((sv = select(pipe[0]+1, set, NULL, NULL, &tval)) > 0) {
433
                        LLOG("Read() -> select");
434
                        char buffer[1024];
435
                        int done = read(pipe[0], buffer, sizeof(buffer));
436
                        LLOG("Read(), read -> " << done << ": " << String(buffer, done));
437
                        if(done > 0)
438
                                res[wp].Cat(buffer, done);
439
                        else if (done == 0) {
440
                                close(pipe[0]);
441
                                pipe[0] = -1;
442
                        }
443
                }
444
                LLOG("Pipe["<<wp<<"]=="<<pipe[0]<<" sv:"<<sv);
445
                if(sv < 0) {
446
                        LLOG("select -> " << sv);
447
                }
448
        }
449
        reso = Null;
450
        rese = Null;
451
        if(convertcharset) {
452
                reso << FromSystemCharset(res[0]);
453
                rese << FromSystemCharset(res[1]);
454
        } else {
455
                reso << res[0];
456
                rese << res[1];
457
        }
458
        return !IsNull(res[0]) || !IsNull(res[1]) || was_running;
459
#endif
460
}
461

    
462
void LocalProcess::Write(String s)
463
{
464
        if(convertcharset)
465
                s = ToSystemCharset(s);
466
#ifdef PLATFORM_WIN32
467
        dword n;
468
        if (hInputWrite)
469
                WriteFile(hInputWrite, s, s.GetLength(), &n, NULL);
470
#endif
471
#ifdef PLATFORM_POSIX
472
        if (rpipe[1] >= 0) {
473
                int ret=1;
474
                for (int wn=0; (ret>0 || errno==EINTR) && wn<s.GetLength(); wn+=ret) {
475
                        ret = write(rpipe[1], ~s + wn, s.GetLength() - wn);
476
                }
477
        }
478
#endif
479
}
480

    
481
void LocalProcess::CloseRead()
482
{
483
#ifdef PLATFORM_WIN32
484
        if(hOutputRead) {
485
                CloseHandle(hOutputRead);
486
                hOutputRead = NULL;
487
        }
488
#endif
489
#ifdef PLATFORM_POSIX
490
        if (wpipe[0] >= 0) {
491
                close(wpipe[0]);
492
                wpipe[0]=-1;
493
        }
494
#endif
495
}
496

    
497
void LocalProcess::CloseWrite()
498
{
499
#ifdef PLATFORM_WIN32
500
        if(hInputWrite) {
501
                CloseHandle(hInputWrite);
502
                hInputWrite = NULL;
503
        }
504
#endif
505
#ifdef PLATFORM_POSIX
506
        if (rpipe[1] >= 0) {
507
                close(rpipe[1]);
508
                rpipe[1]=-1;
509
        }
510
#endif
511
}
512

    
513
int Sys(const char *cmd, String& out, bool convertcharset)
514
{
515
        out.Clear();
516
        LocalProcess p;
517
        p.ConvertCharset(convertcharset);
518
        if(!p.Start(cmd))
519
                return -1;
520
        while(p.IsRunning()) {
521
                out.Cat(p.Get());
522
                Sleep(1); // p.Wait would be much better here!
523
        }
524
        out.Cat(p.Get());
525
        return p.GetExitCode();
526
}
527

    
528
String Sys(const char *cmd, bool convertcharset)
529
{
530
        String r;
531
        return Sys(cmd, r, convertcharset) ? String::GetVoid() : r;
532
}
533

    
534
END_UPP_NAMESPACE