Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include "sal/config.h"
21 : #include "rtl/ustring.hxx"
22 :
23 : #include <cassert>
24 :
25 : /*
26 : * ToDo:
27 : * - cleanup of process status things
28 : * - cleanup of process spawning
29 : * - cleanup of resource transfer
30 : */
31 :
32 : #if defined(SOLARIS)
33 : // The procfs may only be used without LFS in 32bits.
34 : # ifdef _FILE_OFFSET_BITS
35 : # undef _FILE_OFFSET_BITS
36 : # endif
37 : #endif
38 :
39 : #if defined(FREEBSD) || defined(NETBSD) || defined(DRAGONFLY)
40 : #include <machine/param.h>
41 : #endif
42 :
43 : #include "system.hxx"
44 : #if defined(SOLARIS)
45 : # include <sys/procfs.h>
46 : #endif
47 : #include <osl/diagnose.h>
48 : #include <osl/mutex.h>
49 : #include <osl/process.h>
50 : #include <osl/conditn.h>
51 : #include <osl/thread.h>
52 : #include <osl/file.h>
53 : #include <osl/file.hxx>
54 : #include <osl/signal.h>
55 : #include <rtl/alloc.h>
56 : #include <sal/log.hxx>
57 :
58 : #include <grp.h>
59 :
60 : #include "createfilehandlefromfd.hxx"
61 : #include "file_url.hxx"
62 : #include "readwrite_helper.hxx"
63 : #include "sockimpl.hxx"
64 : #include "secimpl.hxx"
65 :
66 : #define MAX_ARGS 255
67 : #define MAX_ENVS 255
68 :
69 : namespace
70 : {
71 :
72 : struct oslProcessImpl {
73 : pid_t m_pid;
74 : oslCondition m_terminated;
75 : int m_status;
76 : oslProcessImpl* m_pnext;
77 : };
78 :
79 : struct ProcessData
80 : {
81 : const sal_Char* m_pszArgs[MAX_ARGS + 1];
82 : oslProcessOption m_options;
83 : const sal_Char* m_pszDir;
84 : sal_Char* m_pszEnv[MAX_ENVS + 1];
85 : uid_t m_uid;
86 : gid_t m_gid;
87 : sal_Char* m_name;
88 : oslCondition m_started;
89 : oslProcessImpl* m_pProcImpl;
90 : oslFileHandle *m_pInputWrite;
91 : oslFileHandle *m_pOutputRead;
92 : oslFileHandle *m_pErrorRead;
93 : };
94 :
95 : static oslProcessImpl* ChildList;
96 : static oslMutex ChildListMutex;
97 :
98 : } //Anonymous namespace
99 :
100 : oslProcessError SAL_CALL osl_psz_executeProcess(sal_Char *pszImageName,
101 : sal_Char *pszArguments[],
102 : oslProcessOption Options,
103 : oslSecurity Security,
104 : sal_Char *pszDirectory,
105 : sal_Char *pszEnvironments[],
106 : oslProcess *pProcess,
107 : oslFileHandle *pInputWrite,
108 : oslFileHandle *pOutputRead,
109 : oslFileHandle *pErrorRead );
110 :
111 : /******************************************************************************
112 : *
113 : * Functions for starting a process
114 : *
115 : *****************************************************************************/
116 :
117 : extern "C" {
118 :
119 593 : static void ChildStatusProc(void *pData)
120 : {
121 593 : osl_setThreadName("osl_executeProcess");
122 :
123 593 : pid_t pid = -1;
124 593 : int status = 0;
125 593 : int channel[2] = { -1, -1 };
126 : ProcessData data;
127 : ProcessData *pdata;
128 593 : int stdOutput[2] = { -1, -1 }, stdInput[2] = { -1, -1 }, stdError[2] = { -1, -1 };
129 :
130 593 : pdata = static_cast<ProcessData *>(pData);
131 :
132 : /* make a copy of our data, because forking will only copy
133 : our local stack of the thread, so the process data will not be accessible
134 : in our child process */
135 593 : memcpy(&data, pData, sizeof(data));
136 :
137 : #ifdef NO_CHILD_PROCESSES
138 : #define fork() (errno = EINVAL, -1)
139 : #endif
140 593 : if (socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == -1)
141 : {
142 0 : status = errno;
143 : SAL_WARN("sal.osl", "executeProcess socketpair() errno " << status);
144 : }
145 :
146 593 : (void) fcntl(channel[0], F_SETFD, FD_CLOEXEC);
147 593 : (void) fcntl(channel[1], F_SETFD, FD_CLOEXEC);
148 :
149 : /* Create redirected IO pipes */
150 593 : if ( status == 0 && data.m_pInputWrite && pipe( stdInput ) == -1 )
151 : {
152 0 : status = errno;
153 : assert(status != 0);
154 : SAL_WARN("sal.osl", "executeProcess pipe(stdInput) errno " << status);
155 : }
156 :
157 593 : if ( status == 0 && data.m_pOutputRead && pipe( stdOutput ) == -1 )
158 : {
159 0 : status = errno;
160 : assert(status != 0);
161 : SAL_WARN("sal.osl", "executeProcess pipe(stdOutput) errno " << status);
162 : }
163 :
164 593 : if ( status == 0 && data.m_pErrorRead && pipe( stdError ) == -1 )
165 : {
166 0 : status = errno;
167 : assert(status != 0);
168 : SAL_WARN("sal.osl", "executeProcess pipe(stdError) errno " << status);
169 : }
170 :
171 593 : if ( (status == 0) && ((pid = fork()) == 0) )
172 : {
173 : /* Child */
174 0 : int chstatus = 0;
175 : int errno_copy;
176 :
177 0 : if (channel[0] != -1) close(channel[0]);
178 :
179 0 : if ((data.m_uid != (uid_t)-1) && ((data.m_uid != getuid()) || (data.m_gid != getgid())))
180 : {
181 : OSL_ASSERT(geteuid() == 0); /* must be root */
182 :
183 0 : if (! INIT_GROUPS(data.m_name, data.m_gid) || (setuid(data.m_uid) != 0))
184 : OSL_TRACE("Failed to change uid and guid, errno=%d (%s)", errno, strerror(errno));
185 :
186 0 : const rtl::OUString envVar("HOME");
187 0 : osl_clearEnvironment(envVar.pData);
188 : }
189 :
190 0 : if (data.m_pszDir)
191 0 : chstatus = chdir(data.m_pszDir);
192 :
193 0 : if (chstatus == 0 && ((data.m_uid == (uid_t)-1) || ((data.m_uid == getuid()) && (data.m_gid == getgid()))))
194 : {
195 : int i;
196 0 : for (i = 0; data.m_pszEnv[i] != NULL; i++)
197 : {
198 0 : if (strchr(data.m_pszEnv[i], '=') == NULL)
199 : {
200 0 : unsetenv(data.m_pszEnv[i]); /*TODO: check error return*/
201 : }
202 : else
203 : {
204 0 : putenv(data.m_pszEnv[i]); /*TODO: check error return*/
205 : }
206 : }
207 :
208 : OSL_TRACE("ChildStatusProc : starting '%s'",data.m_pszArgs[0]);
209 :
210 : /* Connect std IO to pipe ends */
211 :
212 : /* Write end of stdInput not used in child process */
213 0 : if (stdInput[1] != -1) close( stdInput[1] );
214 :
215 : /* Read end of stdOutput not used in child process */
216 0 : if (stdOutput[0] != -1) close( stdOutput[0] );
217 :
218 : /* Read end of stdError not used in child process */
219 0 : if (stdError[0] != -1) close( stdError[0] );
220 :
221 : /* Redirect pipe ends to std IO */
222 :
223 0 : if ( stdInput[0] != STDIN_FILENO )
224 : {
225 0 : dup2( stdInput[0], STDIN_FILENO );
226 0 : if (stdInput[0] != -1) close( stdInput[0] );
227 : }
228 :
229 0 : if ( stdOutput[1] != STDOUT_FILENO )
230 : {
231 0 : dup2( stdOutput[1], STDOUT_FILENO );
232 0 : if (stdOutput[1] != -1) close( stdOutput[1] );
233 : }
234 :
235 0 : if ( stdError[1] != STDERR_FILENO )
236 : {
237 0 : dup2( stdError[1], STDERR_FILENO );
238 0 : if (stdError[1] != -1) close( stdError[1] );
239 : }
240 :
241 : // No need to check the return value of execv. If we return from
242 : // it, an error has occurred.
243 0 : execv(data.m_pszArgs[0], const_cast<char **>(data.m_pszArgs));
244 : }
245 :
246 : OSL_TRACE("Failed to exec, errno=%d (%s)", errno, strerror(errno));
247 :
248 : OSL_TRACE("ChildStatusProc : starting '%s' failed",data.m_pszArgs[0]);
249 :
250 : /* if we reach here, something went wrong */
251 0 : errno_copy = errno;
252 0 : if ( !safeWrite(channel[1], &errno_copy, sizeof(errno_copy)) )
253 : OSL_TRACE("sendFdPipe : sending failed (%s)",strerror(errno));
254 :
255 0 : if ( channel[1] != -1 )
256 0 : close(channel[1]);
257 :
258 0 : _exit(255);
259 : }
260 : else
261 : { /* Parent */
262 593 : int i = -1;
263 593 : if (channel[1] != -1) close(channel[1]);
264 :
265 : /* Close unused pipe ends */
266 593 : if (stdInput[0] != -1) close( stdInput[0] );
267 593 : if (stdOutput[1] != -1) close( stdOutput[1] );
268 593 : if (stdError[1] != -1) close( stdError[1] );
269 :
270 593 : if (pid > 0)
271 : {
272 1186 : while (((i = read(channel[0], &status, sizeof(status))) < 0))
273 : {
274 0 : if (errno != EINTR)
275 0 : break;
276 : }
277 : }
278 :
279 593 : if (channel[0] != -1) close(channel[0]);
280 :
281 593 : if ((pid > 0) && (i == 0))
282 : {
283 : pid_t child_pid;
284 591 : osl_acquireMutex(ChildListMutex);
285 :
286 591 : pdata->m_pProcImpl->m_pid = pid;
287 591 : pdata->m_pProcImpl->m_pnext = ChildList;
288 591 : ChildList = pdata->m_pProcImpl;
289 :
290 : /* Store used pipe ends in data structure */
291 :
292 591 : if ( pdata->m_pInputWrite )
293 8 : *(pdata->m_pInputWrite) = osl::detail::createFileHandleFromFD( stdInput[1] );
294 :
295 591 : if ( pdata->m_pOutputRead )
296 148 : *(pdata->m_pOutputRead) = osl::detail::createFileHandleFromFD( stdOutput[0] );
297 :
298 591 : if ( pdata->m_pErrorRead )
299 85 : *(pdata->m_pErrorRead) = osl::detail::createFileHandleFromFD( stdError[0] );
300 :
301 591 : osl_releaseMutex(ChildListMutex);
302 :
303 591 : osl_setCondition(pdata->m_started);
304 :
305 591 : do
306 : {
307 591 : child_pid = waitpid(pid, &status, 0);
308 0 : } while ( 0 > child_pid && EINTR == errno );
309 :
310 591 : if ( child_pid < 0)
311 : {
312 : OSL_TRACE("Failed to wait for child process, errno=%d (%s)", errno, strerror(errno));
313 :
314 : /*
315 : We got an other error than EINTR. Anyway we have to wake up the
316 : waiting thread under any circumstances */
317 :
318 0 : child_pid = pid;
319 : }
320 :
321 591 : if ( child_pid > 0 )
322 : {
323 : oslProcessImpl* pChild;
324 :
325 591 : osl_acquireMutex(ChildListMutex);
326 :
327 591 : pChild = ChildList;
328 :
329 : /* check if it is one of our child processes */
330 1748 : while (pChild != NULL)
331 : {
332 566 : if (pChild->m_pid == child_pid)
333 : {
334 528 : if (WIFEXITED(status))
335 528 : pChild->m_status = WEXITSTATUS(status);
336 0 : else if (WIFSIGNALED(status))
337 0 : pChild->m_status = 128 + WTERMSIG(status);
338 : else
339 0 : pChild->m_status = -1;
340 :
341 528 : osl_setCondition(pChild->m_terminated);
342 : }
343 :
344 566 : pChild = pChild->m_pnext;
345 : }
346 :
347 591 : osl_releaseMutex(ChildListMutex);
348 579 : }
349 : }
350 : else
351 : {
352 : OSL_TRACE("ChildStatusProc : starting '%s' failed",data.m_pszArgs[0]);
353 : OSL_TRACE("Failed to launch child process, child reports errno=%d (%s)", status, strerror(status));
354 :
355 : /* Close pipe ends */
356 2 : if ( pdata->m_pInputWrite )
357 2 : *pdata->m_pInputWrite = NULL;
358 :
359 2 : if ( pdata->m_pOutputRead )
360 2 : *pdata->m_pOutputRead = NULL;
361 :
362 2 : if ( pdata->m_pErrorRead )
363 2 : *pdata->m_pErrorRead = NULL;
364 :
365 2 : if (stdInput[1] != -1) close( stdInput[1] );
366 2 : if (stdOutput[0] != -1) close( stdOutput[0] );
367 2 : if (stdError[0] != -1) close( stdError[0] );
368 :
369 : //if pid > 0 then a process was created, even if it later failed
370 : //e.g. bash searching for a command to execute, and we still
371 : //need to clean it up to avoid "defunct" processes
372 2 : if (pid > 0)
373 : {
374 : pid_t child_pid;
375 2 : do
376 : {
377 2 : child_pid = waitpid(pid, &status, 0);
378 0 : } while ( 0 > child_pid && EINTR == errno );
379 : }
380 :
381 : /* notify (and unblock) parent thread */
382 2 : osl_setCondition(pdata->m_started);
383 : }
384 : }
385 581 : }
386 :
387 : }
388 :
389 593 : oslProcessError SAL_CALL osl_executeProcess_WithRedirectedIO(
390 : rtl_uString *ustrImageName,
391 : rtl_uString *ustrArguments[],
392 : sal_uInt32 nArguments,
393 : oslProcessOption Options,
394 : oslSecurity Security,
395 : rtl_uString *ustrWorkDir,
396 : rtl_uString *ustrEnvironment[],
397 : sal_uInt32 nEnvironmentVars,
398 : oslProcess *pProcess,
399 : oslFileHandle *pInputWrite,
400 : oslFileHandle *pOutputRead,
401 : oslFileHandle *pErrorRead
402 : )
403 : {
404 593 : rtl::OUString image;
405 593 : if (ustrImageName == nullptr)
406 : {
407 0 : if (nArguments == 0)
408 : {
409 0 : return osl_Process_E_InvalidError;
410 : }
411 0 : image = rtl::OUString::unacquired(ustrArguments);
412 : }
413 : else
414 : {
415 : osl::FileBase::RC e = osl::FileBase::getSystemPathFromFileURL(
416 593 : rtl::OUString::unacquired(&ustrImageName), image);
417 593 : if (e != osl::FileBase::E_None)
418 : {
419 : SAL_INFO(
420 : "sal.osl",
421 : "getSystemPathFromFileURL("
422 : << rtl::OUString::unacquired(&ustrImageName)
423 : << ") failed with " << e);
424 0 : return osl_Process_E_Unknown;
425 : }
426 : }
427 :
428 593 : if ((Options & osl_Process_SEARCHPATH) != 0)
429 : {
430 10 : rtl::OUString path;
431 10 : if (osl::detail::find_in_PATH(image, path))
432 : {
433 3 : image = path;
434 10 : }
435 : }
436 :
437 : oslProcessError Error;
438 593 : sal_Char* pszWorkDir=0;
439 593 : sal_Char** pArguments=0;
440 593 : sal_Char** pEnvironment=0;
441 : unsigned int idx;
442 :
443 593 : char szImagePath[PATH_MAX] = "";
444 1186 : if (!image.isEmpty()
445 1186 : && (UnicodeToText(
446 : szImagePath, SAL_N_ELEMENTS(szImagePath), image.getStr(),
447 593 : image.getLength())
448 : == 0))
449 : {
450 0 : int e = errno;
451 : SAL_INFO("sal.osl", "UnicodeToText(" << image << ") failed with " << e);
452 0 : return osl_Process_E_Unknown;
453 : }
454 :
455 593 : char szWorkDir[PATH_MAX] = "";
456 593 : if ( ustrWorkDir != 0 && ustrWorkDir->length )
457 : {
458 3 : oslFileError e = FileURLToPath( szWorkDir, PATH_MAX, ustrWorkDir );
459 3 : if (e != osl_File_E_None)
460 : {
461 : SAL_INFO(
462 : "sal.osl",
463 : "FileURLToPath(" << rtl::OUString::unacquired(&ustrWorkDir)
464 : << ") failed with " << e);
465 0 : return osl_Process_E_Unknown;
466 : }
467 3 : pszWorkDir = szWorkDir;
468 : }
469 :
470 593 : if ( pArguments == 0 && nArguments > 0 )
471 : {
472 592 : pArguments = static_cast<sal_Char**>(malloc( ( nArguments + 2 ) * sizeof(sal_Char*) ));
473 : }
474 :
475 4227 : for ( idx = 0 ; idx < nArguments ; ++idx )
476 : {
477 3634 : rtl_String* strArg =0;
478 :
479 : rtl_uString2String( &strArg,
480 3634 : rtl_uString_getStr(ustrArguments[idx]),
481 3634 : rtl_uString_getLength(ustrArguments[idx]),
482 3634 : osl_getThreadTextEncoding(),
483 10902 : OUSTRING_TO_OSTRING_CVTFLAGS );
484 :
485 3634 : pArguments[idx]=strdup(rtl_string_getStr(strArg));
486 3634 : rtl_string_release(strArg);
487 3634 : pArguments[idx+1]=0;
488 : }
489 :
490 598 : for ( idx = 0 ; idx < nEnvironmentVars ; ++idx )
491 : {
492 5 : rtl_String* strEnv=0;
493 :
494 5 : if ( pEnvironment == 0 )
495 : {
496 2 : pEnvironment = static_cast<sal_Char**>(malloc( ( nEnvironmentVars + 2 ) * sizeof(sal_Char*) ));
497 : }
498 :
499 : rtl_uString2String( &strEnv,
500 5 : rtl_uString_getStr(ustrEnvironment[idx]),
501 5 : rtl_uString_getLength(ustrEnvironment[idx]),
502 5 : osl_getThreadTextEncoding(),
503 15 : OUSTRING_TO_OSTRING_CVTFLAGS );
504 :
505 5 : pEnvironment[idx]=strdup(rtl_string_getStr(strEnv));
506 5 : rtl_string_release(strEnv);
507 5 : pEnvironment[idx+1]=0;
508 : }
509 :
510 : Error = osl_psz_executeProcess(szImagePath,
511 : pArguments,
512 : Options,
513 : Security,
514 : pszWorkDir,
515 : pEnvironment,
516 : pProcess,
517 : pInputWrite,
518 : pOutputRead,
519 : pErrorRead
520 593 : );
521 :
522 593 : if ( pArguments != 0 )
523 : {
524 4226 : for ( idx = 0 ; idx < nArguments ; ++idx )
525 : {
526 3634 : if ( pArguments[idx] != 0 )
527 : {
528 3634 : free(pArguments[idx]);
529 : }
530 : }
531 592 : free(pArguments);
532 : }
533 :
534 593 : if ( pEnvironment != 0 )
535 : {
536 7 : for ( idx = 0 ; idx < nEnvironmentVars ; ++idx )
537 : {
538 5 : if ( pEnvironment[idx] != 0 )
539 : {
540 5 : free(pEnvironment[idx]);
541 : }
542 : }
543 2 : free(pEnvironment);
544 : }
545 :
546 593 : return Error;
547 : }
548 :
549 443 : oslProcessError SAL_CALL osl_executeProcess(
550 : rtl_uString *ustrImageName,
551 : rtl_uString *ustrArguments[],
552 : sal_uInt32 nArguments,
553 : oslProcessOption Options,
554 : oslSecurity Security,
555 : rtl_uString *ustrWorkDir,
556 : rtl_uString *ustrEnvironment[],
557 : sal_uInt32 nEnvironmentVars,
558 : oslProcess *pProcess
559 : )
560 : {
561 : return osl_executeProcess_WithRedirectedIO(
562 : ustrImageName,
563 : ustrArguments,
564 : nArguments,
565 : Options,
566 : Security,
567 : ustrWorkDir,
568 : ustrEnvironment,
569 : nEnvironmentVars,
570 : pProcess,
571 : NULL,
572 : NULL,
573 : NULL
574 443 : );
575 : }
576 :
577 593 : oslProcessError SAL_CALL osl_psz_executeProcess(sal_Char *pszImageName,
578 : sal_Char *pszArguments[],
579 : oslProcessOption Options,
580 : oslSecurity Security,
581 : sal_Char *pszDirectory,
582 : sal_Char *pszEnvironments[],
583 : oslProcess *pProcess,
584 : oslFileHandle *pInputWrite,
585 : oslFileHandle *pOutputRead,
586 : oslFileHandle *pErrorRead
587 : )
588 : {
589 : int i;
590 : ProcessData Data;
591 : oslThread hThread;
592 :
593 593 : memset(&Data,0,sizeof(ProcessData));
594 593 : Data.m_pInputWrite = pInputWrite;
595 593 : Data.m_pOutputRead = pOutputRead;
596 593 : Data.m_pErrorRead = pErrorRead;
597 :
598 : OSL_ASSERT(pszImageName != NULL);
599 :
600 593 : if ( pszImageName == 0 )
601 : {
602 0 : return osl_Process_E_NotFound;
603 : }
604 :
605 593 : Data.m_pszArgs[0] = strdup(pszImageName);
606 593 : Data.m_pszArgs[1] = 0;
607 :
608 593 : if ( pszArguments != 0 )
609 : {
610 4226 : for (i = 0; ((i + 2) < MAX_ARGS) && (pszArguments[i] != NULL); i++)
611 3634 : Data.m_pszArgs[i+1] = strdup(pszArguments[i]);
612 592 : Data.m_pszArgs[i+2] = NULL;
613 : }
614 :
615 593 : Data.m_options = Options;
616 593 : Data.m_pszDir = (pszDirectory != NULL) ? strdup(pszDirectory) : NULL;
617 :
618 593 : if (pszEnvironments != NULL)
619 : {
620 7 : for (i = 0; ((i + 1) < MAX_ENVS) && (pszEnvironments[i] != NULL); i++)
621 5 : Data.m_pszEnv[i] = strdup(pszEnvironments[i]);
622 2 : Data.m_pszEnv[i+1] = NULL;
623 : }
624 : else
625 591 : Data.m_pszEnv[0] = NULL;
626 :
627 593 : if (Security != NULL)
628 : {
629 11 : Data.m_uid = static_cast<oslSecurityImpl*>(Security)->m_pPasswd.pw_uid;
630 11 : Data.m_gid = static_cast<oslSecurityImpl*>(Security)->m_pPasswd.pw_gid;
631 11 : Data.m_name = static_cast<oslSecurityImpl*>(Security)->m_pPasswd.pw_name;
632 : }
633 : else
634 582 : Data.m_uid = (uid_t)-1;
635 :
636 593 : Data.m_pProcImpl = static_cast<oslProcessImpl*>(malloc(sizeof(oslProcessImpl)));
637 593 : Data.m_pProcImpl->m_pid = 0;
638 593 : Data.m_pProcImpl->m_terminated = osl_createCondition();
639 593 : Data.m_pProcImpl->m_pnext = NULL;
640 :
641 593 : if (ChildListMutex == NULL)
642 474 : ChildListMutex = osl_createMutex();
643 :
644 593 : Data.m_started = osl_createCondition();
645 :
646 593 : hThread = osl_createThread(ChildStatusProc, &Data);
647 :
648 593 : if (hThread != 0)
649 : {
650 593 : osl_waitCondition(Data.m_started, NULL);
651 : }
652 593 : osl_destroyCondition(Data.m_started);
653 :
654 4820 : for (i = 0; Data.m_pszArgs[i] != NULL; i++)
655 4227 : free(const_cast<char *>(Data.m_pszArgs[i]));
656 :
657 598 : for (i = 0; Data.m_pszEnv[i] != NULL; i++)
658 5 : free(Data.m_pszEnv[i]);
659 :
660 593 : if ( Data.m_pszDir != 0 )
661 : {
662 3 : free(const_cast<char *>(Data.m_pszDir));
663 : }
664 :
665 593 : osl_destroyThread(hThread);
666 :
667 593 : if (Data.m_pProcImpl->m_pid != 0)
668 : {
669 : assert(hThread != 0);
670 :
671 591 : *pProcess = Data.m_pProcImpl;
672 :
673 591 : if (Options & osl_Process_WAIT)
674 329 : osl_joinProcess(*pProcess);
675 :
676 591 : return osl_Process_E_None;
677 : }
678 :
679 2 : osl_destroyCondition(Data.m_pProcImpl->m_terminated);
680 2 : free(Data.m_pProcImpl);
681 :
682 2 : return osl_Process_E_Unknown;
683 : }
684 :
685 : /******************************************************************************
686 : *
687 : * Functions for processes
688 : *
689 : *****************************************************************************/
690 :
691 0 : oslProcessError SAL_CALL osl_terminateProcess(oslProcess Process)
692 : {
693 0 : if (Process == NULL)
694 0 : return osl_Process_E_Unknown;
695 :
696 0 : if (kill(static_cast<oslProcessImpl*>(Process)->m_pid, SIGKILL) != 0)
697 : {
698 0 : switch (errno)
699 : {
700 : case EPERM:
701 0 : return osl_Process_E_NoPermission;
702 :
703 : case ESRCH:
704 0 : return osl_Process_E_NotFound;
705 :
706 : default:
707 0 : return osl_Process_E_Unknown;
708 : }
709 : }
710 :
711 0 : return osl_Process_E_None;
712 : }
713 :
714 0 : oslProcess SAL_CALL osl_getProcess(oslProcessIdentifier Ident)
715 : {
716 : oslProcessImpl *pProcImpl;
717 :
718 0 : if (kill(Ident, 0) != -1)
719 : {
720 : oslProcessImpl* pChild;
721 :
722 0 : if (ChildListMutex == NULL)
723 0 : ChildListMutex = osl_createMutex();
724 :
725 0 : osl_acquireMutex(ChildListMutex);
726 :
727 0 : pChild = ChildList;
728 :
729 : /* check if it is one of our child processes */
730 0 : while (pChild != NULL)
731 : {
732 0 : if (Ident == (sal_uInt32) pChild->m_pid)
733 0 : break;
734 :
735 0 : pChild = pChild->m_pnext;
736 : }
737 :
738 0 : pProcImpl = static_cast<oslProcessImpl*>(malloc(sizeof(oslProcessImpl)));
739 0 : pProcImpl->m_pid = Ident;
740 0 : pProcImpl->m_terminated = osl_createCondition();
741 :
742 0 : if (pChild != NULL)
743 : {
744 : /* process is a child so insert into list */
745 0 : pProcImpl->m_pnext = pChild->m_pnext;
746 0 : pChild->m_pnext = pProcImpl;
747 :
748 0 : pProcImpl->m_status = pChild->m_status;
749 :
750 0 : if (osl_checkCondition(pChild->m_terminated))
751 0 : osl_setCondition(pProcImpl->m_terminated);
752 : }
753 : else
754 0 : pProcImpl->m_pnext = NULL;
755 :
756 0 : osl_releaseMutex(ChildListMutex);
757 : }
758 : else
759 0 : pProcImpl = NULL;
760 :
761 0 : return pProcImpl;
762 : }
763 :
764 590 : void SAL_CALL osl_freeProcessHandle(oslProcess Process)
765 : {
766 590 : if (Process != NULL)
767 : {
768 590 : oslProcessImpl *pChild, *pPrev = NULL;
769 :
770 : OSL_ASSERT(ChildListMutex != NULL);
771 :
772 590 : if ( ChildListMutex == 0 )
773 : {
774 590 : return;
775 : }
776 :
777 590 : osl_acquireMutex(ChildListMutex);
778 :
779 590 : pChild = ChildList;
780 :
781 : /* remove process from child list */
782 1180 : while (pChild != NULL)
783 : {
784 590 : if (pChild == static_cast<oslProcessImpl*>(Process))
785 : {
786 590 : if (pPrev != NULL)
787 0 : pPrev->m_pnext = pChild->m_pnext;
788 : else
789 590 : ChildList = pChild->m_pnext;
790 :
791 590 : break;
792 : }
793 :
794 0 : pPrev = pChild;
795 0 : pChild = pChild->m_pnext;
796 : }
797 :
798 590 : osl_releaseMutex(ChildListMutex);
799 :
800 590 : osl_destroyCondition(static_cast<oslProcessImpl*>(Process)->m_terminated);
801 :
802 590 : free(Process);
803 : }
804 : }
805 :
806 : #if defined(LINUX)
807 : struct osl_procStat
808 : {
809 : /* from 'stat' */
810 : pid_t pid; /* pid */
811 : char command[16]; /* 'argv[0]' */ /* mfe: it all right char comm[16] in kernel! */
812 : char state; /* state (running, stopped, ...) */
813 : pid_t ppid; /* parent pid */
814 : pid_t pgrp; /* parent group */
815 : int session; /* session ID */
816 : int tty; /* no of tty */
817 : pid_t tpgid; /* group of process owning the tty */
818 : unsigned long flags; /* flags dunno */
819 : unsigned long minflt; /* minor page faults */
820 : unsigned long cminflt; /* minor page faults with children */
821 : unsigned long majflt; /* major page faults */
822 : unsigned long cmajflt; /* major page faults with children */
823 : unsigned long utime; /* no of jiffies in user mode */
824 : unsigned long stime; /* no of jiffies in kernel mode */
825 : unsigned long cutime; /* no of jiffies in user mode with children */
826 : unsigned long cstime; /* no of jiffies in kernel mode with children */
827 : unsigned long priority; /* nice value + 15 (kernel scheduling prio)*/
828 : long nice; /* nice value */
829 : long timeout; /* no of jiffies of next process timeout */
830 : long itrealvalue; /* no jiffies before next SIGALRM */
831 : unsigned long starttime; /* process started this no of jiffies after boot */
832 : unsigned long vsize; /* virtual memory size (in bytes) */
833 : long rss; /* resident set size (in pages) */
834 : unsigned long rss_rlim; /* rss limit (in bytes) */
835 : unsigned long startcode; /* address above program text can run */
836 : unsigned long endcode; /* address below program text can run */
837 : unsigned long startstack; /* address of start of stack */
838 : unsigned long kstkesp; /* current value of 'esp' (stack pointer) */
839 : unsigned long kstkeip; /* current value of 'eip' (instruction pointer) */
840 : /* mfe: Linux > 2.1.7x have more signals (88) */
841 : char signal[24]; /* pending signals */
842 : char blocked[24]; /* blocked signals */
843 : char sigignore[24]; /* ignored signals */
844 : char sigcatch[24]; /* catched signals */
845 : unsigned long wchan; /* 'channel' the process is waiting in */
846 : unsigned long nswap; /* ? */
847 : unsigned long cnswap; /* ? */
848 :
849 : /* from 'status' */
850 : int ruid; /* real uid */
851 : int euid; /* effective uid */
852 : int suid; /* saved uid */
853 : int fuid; /* file access uid */
854 : int rgid; /* real gid */
855 : int egid; /* effective gid */
856 : int sgid; /* saved gid */
857 : int fgid; /* file access gid */
858 : unsigned long vm_size; /* like vsize but on kb */
859 : unsigned long vm_lock; /* locked pages in kb */
860 : unsigned long vm_rss; /* like rss but in kb */
861 : unsigned long vm_data; /* data size */
862 : unsigned long vm_stack; /* stack size */
863 : unsigned long vm_exe; /* executable size */
864 : unsigned long vm_lib; /* library size */
865 : };
866 :
867 0 : bool osl_getProcStat(pid_t pid, struct osl_procStat* procstat)
868 : {
869 0 : int fd = 0;
870 0 : bool bRet = false;
871 : char name[PATH_MAX + 1];
872 0 : snprintf(name, sizeof(name), "/proc/%u/stat", pid);
873 :
874 0 : if ((fd = open(name,O_RDONLY)) >=0 )
875 : {
876 0 : char* tmp=0;
877 : char prstatbuf[512];
878 0 : memset(prstatbuf,0,512);
879 0 : bRet = safeRead(fd, prstatbuf, 511);
880 :
881 0 : close(fd);
882 :
883 0 : if (!bRet)
884 0 : return false;
885 :
886 0 : tmp = strrchr(prstatbuf, ')');
887 0 : if(tmp)
888 : {
889 0 : *tmp = '\0';
890 :
891 0 : memset(procstat->command, 0, sizeof(procstat->command));
892 :
893 0 : sscanf(prstatbuf, "%d (%15c", &procstat->pid, procstat->command);
894 : sscanf(tmp + 2,
895 : "%c"
896 : "%i %i %i %i %i"
897 : "%lu %lu %lu %lu %lu"
898 : "%lu %lu %lu %lu"
899 : "%lu %li %li %li"
900 : "%lu %lu %li %lu"
901 : "%lu %lu %lu %lu %lu"
902 : "%s %s %s %s"
903 : "%lu %lu %lu",
904 : &procstat->state,
905 : &procstat->ppid, &procstat->pgrp, &procstat->session, &procstat->tty, &procstat->tpgid,
906 : &procstat->flags, &procstat->minflt, &procstat->cminflt, &procstat->majflt, &procstat->cmajflt,
907 : &procstat->utime, &procstat->stime, &procstat->cutime, &procstat->cstime,
908 : &procstat->priority, &procstat->nice, &procstat->timeout, &procstat->itrealvalue,
909 : &procstat->starttime, &procstat->vsize, &procstat->rss, &procstat->rss_rlim,
910 : &procstat->startcode, &procstat->endcode, &procstat->startstack, &procstat->kstkesp, &procstat->kstkeip,
911 : procstat->signal, procstat->blocked, procstat->sigignore, procstat->sigcatch,
912 : &procstat->wchan, &procstat->nswap, &procstat->cnswap
913 0 : );
914 : }
915 : else
916 : {
917 0 : bRet = false;
918 : }
919 : }
920 0 : return bRet;
921 : }
922 :
923 0 : bool osl_getProcStatus(pid_t pid, struct osl_procStat* procstat)
924 : {
925 0 : int fd = 0;
926 : char name[PATH_MAX + 1];
927 0 : bool bRet = false;
928 :
929 0 : snprintf(name, sizeof(name), "/proc/%u/status", pid);
930 :
931 0 : if ((fd = open(name,O_RDONLY)) >=0 )
932 : {
933 0 : char* tmp=0;
934 : char prstatusbuf[512];
935 0 : memset(prstatusbuf,0,512);
936 0 : bRet = safeRead(fd, prstatusbuf, 511);
937 :
938 0 : close(fd);
939 :
940 0 : if (!bRet)
941 0 : return false;
942 :
943 0 : tmp = strstr(prstatusbuf,"Uid:");
944 0 : if(tmp)
945 : {
946 : sscanf(tmp,"Uid:\t%d\t%d\t%d\t%d",
947 : &procstat->ruid, &procstat->euid, &procstat->suid, &procstat->fuid
948 0 : );
949 : }
950 :
951 0 : tmp = strstr(prstatusbuf,"Gid:");
952 0 : if(tmp)
953 : {
954 : sscanf(tmp,"Gid:\t%d\t%d\t%d\t%d",
955 : &procstat->rgid, &procstat->egid, &procstat->sgid, &procstat->fgid
956 0 : );
957 : }
958 :
959 0 : tmp = strstr(prstatusbuf,"VmSize:");
960 0 : if(tmp)
961 : {
962 : sscanf(tmp,
963 : "VmSize: %lu kB\n"
964 : "VmLck: %lu kB\n"
965 : "VmRSS: %lu kB\n"
966 : "VmData: %lu kB\n"
967 : "VmStk: %lu kB\n"
968 : "VmExe: %lu kB\n"
969 : "VmLib: %lu kB\n",
970 : &procstat->vm_size, &procstat->vm_lock, &procstat->vm_rss, &procstat->vm_data,
971 : &procstat->vm_stack, &procstat->vm_exe, &procstat->vm_lib
972 0 : );
973 : }
974 :
975 0 : tmp = strstr(prstatusbuf,"SigPnd:");
976 0 : if(tmp)
977 : {
978 : sscanf(tmp, "SigPnd: %s SigBlk: %s SigIgn: %s %*s %s",
979 : procstat->signal, procstat->blocked, procstat->sigignore, procstat->sigcatch
980 0 : );
981 : }
982 : }
983 0 : return bRet;
984 : }
985 :
986 : #endif
987 :
988 656 : oslProcessError SAL_CALL osl_getProcessInfo(oslProcess Process, oslProcessData Fields, oslProcessInfo* pInfo)
989 : {
990 : pid_t pid;
991 :
992 656 : if (Process == NULL)
993 212 : pid = getpid();
994 : else
995 444 : pid = static_cast<oslProcessImpl*>(Process)->m_pid;
996 :
997 656 : if (! pInfo || (pInfo->Size != sizeof(oslProcessInfo)))
998 0 : return osl_Process_E_Unknown;
999 :
1000 656 : pInfo->Fields = 0;
1001 :
1002 656 : if (Fields & osl_Process_IDENTIFIER)
1003 : {
1004 212 : pInfo->Ident = pid;
1005 212 : pInfo->Fields |= osl_Process_IDENTIFIER;
1006 : }
1007 :
1008 656 : if (Fields & osl_Process_EXITCODE)
1009 : {
1010 888 : if ((Process != NULL) &&
1011 444 : osl_checkCondition(static_cast<oslProcessImpl*>(Process)->m_terminated))
1012 : {
1013 444 : pInfo->Code = static_cast<oslProcessImpl*>(Process)->m_status;
1014 444 : pInfo->Fields |= osl_Process_EXITCODE;
1015 : }
1016 : }
1017 :
1018 656 : if (Fields & (osl_Process_HEAPUSAGE | osl_Process_CPUTIMES))
1019 : {
1020 :
1021 : #if defined(SOLARIS)
1022 :
1023 : int fd;
1024 : sal_Char name[PATH_MAX + 1];
1025 :
1026 : snprintf(name, sizeof(name), "/proc/%u", pid);
1027 :
1028 : if ((fd = open(name, O_RDONLY)) >= 0)
1029 : {
1030 : prstatus_t prstatus;
1031 :
1032 : if (ioctl(fd, PIOCSTATUS, &prstatus) >= 0)
1033 : {
1034 : if (Fields & osl_Process_CPUTIMES)
1035 : {
1036 : pInfo->UserTime.Seconds = prstatus.pr_utime.tv_sec;
1037 : pInfo->UserTime.Nanosec = prstatus.pr_utime.tv_nsec;
1038 : pInfo->SystemTime.Seconds = prstatus.pr_stime.tv_sec;
1039 : pInfo->SystemTime.Nanosec = prstatus.pr_stime.tv_nsec;
1040 :
1041 : pInfo->Fields |= osl_Process_CPUTIMES;
1042 : }
1043 :
1044 : if (Fields & osl_Process_HEAPUSAGE)
1045 : {
1046 : pInfo->HeapUsage = prstatus.pr_brksize;
1047 :
1048 : pInfo->Fields |= osl_Process_HEAPUSAGE;
1049 : }
1050 :
1051 : close(fd);
1052 :
1053 : return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
1054 : }
1055 : else
1056 : close(fd);
1057 : }
1058 :
1059 : #elif defined(LINUX)
1060 :
1061 0 : if ( (Fields & osl_Process_CPUTIMES) || (Fields & osl_Process_HEAPUSAGE) )
1062 : {
1063 : struct osl_procStat procstat;
1064 0 : memset(&procstat,0,sizeof(procstat));
1065 :
1066 0 : if ( (Fields & osl_Process_CPUTIMES) && osl_getProcStat(pid, &procstat) )
1067 : {
1068 : /*
1069 : * mfe:
1070 : * We calculate only time of the process proper.
1071 : * Threads are processes, we do not consider their time here!
1072 : * (For this, cutime and cstime should be used, it seems not
1073 : * to work in 2.0.36)
1074 : */
1075 :
1076 : long clktck;
1077 : unsigned long hz;
1078 : unsigned long userseconds;
1079 : unsigned long systemseconds;
1080 :
1081 0 : clktck = sysconf(_SC_CLK_TCK);
1082 0 : if (clktck < 0) {
1083 0 : return osl_Process_E_Unknown;
1084 : }
1085 0 : hz = (unsigned long) clktck;
1086 :
1087 0 : userseconds = procstat.utime/hz;
1088 0 : systemseconds = procstat.stime/hz;
1089 :
1090 0 : pInfo->UserTime.Seconds = userseconds;
1091 0 : pInfo->UserTime.Nanosec = procstat.utime - (userseconds * hz);
1092 0 : pInfo->SystemTime.Seconds = systemseconds;
1093 0 : pInfo->SystemTime.Nanosec = procstat.stime - (systemseconds * hz);
1094 :
1095 0 : pInfo->Fields |= osl_Process_CPUTIMES;
1096 : }
1097 :
1098 0 : if ( (Fields & osl_Process_HEAPUSAGE) && osl_getProcStatus(pid, &procstat) )
1099 : {
1100 : /*
1101 : * mfe:
1102 : * vm_data (found in status) shows the size of the data segment
1103 : * it a rough approximation of the core heap size
1104 : */
1105 0 : pInfo->HeapUsage = procstat.vm_data*1024;
1106 :
1107 0 : pInfo->Fields |= osl_Process_HEAPUSAGE;
1108 : }
1109 : }
1110 :
1111 0 : return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
1112 : #endif
1113 :
1114 : }
1115 :
1116 656 : return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
1117 : }
1118 :
1119 : /***********************************************
1120 : helper function for osl_joinProcessWithTimeout
1121 : **********************************************/
1122 :
1123 0 : static bool is_timeout(const struct timeval* tend)
1124 : {
1125 : struct timeval tcurrent;
1126 0 : gettimeofday(&tcurrent, NULL);
1127 0 : return (tcurrent.tv_sec >= tend->tv_sec);
1128 : }
1129 :
1130 : /**********************************************
1131 : kill(pid, 0) is useful for checking if a
1132 : process is still alive, but remember that
1133 : kill even returns 0 if the process is already
1134 : a zombie.
1135 : *********************************************/
1136 :
1137 0 : static bool is_process_dead(pid_t pid)
1138 : {
1139 0 : return ((-1 == kill(pid, 0)) && (ESRCH == errno));
1140 : }
1141 :
1142 : /**********************************************
1143 : osl_joinProcessWithTimeout
1144 : *********************************************/
1145 :
1146 561 : oslProcessError SAL_CALL osl_joinProcessWithTimeout(oslProcess Process, const TimeValue* pTimeout)
1147 : {
1148 561 : oslProcessImpl* pChild = ChildList;
1149 561 : oslProcessError osl_error = osl_Process_E_None;
1150 :
1151 : OSL_PRECOND(Process, "osl_joinProcess: Invalid parameter");
1152 : OSL_ASSERT(ChildListMutex);
1153 :
1154 561 : if (NULL == Process || 0 == ChildListMutex)
1155 0 : return osl_Process_E_Unknown;
1156 :
1157 561 : osl_acquireMutex(ChildListMutex);
1158 :
1159 : /* check if process is a child of ours */
1160 1122 : while (pChild != NULL)
1161 : {
1162 561 : if (pChild == static_cast<oslProcessImpl*>(Process))
1163 561 : break;
1164 :
1165 0 : pChild = pChild->m_pnext;
1166 : }
1167 :
1168 561 : osl_releaseMutex(ChildListMutex);
1169 :
1170 561 : if (pChild != NULL)
1171 : {
1172 561 : oslConditionResult cond_res = osl_waitCondition(pChild->m_terminated, pTimeout);
1173 :
1174 561 : if (osl_cond_result_timeout == cond_res)
1175 34 : osl_error = osl_Process_E_TimedOut;
1176 527 : else if (osl_cond_result_ok != cond_res)
1177 0 : osl_error = osl_Process_E_Unknown;
1178 : }
1179 : else /* alien process; StatusThread will not be able
1180 : to set the condition terminated */
1181 : {
1182 0 : pid_t pid = static_cast<oslProcessImpl*>(Process)->m_pid;
1183 :
1184 0 : if (pTimeout)
1185 : {
1186 0 : bool timeout = false;
1187 : struct timeval tend;
1188 :
1189 0 : gettimeofday(&tend, NULL);
1190 :
1191 0 : tend.tv_sec += pTimeout->Seconds;
1192 :
1193 0 : while (!is_process_dead(pid) && !(timeout = is_timeout(&tend)))
1194 0 : sleep(1);
1195 :
1196 0 : if (timeout)
1197 0 : osl_error = osl_Process_E_TimedOut;
1198 : }
1199 : else /* infinite */
1200 : {
1201 0 : while (!is_process_dead(pid))
1202 0 : sleep(1);
1203 : }
1204 : }
1205 561 : return osl_error;
1206 : }
1207 :
1208 341 : oslProcessError SAL_CALL osl_joinProcess(oslProcess Process)
1209 : {
1210 341 : return osl_joinProcessWithTimeout(Process, NULL);
1211 : }
1212 :
1213 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|