Version 4.3.0.0.beta1, tag libreoffice-4.3.0.0.beta1
[LibreOffice.git] / sal / osl / unx / process.cxx
blobabe4bc9f6b645c907ebf5b75a12b2a73377a37c1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "sal/config.h"
21 #include "rtl/ustring.hxx"
23 #include <cassert>
26 * ToDo:
27 * - cleanup of process status things
28 * - cleanup of process spawning
29 * - cleanup of resource transfer
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
39 #if defined(FREEBSD) || defined(NETBSD) || defined(DRAGONFLY)
40 #include <machine/param.h>
41 #endif
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>
56 #include <grp.h>
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"
65 #define MAX_ARGS 255
66 #define MAX_ENVS 255
68 namespace
71 struct ProcessData
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;
87 static oslProcessImpl* ChildList;
88 static oslMutex ChildListMutex;
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 *****************************************************************************/
97 static oslProcessError SAL_CALL osl_searchPath_impl(const sal_Char* pszName,
98 sal_Char *pszBuffer, sal_uInt32 Max)
100 sal_Char path[PATH_MAX + 1];
101 sal_Char *pchr;
103 path[0] = '\0';
105 OSL_ASSERT(pszName != NULL);
107 if ( pszName == 0 )
109 return osl_Process_E_NotFound;
112 if ( (pchr = getenv("PATH")) != 0 )
114 sal_Char *pstr;
116 while (*pchr != '\0')
118 pstr = path;
120 while ((*pchr != '\0') && (*pchr != ':'))
121 *pstr++ = *pchr++;
123 if ((pstr > path) && ((*(pstr - 1) != '/')))
124 *pstr++ = '/';
126 *pstr = '\0';
128 strcat(path, pszName);
130 if (access(path, 0) == 0)
132 char szRealPathBuf[PATH_MAX] = "";
134 if( NULL == realpath(path, szRealPathBuf) || (strlen(szRealPathBuf) >= (sal_uInt32)Max))
135 return osl_Process_E_Unknown;
137 strcpy(pszBuffer, path);
139 return osl_Process_E_None;
142 if (*pchr == ':')
143 pchr++;
147 return osl_Process_E_NotFound;
150 } //Anonymous namespace
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 );
163 /******************************************************************************
165 * New io resource transfer functions
167 *****************************************************************************/
169 sal_Bool osl_sendResourcePipe(oslPipe /*pPipe*/, oslSocket /*pSocket*/)
171 return osl_Process_E_InvalidError;
174 oslSocket osl_receiveResourcePipe(oslPipe /*pPipe*/)
176 oslSocket pSocket = 0;
177 return pSocket;
180 /******************************************************************************
182 * Functions for starting a process
184 *****************************************************************************/
186 extern "C" {
188 static void ChildStatusProc(void *pData)
190 pid_t pid = -1;
191 int status = 0;
192 int channel[2] = { -1, -1 };
193 ProcessData data;
194 ProcessData *pdata;
195 int stdOutput[2] = { -1, -1 }, stdInput[2] = { -1, -1 }, stdError[2] = { -1, -1 };
197 pdata = (ProcessData *)pData;
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 memcpy(&data, pData, sizeof(data));
204 #ifdef NO_CHILD_PROCESSES
205 #define fork() (errno = EINVAL, -1)
206 #endif
207 if (socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == -1)
209 status = errno;
210 SAL_WARN("sal.osl", "executeProcess socketpair() errno " << status);
213 fcntl(channel[0], F_SETFD, FD_CLOEXEC);
214 fcntl(channel[1], F_SETFD, FD_CLOEXEC);
216 /* Create redirected IO pipes */
217 if ( status == 0 && data.m_pInputWrite && pipe( stdInput ) == -1 )
219 status = errno;
220 assert(status != 0);
221 SAL_WARN("sal.osl", "executeProcess pipe(stdInput) errno " << status);
224 if ( status == 0 && data.m_pOutputRead && pipe( stdOutput ) == -1 )
226 status = errno;
227 assert(status != 0);
228 SAL_WARN("sal.osl", "executeProcess pipe(stdOutput) errno " << status);
231 if ( status == 0 && data.m_pErrorRead && pipe( stdError ) == -1 )
233 status = errno;
234 assert(status != 0);
235 SAL_WARN("sal.osl", "executeProcess pipe(stdError) errno " << status);
238 if ( (status == 0) && ((pid = fork()) == 0) )
240 /* Child */
241 int chstatus = 0;
242 int errno_copy;
244 if (channel[0] != -1) close(channel[0]);
246 if ((data.m_uid != (uid_t)-1) && ((data.m_uid != getuid()) || (data.m_gid != getgid())))
248 OSL_ASSERT(geteuid() == 0); /* must be root */
250 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));
253 const rtl::OUString envVar("HOME");
254 osl_clearEnvironment(envVar.pData);
257 if (data.m_pszDir)
258 chstatus = chdir(data.m_pszDir);
260 if (chstatus == 0 && ((data.m_uid == (uid_t)-1) || ((data.m_uid == getuid()) && (data.m_gid == getgid()))))
262 int i;
263 for (i = 0; data.m_pszEnv[i] != NULL; i++)
265 if (strchr(data.m_pszEnv[i], '=') == NULL)
267 unsetenv(data.m_pszEnv[i]); /*TODO: check error return*/
269 else
271 putenv(data.m_pszEnv[i]); /*TODO: check error return*/
275 OSL_TRACE("ChildStatusProc : starting '%s'",data.m_pszArgs[0]);
277 /* Connect std IO to pipe ends */
279 /* Write end of stdInput not used in child process */
280 if (stdInput[1] != -1) close( stdInput[1] );
282 /* Read end of stdOutput not used in child process */
283 if (stdOutput[0] != -1) close( stdOutput[0] );
285 /* Read end of stdError not used in child process */
286 if (stdError[0] != -1) close( stdError[0] );
288 /* Redirect pipe ends to std IO */
290 if ( stdInput[0] != STDIN_FILENO )
292 dup2( stdInput[0], STDIN_FILENO );
293 if (stdInput[0] != -1) close( stdInput[0] );
296 if ( stdOutput[1] != STDOUT_FILENO )
298 dup2( stdOutput[1], STDOUT_FILENO );
299 if (stdOutput[1] != -1) close( stdOutput[1] );
302 if ( stdError[1] != STDERR_FILENO )
304 dup2( stdError[1], STDERR_FILENO );
305 if (stdError[1] != -1) close( stdError[1] );
308 // No need to check the return value of execv. If we return from
309 // it, an error has occurred.
310 execv(data.m_pszArgs[0], (sal_Char **)data.m_pszArgs);
313 OSL_TRACE("Failed to exec, errno=%d (%s)", errno, strerror(errno));
315 OSL_TRACE("ChildStatusProc : starting '%s' failed",data.m_pszArgs[0]);
317 /* if we reach here, something went wrong */
318 errno_copy = errno;
319 if ( !safeWrite(channel[1], &errno_copy, sizeof(errno_copy)) )
320 OSL_TRACE("sendFdPipe : sending failed (%s)",strerror(errno));
322 if ( channel[1] != -1 )
323 close(channel[1]);
325 _exit(255);
327 else
328 { /* Parent */
329 int i = -1;
330 if (channel[1] != -1) close(channel[1]);
332 /* Close unused pipe ends */
333 if (stdInput[0] != -1) close( stdInput[0] );
334 if (stdOutput[1] != -1) close( stdOutput[1] );
335 if (stdError[1] != -1) close( stdError[1] );
337 if (pid > 0)
339 while (((i = read(channel[0], &status, sizeof(status))) < 0))
341 if (errno != EINTR)
342 break;
346 if (channel[0] != -1) close(channel[0]);
348 if ((pid > 0) && (i == 0))
350 pid_t child_pid;
351 osl_acquireMutex(ChildListMutex);
353 pdata->m_pProcImpl->m_pid = pid;
354 pdata->m_pProcImpl->m_pnext = ChildList;
355 ChildList = pdata->m_pProcImpl;
357 /* Store used pipe ends in data structure */
359 if ( pdata->m_pInputWrite )
360 *(pdata->m_pInputWrite) = osl::detail::createFileHandleFromFD( stdInput[1] );
362 if ( pdata->m_pOutputRead )
363 *(pdata->m_pOutputRead) = osl::detail::createFileHandleFromFD( stdOutput[0] );
365 if ( pdata->m_pErrorRead )
366 *(pdata->m_pErrorRead) = osl::detail::createFileHandleFromFD( stdError[0] );
368 osl_releaseMutex(ChildListMutex);
370 osl_setCondition(pdata->m_started);
374 child_pid = waitpid(pid, &status, 0);
375 } while ( 0 > child_pid && EINTR == errno );
377 if ( child_pid < 0)
379 OSL_TRACE("Failed to wait for child process, errno=%d (%s)", errno, strerror(errno));
382 We got an other error than EINTR. Anyway we have to wake up the
383 waiting thread under any circumstances */
385 child_pid = pid;
388 if ( child_pid > 0 )
390 oslProcessImpl* pChild;
392 osl_acquireMutex(ChildListMutex);
394 pChild = ChildList;
396 /* check if it is one of our child processes */
397 while (pChild != NULL)
399 if (pChild->m_pid == child_pid)
401 if (WIFEXITED(status))
402 pChild->m_status = WEXITSTATUS(status);
403 else if (WIFSIGNALED(status))
404 pChild->m_status = 128 + WTERMSIG(status);
405 else
406 pChild->m_status = -1;
408 osl_setCondition(pChild->m_terminated);
411 pChild = pChild->m_pnext;
414 osl_releaseMutex(ChildListMutex);
417 else
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));
422 /* Close pipe ends */
423 if ( pdata->m_pInputWrite )
424 *pdata->m_pInputWrite = NULL;
426 if ( pdata->m_pOutputRead )
427 *pdata->m_pOutputRead = NULL;
429 if ( pdata->m_pErrorRead )
430 *pdata->m_pErrorRead = NULL;
432 if (stdInput[1] != -1) close( stdInput[1] );
433 if (stdOutput[0] != -1) close( stdOutput[0] );
434 if (stdError[0] != -1) close( stdError[0] );
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 if (pid > 0)
441 pid_t child_pid;
444 child_pid = waitpid(pid, &status, 0);
445 } while ( 0 > child_pid && EINTR == errno );
448 /* notify (and unblock) parent thread */
449 osl_setCondition(pdata->m_started);
456 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
472 oslProcessError Error;
473 sal_Char* pszWorkDir=0;
474 sal_Char** pArguments=0;
475 sal_Char** pEnvironment=0;
476 unsigned int idx;
478 char szImagePath[PATH_MAX] = "";
479 char szWorkDir[PATH_MAX] = "";
481 if ( ustrImageName && ustrImageName->length )
483 FileURLToPath( szImagePath, PATH_MAX, ustrImageName );
486 if ( ustrWorkDir != 0 && ustrWorkDir->length )
488 FileURLToPath( szWorkDir, PATH_MAX, ustrWorkDir );
489 pszWorkDir = szWorkDir;
492 if ( pArguments == 0 && nArguments > 0 )
494 pArguments = (sal_Char**) malloc( ( nArguments + 2 ) * sizeof(sal_Char*) );
497 for ( idx = 0 ; idx < nArguments ; ++idx )
499 rtl_String* strArg =0;
501 rtl_uString2String( &strArg,
502 rtl_uString_getStr(ustrArguments[idx]),
503 rtl_uString_getLength(ustrArguments[idx]),
504 osl_getThreadTextEncoding(),
505 OUSTRING_TO_OSTRING_CVTFLAGS );
507 pArguments[idx]=strdup(rtl_string_getStr(strArg));
508 rtl_string_release(strArg);
509 pArguments[idx+1]=0;
512 for ( idx = 0 ; idx < nEnvironmentVars ; ++idx )
514 rtl_String* strEnv=0;
516 if ( pEnvironment == 0 )
518 pEnvironment = (sal_Char**) malloc( ( nEnvironmentVars + 2 ) * sizeof(sal_Char*) );
521 rtl_uString2String( &strEnv,
522 rtl_uString_getStr(ustrEnvironment[idx]),
523 rtl_uString_getLength(ustrEnvironment[idx]),
524 osl_getThreadTextEncoding(),
525 OUSTRING_TO_OSTRING_CVTFLAGS );
527 pEnvironment[idx]=strdup(rtl_string_getStr(strEnv));
528 rtl_string_release(strEnv);
529 pEnvironment[idx+1]=0;
532 Error = osl_psz_executeProcess(szImagePath,
533 pArguments,
534 Options,
535 Security,
536 pszWorkDir,
537 pEnvironment,
538 pProcess,
539 pInputWrite,
540 pOutputRead,
541 pErrorRead
544 if ( pArguments != 0 )
546 for ( idx = 0 ; idx < nArguments ; ++idx )
548 if ( pArguments[idx] != 0 )
550 free(pArguments[idx]);
553 free(pArguments);
556 if ( pEnvironment != 0 )
558 for ( idx = 0 ; idx < nEnvironmentVars ; ++idx )
560 if ( pEnvironment[idx] != 0 )
562 free(pEnvironment[idx]);
565 free(pEnvironment);
568 return Error;
571 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
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
599 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
611 int i;
612 sal_Char path[PATH_MAX + 1];
613 ProcessData Data;
614 oslThread hThread;
616 path[0] = '\0';
618 memset(&Data,0,sizeof(ProcessData));
619 Data.m_pInputWrite = pInputWrite;
620 Data.m_pOutputRead = pOutputRead;
621 Data.m_pErrorRead = pErrorRead;
623 if (pszImageName == NULL)
624 pszImageName = pszArguments[0];
626 OSL_ASSERT(pszImageName != NULL);
628 if ( pszImageName == 0 )
630 return osl_Process_E_NotFound;
633 if ((Options & osl_Process_SEARCHPATH) &&
634 (osl_searchPath_impl(pszImageName, path, sizeof(path)) == osl_Process_E_None))
635 pszImageName = path;
637 Data.m_pszArgs[0] = strdup(pszImageName);
638 Data.m_pszArgs[1] = 0;
640 if ( pszArguments != 0 )
642 for (i = 0; ((i + 2) < MAX_ARGS) && (pszArguments[i] != NULL); i++)
643 Data.m_pszArgs[i+1] = strdup(pszArguments[i]);
644 Data.m_pszArgs[i+2] = NULL;
647 Data.m_options = Options;
648 Data.m_pszDir = (pszDirectory != NULL) ? strdup(pszDirectory) : NULL;
650 if (pszEnvironments != NULL)
652 for (i = 0; ((i + 1) < MAX_ENVS) && (pszEnvironments[i] != NULL); i++)
653 Data.m_pszEnv[i] = strdup(pszEnvironments[i]);
654 Data.m_pszEnv[i+1] = NULL;
656 else
657 Data.m_pszEnv[0] = NULL;
659 if (Security != NULL)
661 Data.m_uid = ((oslSecurityImpl*)Security)->m_pPasswd.pw_uid;
662 Data.m_gid = ((oslSecurityImpl*)Security)->m_pPasswd.pw_gid;
663 Data.m_name = ((oslSecurityImpl*)Security)->m_pPasswd.pw_name;
665 else
666 Data.m_uid = (uid_t)-1;
668 Data.m_pProcImpl = (oslProcessImpl*) malloc(sizeof(oslProcessImpl));
669 Data.m_pProcImpl->m_pid = 0;
670 Data.m_pProcImpl->m_terminated = osl_createCondition();
671 Data.m_pProcImpl->m_pnext = NULL;
673 if (ChildListMutex == NULL)
674 ChildListMutex = osl_createMutex();
676 Data.m_started = osl_createCondition();
678 hThread = osl_createThread(ChildStatusProc, &Data);
680 if (hThread != 0)
682 osl_waitCondition(Data.m_started, NULL);
684 osl_destroyCondition(Data.m_started);
686 for (i = 0; Data.m_pszArgs[i] != NULL; i++)
687 free((void *)Data.m_pszArgs[i]);
689 for (i = 0; Data.m_pszEnv[i] != NULL; i++)
690 free((void *)Data.m_pszEnv[i]);
692 if ( Data.m_pszDir != 0 )
694 free((void *)Data.m_pszDir);
697 osl_destroyThread(hThread);
699 if (Data.m_pProcImpl->m_pid != 0)
701 assert(hThread != 0);
703 *pProcess = Data.m_pProcImpl;
705 if (Options & osl_Process_WAIT)
706 osl_joinProcess(*pProcess);
708 return osl_Process_E_None;
711 osl_destroyCondition(Data.m_pProcImpl->m_terminated);
712 free(Data.m_pProcImpl);
714 return osl_Process_E_Unknown;
717 /******************************************************************************
719 * Functions for processes
721 *****************************************************************************/
723 oslProcessError SAL_CALL osl_terminateProcess(oslProcess Process)
725 if (Process == NULL)
726 return osl_Process_E_Unknown;
728 if (kill(((oslProcessImpl*)Process)->m_pid, SIGKILL) != 0)
730 switch (errno)
732 case EPERM:
733 return osl_Process_E_NoPermission;
735 case ESRCH:
736 return osl_Process_E_NotFound;
738 default:
739 return osl_Process_E_Unknown;
743 return osl_Process_E_None;
746 oslProcess SAL_CALL osl_getProcess(oslProcessIdentifier Ident)
748 oslProcessImpl *pProcImpl;
750 if (kill(Ident, 0) != -1)
752 oslProcessImpl* pChild;
754 if (ChildListMutex == NULL)
755 ChildListMutex = osl_createMutex();
757 osl_acquireMutex(ChildListMutex);
759 pChild = ChildList;
761 /* check if it is one of our child processes */
762 while (pChild != NULL)
764 if (Ident == (sal_uInt32) pChild->m_pid)
765 break;
767 pChild = pChild->m_pnext;
770 pProcImpl = (oslProcessImpl*) malloc(sizeof(oslProcessImpl));
771 pProcImpl->m_pid = Ident;
772 pProcImpl->m_terminated = osl_createCondition();
774 if (pChild != NULL)
776 /* process is a child so insert into list */
777 pProcImpl->m_pnext = pChild->m_pnext;
778 pChild->m_pnext = pProcImpl;
780 pProcImpl->m_status = pChild->m_status;
782 if (osl_checkCondition(pChild->m_terminated))
783 osl_setCondition(pProcImpl->m_terminated);
785 else
786 pProcImpl->m_pnext = NULL;
788 osl_releaseMutex(ChildListMutex);
790 else
791 pProcImpl = NULL;
793 return (pProcImpl);
796 void SAL_CALL osl_freeProcessHandle(oslProcess Process)
798 if (Process != NULL)
800 oslProcessImpl *pChild, *pPrev = NULL;
802 OSL_ASSERT(ChildListMutex != NULL);
804 if ( ChildListMutex == 0 )
806 return;
809 osl_acquireMutex(ChildListMutex);
811 pChild = ChildList;
813 /* remove process from child list */
814 while (pChild != NULL)
816 if (pChild == (oslProcessImpl*)Process)
818 if (pPrev != NULL)
819 pPrev->m_pnext = pChild->m_pnext;
820 else
821 ChildList = pChild->m_pnext;
823 break;
826 pPrev = pChild;
827 pChild = pChild->m_pnext;
830 osl_releaseMutex(ChildListMutex);
832 osl_destroyCondition(((oslProcessImpl*)Process)->m_terminated);
834 free(Process);
838 #if defined(LINUX)
839 struct osl_procStat
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; /* ? */
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 */
899 bool osl_getProcStat(pid_t pid, struct osl_procStat* procstat)
901 int fd = 0;
902 bool bRet = false;
903 char name[PATH_MAX + 1];
904 snprintf(name, sizeof(name), "/proc/%u/stat", pid);
906 if ((fd = open(name,O_RDONLY)) >=0 )
908 char* tmp=0;
909 char prstatbuf[512];
910 memset(prstatbuf,0,512);
911 bRet = safeRead(fd, prstatbuf, 511);
913 close(fd);
915 if (!bRet)
916 return false;
918 tmp = strrchr(prstatbuf, ')');
919 *tmp = '\0';
920 memset(procstat->command, 0, sizeof(procstat->command));
922 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
944 return bRet;
947 bool osl_getProcStatus(pid_t pid, struct osl_procStat* procstat)
949 int fd = 0;
950 char name[PATH_MAX + 1];
951 bool bRet = false;
953 snprintf(name, sizeof(name), "/proc/%u/status", pid);
955 if ((fd = open(name,O_RDONLY)) >=0 )
957 char* tmp=0;
958 char prstatusbuf[512];
959 memset(prstatusbuf,0,512);
960 bRet = safeRead(fd, prstatusbuf, 511);
962 close(fd);
964 if (!bRet)
965 return false;
967 tmp = strstr(prstatusbuf,"Uid:");
968 if(tmp)
970 sscanf(tmp,"Uid:\t%d\t%d\t%d\t%d",
971 &procstat->ruid, &procstat->euid, &procstat->suid, &procstat->fuid
975 tmp = strstr(prstatusbuf,"Gid:");
976 if(tmp)
978 sscanf(tmp,"Gid:\t%d\t%d\t%d\t%d",
979 &procstat->rgid, &procstat->egid, &procstat->sgid, &procstat->fgid
983 tmp = strstr(prstatusbuf,"VmSize:");
984 if(tmp)
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
999 tmp = strstr(prstatusbuf,"SigPnd:");
1000 if(tmp)
1002 sscanf(tmp, "SigPnd: %s SigBlk: %s SigIgn: %s %*s %s",
1003 procstat->signal, procstat->blocked, procstat->sigignore, procstat->sigcatch
1007 return bRet;
1010 #endif
1012 oslProcessError SAL_CALL osl_getProcessInfo(oslProcess Process, oslProcessData Fields, oslProcessInfo* pInfo)
1014 pid_t pid;
1016 if (Process == NULL)
1017 pid = getpid();
1018 else
1019 pid = ((oslProcessImpl*)Process)->m_pid;
1021 if (! pInfo || (pInfo->Size != sizeof(oslProcessInfo)))
1022 return osl_Process_E_Unknown;
1024 pInfo->Fields = 0;
1026 if (Fields & osl_Process_IDENTIFIER)
1028 pInfo->Ident = pid;
1029 pInfo->Fields |= osl_Process_IDENTIFIER;
1032 if (Fields & osl_Process_EXITCODE)
1034 if ((Process != NULL) &&
1035 osl_checkCondition(((oslProcessImpl*)Process)->m_terminated))
1037 pInfo->Code = ((oslProcessImpl*)Process)->m_status;
1038 pInfo->Fields |= osl_Process_EXITCODE;
1042 if (Fields & (osl_Process_HEAPUSAGE | osl_Process_CPUTIMES))
1045 #if defined(SOLARIS)
1047 int fd;
1048 sal_Char name[PATH_MAX + 1];
1050 snprintf(name, sizeof(name), "/proc/%u", pid);
1052 if ((fd = open(name, O_RDONLY)) >= 0)
1054 prstatus_t prstatus;
1056 if (ioctl(fd, PIOCSTATUS, &prstatus) >= 0)
1058 if (Fields & osl_Process_CPUTIMES)
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;
1065 pInfo->Fields |= osl_Process_CPUTIMES;
1068 if (Fields & osl_Process_HEAPUSAGE)
1070 pInfo->HeapUsage = prstatus.pr_brksize;
1072 pInfo->Fields |= osl_Process_HEAPUSAGE;
1075 close(fd);
1077 return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
1079 else
1080 close(fd);
1083 #elif defined(LINUX)
1085 if ( (Fields & osl_Process_CPUTIMES) || (Fields & osl_Process_HEAPUSAGE) )
1087 struct osl_procStat procstat;
1088 memset(&procstat,0,sizeof(procstat));
1090 if ( (Fields & osl_Process_CPUTIMES) && osl_getProcStat(pid, &procstat) )
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)
1100 long clktck;
1101 unsigned long hz;
1102 unsigned long userseconds;
1103 unsigned long systemseconds;
1105 clktck = sysconf(_SC_CLK_TCK);
1106 if (clktck < 0) {
1107 return osl_Process_E_Unknown;
1109 hz = (unsigned long) clktck;
1111 userseconds = procstat.utime/hz;
1112 systemseconds = procstat.stime/hz;
1114 pInfo->UserTime.Seconds = userseconds;
1115 pInfo->UserTime.Nanosec = procstat.utime - (userseconds * hz);
1116 pInfo->SystemTime.Seconds = systemseconds;
1117 pInfo->SystemTime.Nanosec = procstat.stime - (systemseconds * hz);
1119 pInfo->Fields |= osl_Process_CPUTIMES;
1122 if ( (Fields & osl_Process_HEAPUSAGE) && osl_getProcStatus(pid, &procstat) )
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
1129 pInfo->HeapUsage = procstat.vm_data*1024;
1131 pInfo->Fields |= osl_Process_HEAPUSAGE;
1135 return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
1136 #endif
1140 return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
1143 /***********************************************
1144 helper function for osl_joinProcessWithTimeout
1145 **********************************************/
1147 static bool is_timeout(const struct timeval* tend)
1149 struct timeval tcurrent;
1150 gettimeofday(&tcurrent, NULL);
1151 return (tcurrent.tv_sec >= tend->tv_sec);
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 *********************************************/
1161 static bool is_process_dead(pid_t pid)
1163 return ((-1 == kill(pid, 0)) && (ESRCH == errno));
1166 /**********************************************
1167 osl_joinProcessWithTimeout
1168 *********************************************/
1170 oslProcessError SAL_CALL osl_joinProcessWithTimeout(oslProcess Process, const TimeValue* pTimeout)
1172 oslProcessImpl* pChild = ChildList;
1173 oslProcessError osl_error = osl_Process_E_None;
1175 OSL_PRECOND(Process, "osl_joinProcess: Invalid parameter");
1176 OSL_ASSERT(ChildListMutex);
1178 if (NULL == Process || 0 == ChildListMutex)
1179 return osl_Process_E_Unknown;
1181 osl_acquireMutex(ChildListMutex);
1183 /* check if process is a child of ours */
1184 while (pChild != NULL)
1186 if (pChild == (oslProcessImpl*)Process)
1187 break;
1189 pChild = pChild->m_pnext;
1192 osl_releaseMutex(ChildListMutex);
1194 if (pChild != NULL)
1196 oslConditionResult cond_res = osl_waitCondition(pChild->m_terminated, pTimeout);
1198 if (osl_cond_result_timeout == cond_res)
1199 osl_error = osl_Process_E_TimedOut;
1200 else if (osl_cond_result_ok != cond_res)
1201 osl_error = osl_Process_E_Unknown;
1203 else /* alien process; StatusThread will not be able
1204 to set the condition terminated */
1206 pid_t pid = ((oslProcessImpl*)Process)->m_pid;
1208 if (pTimeout)
1210 bool timeout = false;
1211 struct timeval tend;
1213 gettimeofday(&tend, NULL);
1215 tend.tv_sec += pTimeout->Seconds;
1217 while (!is_process_dead(pid) && !(timeout = is_timeout(&tend)))
1218 sleep(1);
1220 if (timeout)
1221 osl_error = osl_Process_E_TimedOut;
1223 else /* infinite */
1225 while (!is_process_dead(pid))
1226 sleep(1);
1229 return osl_error;
1232 oslProcessError SAL_CALL osl_joinProcess(oslProcess Process)
1234 return osl_joinProcessWithTimeout(Process, NULL);
1237 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */