]> git.tdb.fi Git - builder.git/blob - source/externaltask.cpp
643831e993b9b7e8a8d4c2b65f11245c612bba18
[builder.git] / source / externaltask.cpp
1 #include <cstdlib>
2 #include <unistd.h>
3 #include <sys/wait.h>
4 #include <msp/fs/dir.h>
5 #include <msp/io/console.h>
6 #include <msp/io/file.h>
7 #include <msp/io/print.h>
8 #include <msp/time/units.h>
9 #include "externaltask.h"
10
11 using namespace std;
12 using namespace Msp;
13
14 ExternalTask::ExternalTask(const vector<string> &a, const FS::Path &wd):
15         argv(a),
16         work_dir(wd),
17         pid(-1),
18         exit_code(-1),
19         stdout_dest(PASSTHROUGH),
20         stderr_dest(PASSTHROUGH),
21         capture_pipe(0)
22 { }
23
24 ExternalTask::~ExternalTask()
25 {
26         delete capture_pipe;
27 }
28
29 string ExternalTask::get_command() const
30 {
31         string cmd;
32         for(vector<string>::const_iterator i=argv.begin(); i!=argv.end(); ++i)
33         {
34                 if(i!=argv.begin())
35                         cmd += ' ';
36
37                 for(string::const_iterator j=i->begin(); j!=i->end(); ++j)
38                 {
39                         if(*j=='"' || *j=='\'' || *j==' ' || *j=='\\' || *j=='&')
40                                 cmd += '\\';
41                         cmd += *j;
42                 }
43         }
44
45         return cmd;
46 }
47
48 void ExternalTask::start()
49 {
50         if(stdout_dest==CAPTURE || stderr_dest==CAPTURE)
51                 capture_pipe = new IO::Pipe;
52
53         prepare();
54
55         if((pid = fork()))
56         {
57                 if(pid==-1)
58                         exit_code = 1026;
59                 else
60                         exit_code = 0;
61         }
62         else
63         {
64                 vector<const char *> cargv(argv.size()+1);
65                 for(unsigned i=0; i<argv.size(); ++i)
66                         cargv[i] = argv[i].c_str();
67                 cargv.back() = 0;
68
69                 if(stdout_dest==IGNORE || stderr_dest==IGNORE)
70                 {
71                         IO::File devnull("/dev/null", IO::M_WRITE);
72                         if(stdout_dest==IGNORE)
73                                 IO::cout.redirect(devnull);
74                         if(stderr_dest==IGNORE)
75                                 IO::cerr.redirect(devnull);
76                 }
77
78                 if(capture_pipe)
79                 {
80                         if(stdout_dest==CAPTURE)
81                                 IO::cout.redirect(*capture_pipe);
82                         if(stderr_dest==CAPTURE)
83                                 IO::cerr.redirect(*capture_pipe);
84                         delete capture_pipe;
85                 }
86
87                 if(!work_dir.empty())
88                         FS::chdir(work_dir);
89                 execvp(cargv.front(), const_cast<char *const *>(&cargv.front()));
90                 IO::print("Couldn't execute %s\n", argv.front());
91                 exit(1);
92         }
93 }
94
95 Task::Status ExternalTask::check()
96 {
97         return do_wait(false);
98 }
99
100 Task::Status ExternalTask::wait()
101 {
102         return do_wait(true);
103 }
104
105 Task::Status ExternalTask::do_wait(bool block)
106 {
107         if(pid>0)
108         {
109                 int status;
110                 if(waitpid(pid, &status, (block ? 0 : WNOHANG))==pid)
111                 {
112                         if(WIFEXITED(status))
113                                 exit_code = WEXITSTATUS(status);
114                         else if(WIFSIGNALED(status))
115                                 exit_code = 256+WTERMSIG(status);
116                         else
117                                 exit_code = 1025;
118                         pid = 0;
119                 }
120
121                 // Do this after waiting to avoid a race condition
122                 if(capture_pipe)
123                 {
124                         while(IO::poll(*capture_pipe, IO::P_INPUT, Time::zero))
125                         {
126                                 char buf[1024];
127                                 unsigned len = capture_pipe->read(buf, sizeof(buf));
128                                 output.append(buf, len);
129                                 if(len<sizeof(buf))
130                                         break;
131                         }
132                 }
133
134                 if(pid>0)
135                         return RUNNING;
136                 else
137                         signal_finished.emit(!exit_code);
138         }
139
140         return exit_code ? ERROR : SUCCESS;
141 }
142
143 void ExternalTask::set_stdout(Destination d)
144 {
145         stdout_dest = d;
146 }
147
148 void ExternalTask::set_stderr(Destination d)
149 {
150         stderr_dest = d;
151 }
152
153 string ExternalTask::run_and_capture_output(const Arguments &argv, const FS::Path &wd)
154 {
155         ExternalTask task(argv, wd);
156         task.set_stdout(CAPTURE);
157         task.set_stderr(IGNORE);
158         task.start();
159         if(task.wait()!=SUCCESS)
160                 throw runtime_error(format("%s failed", argv.front()));
161         return task.get_output();
162 }