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