Diffstat (limited to 'noncore/applets/keyhelper/keyhelperapplet/anylnk/ProcessInvoker.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | noncore/applets/keyhelper/keyhelperapplet/anylnk/ProcessInvoker.cpp | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/noncore/applets/keyhelper/keyhelperapplet/anylnk/ProcessInvoker.cpp b/noncore/applets/keyhelper/keyhelperapplet/anylnk/ProcessInvoker.cpp new file mode 100644 index 0000000..09605bd --- a/dev/null +++ b/noncore/applets/keyhelper/keyhelperapplet/anylnk/ProcessInvoker.cpp | |||
@@ -0,0 +1,424 @@ | |||
1 | #include "ProcessInvoker.h" | ||
2 | |||
3 | static ProcessInvoker* g_this; | ||
4 | /* ------------------------------------------------------------------------ */ | ||
5 | /* static functions */ | ||
6 | /* ------------------------------------------------------------------------ */ | ||
7 | |||
8 | static Sigfunc* setSignalHandler(int signo, Sigfunc* handler) | ||
9 | { | ||
10 | struct sigaction act,oact; | ||
11 | |||
12 | act.sa_handler = handler; | ||
13 | ::sigemptyset(&act.sa_mask); | ||
14 | act.sa_flags = 0; | ||
15 | #ifdefSA_RESTART | ||
16 | act.sa_flags |= SA_RESTART; | ||
17 | #endif | ||
18 | if(::sigaction(signo, &act, &oact) < 0){ | ||
19 | return(NULL); | ||
20 | } | ||
21 | return(oact.sa_handler); | ||
22 | } | ||
23 | |||
24 | static void childHandler(int /*signo*/) | ||
25 | { | ||
26 | pid_t pid; | ||
27 | int status; | ||
28 | while((pid = ::waitpid(-1, &status, WNOHANG)) > 0){ | ||
29 | if(pid == g_this->m_child){ | ||
30 | g_this->notifyFinish(status); | ||
31 | } | ||
32 | } | ||
33 | } | ||
34 | |||
35 | /* ------------------------------------------------------------------------ */ | ||
36 | /* ProcessInvoker Class : parent process */ | ||
37 | /* ------------------------------------------------------------------------ */ | ||
38 | ProcessInvoker::ProcessInvoker() | ||
39 | { | ||
40 | g_this = this; | ||
41 | m_isRunning = false; | ||
42 | m_child = 0; | ||
43 | m_defChildHandler = SIG_DFL; | ||
44 | m_pTimer = new QTimer(this); | ||
45 | m_stdfd[0] = m_stdfd[1] = -1; | ||
46 | m_errfd[0] = m_errfd[1] = -1; | ||
47 | connect(m_pTimer, SIGNAL(timeout()), | ||
48 | this, SLOT(readOutputs())); | ||
49 | } | ||
50 | |||
51 | ProcessInvoker::~ProcessInvoker() | ||
52 | { | ||
53 | qDebug("ProcessInvoker::~ProcessInvoker()"); | ||
54 | } | ||
55 | |||
56 | bool ProcessInvoker::openPipe() | ||
57 | { | ||
58 | if(m_stdfd[0] >= 0) closePipe(m_stdfd, 0); | ||
59 | if(m_stdfd[1] >= 0) closePipe(m_stdfd, 1); | ||
60 | if(::pipe(m_stdfd) < 0){ | ||
61 | return(false); | ||
62 | } | ||
63 | if(m_errfd[0] >= 0) closePipe(m_errfd, 0); | ||
64 | if(m_errfd[1] >= 0) closePipe(m_errfd, 1); | ||
65 | if(::pipe(m_errfd) < 0){ | ||
66 | closePipe(m_stdfd); | ||
67 | return(false); | ||
68 | } | ||
69 | m_maxfdp1 = m_stdfd[0]; | ||
70 | if(m_errfd[0] > m_maxfdp1){ | ||
71 | m_maxfdp1 = m_errfd[0]; | ||
72 | } | ||
73 | m_maxfdp1++; | ||
74 | return(true); | ||
75 | } | ||
76 | |||
77 | void ProcessInvoker::closePipe(int fd[], int n) | ||
78 | { | ||
79 | if(fd == NULL){ | ||
80 | closePipe(m_stdfd, n); | ||
81 | closePipe(m_errfd, n); | ||
82 | } else { | ||
83 | if(n != 1 && fd[0] >= 0){ | ||
84 | ::close(fd[0]); | ||
85 | fd[0] = -1; | ||
86 | } | ||
87 | if(n != 0 && fd[1] >= 0){ | ||
88 | ::close(fd[1]); | ||
89 | fd[1] = -1; | ||
90 | } | ||
91 | } | ||
92 | } | ||
93 | |||
94 | void ProcessInvoker::setRunning(int pid) | ||
95 | { | ||
96 | m_child = pid; | ||
97 | m_isRunning = true; | ||
98 | } | ||
99 | |||
100 | bool ProcessInvoker::run(const QString& args) | ||
101 | { | ||
102 | //setArguments(KHUtil::parseArgs(args)); | ||
103 | setArguments(StringParser::split(' ', args)); | ||
104 | return(run()); | ||
105 | } | ||
106 | |||
107 | bool ProcessInvoker::run() | ||
108 | { | ||
109 | if(m_isRunning){ | ||
110 | return(false); | ||
111 | } | ||
112 | m_isRunning = true; | ||
113 | if(m_arguments.isEmpty()){ | ||
114 | m_isRunning = false; | ||
115 | return(false); | ||
116 | } | ||
117 | if(m_arguments[0][0] != '/'){ | ||
118 | m_isRunning = false; | ||
119 | return(false); | ||
120 | } | ||
121 | |||
122 | for(QStringList::Iterator it=m_arguments.begin(); | ||
123 | it!=m_arguments.end(); ++it){ | ||
124 | qDebug("arguments[%s]", (*it).ascii()); | ||
125 | } | ||
126 | |||
127 | /* open pipe */ | ||
128 | if(openPipe() == false){ | ||
129 | m_isRunning = false; | ||
130 | return(false); | ||
131 | } | ||
132 | |||
133 | /* signal handler reset */ | ||
134 | m_defChildHandler = setSignalHandler(SIGCHLD, SIG_DFL); | ||
135 | |||
136 | m_child = ::fork(); | ||
137 | if(m_child < 0){ | ||
138 | /* fork error */ | ||
139 | closePipe(); | ||
140 | setSignalHandler(SIGCHLD, m_defChildHandler); | ||
141 | m_isRunning = false; | ||
142 | return(false); | ||
143 | } else if(m_child == 0){ | ||
144 | /* child process */ | ||
145 | qDebug("child process[%d]", ::getpid()); | ||
146 | m_child = ::getpid(); | ||
147 | //setSignalHandler(SIGCHLD, SIG_DFL); | ||
148 | workerProc(); | ||
149 | /* no return */ | ||
150 | } | ||
151 | /* close pipe(write) */ | ||
152 | closePipe(NULL, 1); | ||
153 | #if 0 | ||
154 | m_pTimer = new QTimer(this); | ||
155 | connect(m_pTimer, SIGNAL(timeout()), | ||
156 | this, SLOT(readOutputs())); | ||
157 | #endif | ||
158 | m_pTimer->start(500); | ||
159 | { | ||
160 | emit start(m_child, m_arguments); | ||
161 | QCopEnvelope e(SC_CHANNEL, "start(int,QStringList)"); | ||
162 | e << m_child << m_arguments; | ||
163 | if(m_isNotify){ | ||
164 | int idx = m_arguments[0].findRev('/'); | ||
165 | notifyStatus(m_arguments[0].mid(idx+1), m_child); | ||
166 | } | ||
167 | } | ||
168 | int status; | ||
169 | if(::waitpid(-1, &status, WNOHANG) > 0){ | ||
170 | qDebug("finish"); | ||
171 | notifyFinish(status); | ||
172 | } else { | ||
173 | /* signal handler set */ | ||
174 | setSignalHandler(SIGCHLD, childHandler); | ||
175 | } | ||
176 | return(true); | ||
177 | } | ||
178 | |||
179 | void ProcessInvoker::terminate() | ||
180 | { | ||
181 | if(m_isRunning && m_child > 0){ | ||
182 | terminate(m_child); | ||
183 | } | ||
184 | } | ||
185 | |||
186 | void ProcessInvoker::terminate(pid_t pid) | ||
187 | { | ||
188 | ::kill(pid, SIGTERM); | ||
189 | } | ||
190 | |||
191 | void ProcessInvoker::kill() | ||
192 | { | ||
193 | if(m_isRunning && m_child > 0){ | ||
194 | kill(m_child); | ||
195 | } | ||
196 | } | ||
197 | |||
198 | void ProcessInvoker::kill(pid_t pid) | ||
199 | { | ||
200 | ::kill(pid, SIGKILL); | ||
201 | } | ||
202 | |||
203 | #if 0 | ||
204 | const QStringList ProcessInvoker::parseArgs(const QString& arguments) | ||
205 | { | ||
206 | QString str; | ||
207 | QStringList args; | ||
208 | char quote = 0; | ||
209 | char c; | ||
210 | for(unsigned int i=0; i<arguments.length(); i++){ | ||
211 | c = arguments[i]; | ||
212 | switch(c){ | ||
213 | case '\"': | ||
214 | if(quote == 0){ | ||
215 | quote = c; | ||
216 | } else if(quote == '\"'){ | ||
217 | if(str.length() > 0){ | ||
218 | args.append(str); | ||
219 | } | ||
220 | str = ""; | ||
221 | quote = 0; | ||
222 | } else { | ||
223 | str += c; | ||
224 | } | ||
225 | break; | ||
226 | case '\'': | ||
227 | if(quote == 0){ | ||
228 | quote = c; | ||
229 | } else if(quote == '\''){ | ||
230 | if(str.length() > 0){ | ||
231 | args.append(str); | ||
232 | } | ||
233 | str = ""; | ||
234 | quote = 0; | ||
235 | } else { | ||
236 | str += c; | ||
237 | } | ||
238 | break; | ||
239 | case ' ': | ||
240 | if(quote == 0){ | ||
241 | if(str.length() > 0){ | ||
242 | args.append(str); | ||
243 | str = ""; | ||
244 | } | ||
245 | } else { | ||
246 | str += c; | ||
247 | } | ||
248 | break; | ||
249 | default: | ||
250 | str += c; | ||
251 | break; | ||
252 | } | ||
253 | } | ||
254 | if(str.length() > 0){ | ||
255 | args.append(str); | ||
256 | } | ||
257 | return(args); | ||
258 | } | ||
259 | #endif | ||
260 | |||
261 | void ProcessInvoker::readOutputs() | ||
262 | { | ||
263 | struct timeval tmval; | ||
264 | tmval.tv_sec = 0; | ||
265 | tmval.tv_usec = 0; | ||
266 | fd_set rset; | ||
267 | |||
268 | QByteArray stdBuf, errBuf, resBuf; | ||
269 | QDataStream stdStream(stdBuf, IO_WriteOnly); | ||
270 | QDataStream errStream(errBuf, IO_WriteOnly); | ||
271 | |||
272 | int iRet; | ||
273 | bool running; | ||
274 | char buf[PIPE_BUF+1]; | ||
275 | while(true){ | ||
276 | running = false; | ||
277 | FD_ZERO(&rset); | ||
278 | if(m_stdfd[0] >= 0){ | ||
279 | FD_SET(m_stdfd[0], &rset); | ||
280 | running = true; | ||
281 | } | ||
282 | if(m_errfd[0] >= 0){ | ||
283 | FD_SET(m_errfd[0], &rset); | ||
284 | running = true; | ||
285 | } | ||
286 | if(running == false){ | ||
287 | m_pTimer->stop(); | ||
288 | //delete m_pTimer; | ||
289 | break; | ||
290 | } | ||
291 | |||
292 | if((iRet = ::select(m_maxfdp1, &rset, NULL, NULL, &tmval)) <= 0){ | ||
293 | qDebug("select[%d]", iRet); | ||
294 | break; | ||
295 | } | ||
296 | |||
297 | if(m_stdfd[0] >= 0 && FD_ISSET(m_stdfd[0], &rset)){ | ||
298 | int n = ::read(m_stdfd[0], buf, PIPE_BUF); | ||
299 | if(n > 0){ | ||
300 | stdStream.writeRawBytes(buf, n); | ||
301 | } else { | ||
302 | qDebug("stdout close"); | ||
303 | closePipe(m_stdfd, 0); | ||
304 | } | ||
305 | } | ||
306 | if(m_errfd[0] >= 0 && FD_ISSET(m_errfd[0], &rset)){ | ||
307 | int n = ::read(m_errfd[0], buf, PIPE_BUF); | ||
308 | if(n > 0){ | ||
309 | errStream.writeRawBytes(buf, n); | ||
310 | } else { | ||
311 | qDebug("stderr close"); | ||
312 | closePipe(m_errfd, 0); | ||
313 | } | ||
314 | } | ||
315 | } | ||
316 | |||
317 | if(stdBuf.size() > 0){ | ||
318 | QCopEnvelope e(SC_CHANNEL, "stdout(int,QByteArray)"); | ||
319 | e << m_child << stdBuf; | ||
320 | } | ||
321 | if(errBuf.size() > 0){ | ||
322 | QCopEnvelope e(SC_CHANNEL, "stderr(int,QByteArray)"); | ||
323 | e << m_child << errBuf; | ||
324 | } | ||
325 | if(running == false){ | ||
326 | QCopEnvelope e(SC_CHANNEL, "close(int)"); | ||
327 | e << m_child; | ||
328 | } | ||
329 | } | ||
330 | |||
331 | #if 0 | ||
332 | void ProcessInvoker::waitFinish() | ||
333 | { | ||
334 | int status; | ||
335 | if(::waitpid(m_child, &status, 0) > 0){ | ||
336 | notifyFinish(status); | ||
337 | } else { | ||
338 | notifyFinish(0, false); | ||
339 | } | ||
340 | } | ||
341 | #endif | ||
342 | |||
343 | void ProcessInvoker::notifyFinish(int status, bool success) | ||
344 | { | ||
345 | bool stopped = false; | ||
346 | QString result; | ||
347 | int code; | ||
348 | if(success){ | ||
349 | if(WIFEXITED(status)){ | ||
350 | code = WEXITSTATUS(status); | ||
351 | if(code == 127){ | ||
352 | result = "error"; | ||
353 | } else { | ||
354 | result = "exit"; | ||
355 | } | ||
356 | } else if(WIFSIGNALED(status)){ | ||
357 | result = "terminated"; | ||
358 | code = WTERMSIG(status); | ||
359 | } else if(WIFSTOPPED(status)){ | ||
360 | result = "stopped"; | ||
361 | code = WSTOPSIG(status); | ||
362 | stopped = true; | ||
363 | } else { | ||
364 | /* ¤³¤ì¤Ï̵¤¤¤Ï¤º? */ | ||
365 | result = "error"; | ||
366 | code = -2; | ||
367 | qWarning("ProcessInvoker: unknown status"); | ||
368 | } | ||
369 | } else { | ||
370 | result = "error"; | ||
371 | code = -1; | ||
372 | qWarning("ProcessInvoker: wait error"); | ||
373 | } | ||
374 | emit finish(result, code); | ||
375 | QCopEnvelope e(SC_CHANNEL, "finish(int,QString,int)"); | ||
376 | e << m_child << result << code; | ||
377 | if(m_isNotify){ | ||
378 | notifyStatus(result, code); | ||
379 | setNotify(false); | ||
380 | } | ||
381 | if(stopped == false){ | ||
382 | setSignalHandler(SIGCHLD, m_defChildHandler); | ||
383 | m_isRunning = false; | ||
384 | } | ||
385 | } | ||
386 | |||
387 | void ProcessInvoker::notifyStatus(const QString& result, int code) | ||
388 | { | ||
389 | QString message = QString::number(code); | ||
390 | message.append(":"); | ||
391 | message.append(result); | ||
392 | Global::statusMessage(message); | ||
393 | } | ||
394 | |||
395 | /* ------------------------------------------------------------------------ */ | ||
396 | /* ProcessInvoker Class : child process */ | ||
397 | /* ------------------------------------------------------------------------ */ | ||
398 | void ProcessInvoker::workerProc() | ||
399 | { | ||
400 | closePipe(m_stdfd, 0); | ||
401 | closePipe(m_errfd, 0); | ||
402 | if(m_stdfd[1] != STDOUT_FILENO){ | ||
403 | ::dup2(m_stdfd[1], STDOUT_FILENO); | ||
404 | closePipe(m_stdfd, 1); | ||
405 | } | ||
406 | if(m_errfd[1] != STDERR_FILENO){ | ||
407 | ::dup2(m_errfd[1], STDERR_FILENO); | ||
408 | closePipe(m_errfd, 1); | ||
409 | } | ||
410 | |||
411 | QCString* arglist = new QCString[m_arguments.count()+1]; | ||
412 | const char** argv = new const char*[m_arguments.count()+1]; | ||
413 | unsigned int i; | ||
414 | for(i=0; i<m_arguments.count(); i++){ | ||
415 | //arglist[i] = m_arguments[i].local8Bit(); | ||
416 | arglist[i] = m_arguments[i]; | ||
417 | argv[i] = arglist[i]; | ||
418 | } | ||
419 | argv[i] = 0; | ||
420 | ::execv(argv[0], (char*const*)argv); | ||
421 | delete[] arglist; | ||
422 | delete[] argv; | ||
423 | ::_exit(127); | ||
424 | } | ||