Version 4.2.0.1, tag libreoffice-4.2.0.1
[LibreOffice.git] / sal / osl / unx / process.cxx
blob810ad5d4855aed4dcf4081a3d639f19f80bd5585
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
40 #if defined(FREEBSD) || defined(NETBSD) || defined(DRAGONFLY)
41 #include <machine/param.h>
42 #endif
44 #include "system.h"
45 #if defined(SOLARIS)
46 # include <sys/procfs.h>
47 #endif
48 #include <osl/diagnose.h>
49 #include <osl/mutex.h>
50 #include <osl/conditn.h>
51 #include <osl/thread.h>
52 #include <osl/file.h>
53 #include <osl/signal.h>
54 #include <rtl/alloc.h>
55 #include <sal/log.hxx>
57 #include <grp.h>
59 #include "createfilehandlefromfd.hxx"
60 #include "file_url.h"
61 #include "procimpl.h"
62 #include "readwrite_helper.h"
63 #include "sockimpl.h"
64 #include "secimpl.h"
66 #define MAX_ARGS 255
67 #define MAX_ENVS 255
69 namespace
72 struct ProcessData
74 const sal_Char* m_pszArgs[MAX_ARGS + 1];
75 oslProcessOption m_options;
76 const sal_Char* m_pszDir;
77 sal_Char* m_pszEnv[MAX_ENVS + 1];
78 uid_t m_uid;
79 gid_t m_gid;
80 sal_Char* m_name;
81 oslCondition m_started;
82 oslProcessImpl* m_pProcImpl;
83 oslFileHandle *m_pInputWrite;
84 oslFileHandle *m_pOutputRead;
85 oslFileHandle *m_pErrorRead;
88 static oslProcessImpl* ChildList;
89 static oslMutex ChildListMutex;
91 /******************************************************************************
92 Deprecated
93 Old and buggy implementation of osl_searchPath used only by
94 osl_psz_executeProcess.
95 A new implemenation is in file_path_helper.cxx
96 *****************************************************************************/
98 static oslProcessError SAL_CALL osl_searchPath_impl(const sal_Char* pszName,
99 sal_Char *pszBuffer, sal_uInt32 Max)
101 sal_Char path[PATH_MAX + 1];
102 sal_Char *pchr;
104 path[0] = '\0';
106 OSL_ASSERT(pszName != NULL);
108 if ( pszName == 0 )
110 return osl_Process_E_NotFound;
113 if ( (pchr = getenv("PATH")) != 0 )
115 sal_Char *pstr;
117 while (*pchr != '\0')
119 pstr = path;
121 while ((*pchr != '\0') && (*pchr != ':'))
122 *pstr++ = *pchr++;
124 if ((pstr > path) && ((*(pstr - 1) != '/')))
125 *pstr++ = '/';
127 *pstr = '\0';
129 strcat(path, pszName);
131 if (access(path, 0) == 0)
133 char szRealPathBuf[PATH_MAX] = "";
135 if( NULL == realpath(path, szRealPathBuf) || (strlen(szRealPathBuf) >= (sal_uInt32)Max))
136 return osl_Process_E_Unknown;
138 strcpy(pszBuffer, path);
140 return osl_Process_E_None;
143 if (*pchr == ':')
144 pchr++;
148 return osl_Process_E_NotFound;
151 } //Anonymous namespace
153 oslProcessError SAL_CALL osl_psz_executeProcess(sal_Char *pszImageName,
154 sal_Char *pszArguments[],
155 oslProcessOption Options,
156 oslSecurity Security,
157 sal_Char *pszDirectory,
158 sal_Char *pszEnvironments[],
159 oslProcess *pProcess,
160 oslFileHandle *pInputWrite,
161 oslFileHandle *pOutputRead,
162 oslFileHandle *pErrorRead );
164 /******************************************************************************
166 * New io resource transfer functions
168 *****************************************************************************/
171 sal_Bool osl_sendResourcePipe(oslPipe /*pPipe*/, oslSocket /*pSocket*/)
173 return osl_Process_E_InvalidError;
176 oslSocket osl_receiveResourcePipe(oslPipe /*pPipe*/)
178 oslSocket pSocket = 0;
179 return pSocket;
184 /******************************************************************************
186 * Functions for starting a process
188 *****************************************************************************/
190 extern "C" {
192 static void ChildStatusProc(void *pData)
194 pid_t pid = -1;
195 int status = 0;
196 int channel[2] = { -1, -1 };
197 ProcessData data;
198 ProcessData *pdata;
199 int stdOutput[2] = { -1, -1 }, stdInput[2] = { -1, -1 }, stdError[2] = { -1, -1 };
201 pdata = (ProcessData *)pData;
203 /* make a copy of our data, because forking will only copy
204 our local stack of the thread, so the process data will not be accessible
205 in our child process */
206 memcpy(&data, pData, sizeof(data));
208 #ifdef NO_CHILD_PROCESSES
209 #define fork() (errno = EINVAL, -1)
210 #endif
211 if (socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == -1)
213 status = errno;
214 SAL_WARN("sal.osl", "executeProcess socketpair() errno " << status);
217 fcntl(channel[0], F_SETFD, FD_CLOEXEC);
218 fcntl(channel[1], F_SETFD, FD_CLOEXEC);
220 /* Create redirected IO pipes */
221 if ( status == 0 && data.m_pInputWrite && pipe( stdInput ) == -1 )
223 status = errno;
224 assert(status != 0);
225 SAL_WARN("sal.osl", "executeProcess pipe(stdInput) errno " << status);
228 if ( status == 0 && data.m_pOutputRead && pipe( stdOutput ) == -1 )
230 status = errno;
231 assert(status != 0);
232 SAL_WARN("sal.osl", "executeProcess pipe(stdOutput) errno " << status);
235 if ( status == 0 && data.m_pErrorRead && pipe( stdError ) == -1 )
237 status = errno;
238 assert(status != 0);
239 SAL_WARN("sal.osl", "executeProcess pipe(stdError) errno " << status);
242 if ( (status == 0) && ((pid = fork()) == 0) )
244 /* Child */
245 int chstatus = 0;
246 int errno_copy;
248 if (channel[0] != -1) close(channel[0]);
250 if ((data.m_uid != (uid_t)-1) && ((data.m_uid != getuid()) || (data.m_gid != getgid())))
252 OSL_ASSERT(geteuid() == 0); /* must be root */
254 if (! INIT_GROUPS(data.m_name, data.m_gid) || (setuid(data.m_uid) != 0))
255 OSL_TRACE("Failed to change uid and guid, errno=%d (%s)", errno, strerror(errno));
257 const rtl::OUString envVar("HOME");
258 osl_clearEnvironment(envVar.pData);
261 if (data.m_pszDir)
262 chstatus = chdir(data.m_pszDir);
264 if (chstatus == 0 && ((data.m_uid == (uid_t)-1) || ((data.m_uid == getuid()) && (data.m_gid == getgid()))))
266 int i;
267 for (i = 0; data.m_pszEnv[i] != NULL; i++)
269 if (strchr(data.m_pszEnv[i], '=') == NULL)
271 unsetenv(data.m_pszEnv[i]); /*TODO: check error return*/
273 else
275 putenv(data.m_pszEnv[i]); /*TODO: check error return*/
279 OSL_TRACE("ChildStatusProc : starting '%s'",data.m_pszArgs[0]);
281 /* Connect std IO to pipe ends */
283 /* Write end of stdInput not used in child process */
284 if (stdInput[1] != -1) close( stdInput[1] );
286 /* Read end of stdOutput not used in child process */
287 if (stdOutput[0] != -1) close( stdOutput[0] );
289 /* Read end of stdError not used in child process */
290 if (stdError[0] != -1) close( stdError[0] );
292 /* Redirect pipe ends to std IO */
294 if ( stdInput[0] != STDIN_FILENO )
296 dup2( stdInput[0], STDIN_FILENO );
297 if (stdInput[0] != -1) close( stdInput[0] );
300 if ( stdOutput[1] != STDOUT_FILENO )
302 dup2( stdOutput[1], STDOUT_FILENO );
303 if (stdOutput[1] != -1) close( stdOutput[1] );
306 if ( stdError[1] != STDERR_FILENO )
308 dup2( stdError[1], STDERR_FILENO );
309 if (stdError[1] != -1) close( stdError[1] );
312 // No need to check the return value of execv. If we return from
313 // it, an error has occurred.
314 execv(data.m_pszArgs[0], (sal_Char **)data.m_pszArgs);
317 OSL_TRACE("Failed to exec, errno=%d (%s)", errno, strerror(errno));
319 OSL_TRACE("ChildStatusProc : starting '%s' failed",data.m_pszArgs[0]);
321 /* if we reach here, something went wrong */
322 errno_copy = errno;
323 if ( !safeWrite(channel[1], &errno_copy, sizeof(errno_copy)) )
324 OSL_TRACE("sendFdPipe : sending failed (%s)",strerror(errno));
326 if ( channel[1] != -1 )
327 close(channel[1]);
329 _exit(255);
331 else
332 { /* Parent */
333 int i = -1;
334 if (channel[1] != -1) close(channel[1]);
336 /* Close unused pipe ends */
337 if (stdInput[0] != -1) close( stdInput[0] );
338 if (stdOutput[1] != -1) close( stdOutput[1] );
339 if (stdError[1] != -1) close( stdError[1] );
341 if (pid > 0)
343 while (((i = read(channel[0], &status, sizeof(status))) < 0))
345 if (errno != EINTR)
346 break;
350 if (channel[0] != -1) close(channel[0]);
352 if ((pid > 0) && (i == 0))
354 pid_t child_pid;
355 osl_acquireMutex(ChildListMutex);
357 pdata->m_pProcImpl->m_pid = pid;
358 pdata->m_pProcImpl->m_pnext = ChildList;
359 ChildList = pdata->m_pProcImpl;
361 /* Store used pipe ends in data structure */
363 if ( pdata->m_pInputWrite )
364 *(pdata->m_pInputWrite) = osl::detail::createFileHandleFromFD( stdInput[1] );
366 if ( pdata->m_pOutputRead )
367 *(pdata->m_pOutputRead) = osl::detail::createFileHandleFromFD( stdOutput[0] );
369 if ( pdata->m_pErrorRead )
370 *(pdata->m_pErrorRead) = osl::detail::createFileHandleFromFD( stdError[0] );
372 osl_releaseMutex(ChildListMutex);
374 osl_setCondition(pdata->m_started);
378 child_pid = waitpid(pid, &status, 0);
379 } while ( 0 > child_pid && EINTR == errno );
381 if ( child_pid < 0)
383 OSL_TRACE("Failed to wait for child process, errno=%d (%s)", errno, strerror(errno));
386 We got an other error than EINTR. Anyway we have to wake up the
387 waiting thread under any circumstances */
389 child_pid = pid;
393 if ( child_pid > 0 )
395 oslProcessImpl* pChild;
397 osl_acquireMutex(ChildListMutex);
399 pChild = ChildList;
401 /* check if it is one of our child processes */
402 while (pChild != NULL)
404 if (pChild->m_pid == child_pid)
406 if (WIFEXITED(status))
407 pChild->m_status = WEXITSTATUS(status);
408 else if (WIFSIGNALED(status))
409 pChild->m_status = 128 + WTERMSIG(status);
410 else
411 pChild->m_status = -1;
413 osl_setCondition(pChild->m_terminated);
416 pChild = pChild->m_pnext;
419 osl_releaseMutex(ChildListMutex);
422 else
424 OSL_TRACE("ChildStatusProc : starting '%s' failed",data.m_pszArgs[0]);
425 OSL_TRACE("Failed to launch child process, child reports errno=%d (%s)", status, strerror(status));
427 /* Close pipe ends */
428 if ( pdata->m_pInputWrite )
429 *pdata->m_pInputWrite = NULL;
431 if ( pdata->m_pOutputRead )
432 *pdata->m_pOutputRead = NULL;
434 if ( pdata->m_pErrorRead )
435 *pdata->m_pErrorRead = NULL;
437 if (stdInput[1] != -1) close( stdInput[1] );
438 if (stdOutput[0] != -1) close( stdOutput[0] );
439 if (stdError[0] != -1) close( stdError[0] );
441 //if pid > 0 then a process was created, even if it later failed
442 //e.g. bash searching for a command to execute, and we still
443 //need to clean it up to avoid "defunct" processes
444 if (pid > 0)
446 pid_t child_pid;
449 child_pid = waitpid(pid, &status, 0);
450 } while ( 0 > child_pid && EINTR == errno );
453 /* notify (and unblock) parent thread */
454 osl_setCondition(pdata->m_started);
461 oslProcessError SAL_CALL osl_executeProcess_WithRedirectedIO(
462 rtl_uString *ustrImageName,
463 rtl_uString *ustrArguments[],
464 sal_uInt32 nArguments,
465 oslProcessOption Options,
466 oslSecurity Security,
467 rtl_uString *ustrWorkDir,
468 rtl_uString *ustrEnvironment[],
469 sal_uInt32 nEnvironmentVars,
470 oslProcess *pProcess,
471 oslFileHandle *pInputWrite,
472 oslFileHandle *pOutputRead,
473 oslFileHandle *pErrorRead
477 oslProcessError Error;
478 sal_Char* pszWorkDir=0;
479 sal_Char** pArguments=0;
480 sal_Char** pEnvironment=0;
481 unsigned int idx;
483 char szImagePath[PATH_MAX] = "";
484 char szWorkDir[PATH_MAX] = "";
486 if ( ustrImageName && ustrImageName->length )
488 FileURLToPath( szImagePath, PATH_MAX, ustrImageName );
491 if ( ustrWorkDir != 0 && ustrWorkDir->length )
493 FileURLToPath( szWorkDir, PATH_MAX, ustrWorkDir );
494 pszWorkDir = szWorkDir;
497 if ( pArguments == 0 && nArguments > 0 )
499 pArguments = (sal_Char**) malloc( ( nArguments + 2 ) * sizeof(sal_Char*) );
503 for ( idx = 0 ; idx < nArguments ; ++idx )
505 rtl_String* strArg =0;
508 rtl_uString2String( &strArg,
509 rtl_uString_getStr(ustrArguments[idx]),
510 rtl_uString_getLength(ustrArguments[idx]),
511 osl_getThreadTextEncoding(),
512 OUSTRING_TO_OSTRING_CVTFLAGS );
514 pArguments[idx]=strdup(rtl_string_getStr(strArg));
515 rtl_string_release(strArg);
516 pArguments[idx+1]=0;
519 for ( idx = 0 ; idx < nEnvironmentVars ; ++idx )
521 rtl_String* strEnv=0;
523 if ( pEnvironment == 0 )
525 pEnvironment = (sal_Char**) malloc( ( nEnvironmentVars + 2 ) * sizeof(sal_Char*) );
528 rtl_uString2String( &strEnv,
529 rtl_uString_getStr(ustrEnvironment[idx]),
530 rtl_uString_getLength(ustrEnvironment[idx]),
531 osl_getThreadTextEncoding(),
532 OUSTRING_TO_OSTRING_CVTFLAGS );
534 pEnvironment[idx]=strdup(rtl_string_getStr(strEnv));
535 rtl_string_release(strEnv);
536 pEnvironment[idx+1]=0;
540 Error = osl_psz_executeProcess(szImagePath,
541 pArguments,
542 Options,
543 Security,
544 pszWorkDir,
545 pEnvironment,
546 pProcess,
547 pInputWrite,
548 pOutputRead,
549 pErrorRead
552 if ( pArguments != 0 )
554 for ( idx = 0 ; idx < nArguments ; ++idx )
556 if ( pArguments[idx] != 0 )
558 free(pArguments[idx]);
561 free(pArguments);
564 if ( pEnvironment != 0 )
566 for ( idx = 0 ; idx < nEnvironmentVars ; ++idx )
568 if ( pEnvironment[idx] != 0 )
570 free(pEnvironment[idx]);
573 free(pEnvironment);
576 return Error;
579 oslProcessError SAL_CALL osl_executeProcess(
580 rtl_uString *ustrImageName,
581 rtl_uString *ustrArguments[],
582 sal_uInt32 nArguments,
583 oslProcessOption Options,
584 oslSecurity Security,
585 rtl_uString *ustrWorkDir,
586 rtl_uString *ustrEnvironment[],
587 sal_uInt32 nEnvironmentVars,
588 oslProcess *pProcess
591 return osl_executeProcess_WithRedirectedIO(
592 ustrImageName,
593 ustrArguments,
594 nArguments,
595 Options,
596 Security,
597 ustrWorkDir,
598 ustrEnvironment,
599 nEnvironmentVars,
600 pProcess,
601 NULL,
602 NULL,
603 NULL
607 oslProcessError SAL_CALL osl_psz_executeProcess(sal_Char *pszImageName,
608 sal_Char *pszArguments[],
609 oslProcessOption Options,
610 oslSecurity Security,
611 sal_Char *pszDirectory,
612 sal_Char *pszEnvironments[],
613 oslProcess *pProcess,
614 oslFileHandle *pInputWrite,
615 oslFileHandle *pOutputRead,
616 oslFileHandle *pErrorRead
619 int i;
620 sal_Char path[PATH_MAX + 1];
621 ProcessData Data;
622 oslThread hThread;
624 path[0] = '\0';
626 memset(&Data,0,sizeof(ProcessData));
627 Data.m_pInputWrite = pInputWrite;
628 Data.m_pOutputRead = pOutputRead;
629 Data.m_pErrorRead = pErrorRead;
631 if (pszImageName == NULL)
632 pszImageName = pszArguments[0];
634 OSL_ASSERT(pszImageName != NULL);
636 if ( pszImageName == 0 )
638 return osl_Process_E_NotFound;
641 if ((Options & osl_Process_SEARCHPATH) &&
642 (osl_searchPath_impl(pszImageName, path, sizeof(path)) == osl_Process_E_None))
643 pszImageName = path;
645 Data.m_pszArgs[0] = strdup(pszImageName);
646 Data.m_pszArgs[1] = 0;
648 if ( pszArguments != 0 )
650 for (i = 0; ((i + 2) < MAX_ARGS) && (pszArguments[i] != NULL); i++)
651 Data.m_pszArgs[i+1] = strdup(pszArguments[i]);
652 Data.m_pszArgs[i+2] = NULL;
655 Data.m_options = Options;
656 Data.m_pszDir = (pszDirectory != NULL) ? strdup(pszDirectory) : NULL;
658 if (pszEnvironments != NULL)
660 for (i = 0; ((i + 1) < MAX_ENVS) && (pszEnvironments[i] != NULL); i++)
661 Data.m_pszEnv[i] = strdup(pszEnvironments[i]);
662 Data.m_pszEnv[i+1] = NULL;
664 else
665 Data.m_pszEnv[0] = NULL;
667 if (Security != NULL)
669 Data.m_uid = ((oslSecurityImpl*)Security)->m_pPasswd.pw_uid;
670 Data.m_gid = ((oslSecurityImpl*)Security)->m_pPasswd.pw_gid;
671 Data.m_name = ((oslSecurityImpl*)Security)->m_pPasswd.pw_name;
673 else
674 Data.m_uid = (uid_t)-1;
676 Data.m_pProcImpl = (oslProcessImpl*) malloc(sizeof(oslProcessImpl));
677 Data.m_pProcImpl->m_pid = 0;
678 Data.m_pProcImpl->m_terminated = osl_createCondition();
679 Data.m_pProcImpl->m_pnext = NULL;
681 if (ChildListMutex == NULL)
682 ChildListMutex = osl_createMutex();
684 Data.m_started = osl_createCondition();
686 hThread = osl_createThread(ChildStatusProc, &Data);
688 osl_waitCondition(Data.m_started, NULL);
689 osl_destroyCondition(Data.m_started);
691 for (i = 0; Data.m_pszArgs[i] != NULL; i++)
692 free((void *)Data.m_pszArgs[i]);
694 for (i = 0; Data.m_pszEnv[i] != NULL; i++)
695 free((void *)Data.m_pszEnv[i]);
697 if ( Data.m_pszDir != 0 )
699 free((void *)Data.m_pszDir);
702 osl_destroyThread(hThread);
704 if (Data.m_pProcImpl->m_pid != 0)
706 *pProcess = Data.m_pProcImpl;
708 if (Options & osl_Process_WAIT)
709 osl_joinProcess(*pProcess);
711 return osl_Process_E_None;
714 osl_destroyCondition(Data.m_pProcImpl->m_terminated);
715 free(Data.m_pProcImpl);
717 return osl_Process_E_Unknown;
720 /******************************************************************************
722 * Functions for processes
724 *****************************************************************************/
727 oslProcessError SAL_CALL osl_terminateProcess(oslProcess Process)
729 if (Process == NULL)
730 return osl_Process_E_Unknown;
732 if (kill(((oslProcessImpl*)Process)->m_pid, SIGKILL) != 0)
734 switch (errno)
736 case EPERM:
737 return osl_Process_E_NoPermission;
739 case ESRCH:
740 return osl_Process_E_NotFound;
742 default:
743 return osl_Process_E_Unknown;
747 return osl_Process_E_None;
750 oslProcess SAL_CALL osl_getProcess(oslProcessIdentifier Ident)
752 oslProcessImpl *pProcImpl;
754 if (kill(Ident, 0) != -1)
756 oslProcessImpl* pChild;
758 if (ChildListMutex == NULL)
759 ChildListMutex = osl_createMutex();
761 osl_acquireMutex(ChildListMutex);
763 pChild = ChildList;
765 /* check if it is one of our child processes */
766 while (pChild != NULL)
768 if (Ident == (sal_uInt32) pChild->m_pid)
769 break;
771 pChild = pChild->m_pnext;
774 pProcImpl = (oslProcessImpl*) malloc(sizeof(oslProcessImpl));
775 pProcImpl->m_pid = Ident;
776 pProcImpl->m_terminated = osl_createCondition();
778 if (pChild != NULL)
780 /* process is a child so insert into list */
781 pProcImpl->m_pnext = pChild->m_pnext;
782 pChild->m_pnext = pProcImpl;
784 pProcImpl->m_status = pChild->m_status;
786 if (osl_checkCondition(pChild->m_terminated))
787 osl_setCondition(pProcImpl->m_terminated);
789 else
790 pProcImpl->m_pnext = NULL;
792 osl_releaseMutex(ChildListMutex);
794 else
795 pProcImpl = NULL;
797 return (pProcImpl);
800 void SAL_CALL osl_freeProcessHandle(oslProcess Process)
802 if (Process != NULL)
804 oslProcessImpl *pChild, *pPrev = NULL;
806 OSL_ASSERT(ChildListMutex != NULL);
808 if ( ChildListMutex == 0 )
810 return;
813 osl_acquireMutex(ChildListMutex);
815 pChild = ChildList;
817 /* remove process from child list */
818 while (pChild != NULL)
820 if (pChild == (oslProcessImpl*)Process)
822 if (pPrev != NULL)
823 pPrev->m_pnext = pChild->m_pnext;
824 else
825 ChildList = pChild->m_pnext;
827 break;
830 pPrev = pChild;
831 pChild = pChild->m_pnext;
834 osl_releaseMutex(ChildListMutex);
836 osl_destroyCondition(((oslProcessImpl*)Process)->m_terminated);
838 free(Process);
842 #if defined(LINUX)
843 struct osl_procStat
845 /* from 'stat' */
846 pid_t pid; /* pid */
847 char command[16]; /* 'argv[0]' */ /* mfe: it all right char comm[16] in kernel! */
848 char state; /* state (running, stopped, ...) */
849 pid_t ppid; /* parent pid */
850 pid_t pgrp; /* parent group */
851 int session; /* session ID */
852 int tty; /* no of tty */
853 pid_t tpgid; /* group of process owning the tty */
854 unsigned long flags; /* flags dunno */
855 unsigned long minflt; /* minor page faults */
856 unsigned long cminflt; /* minor page faults with children */
857 unsigned long majflt; /* major page faults */
858 unsigned long cmajflt; /* major page faults with children */
859 unsigned long utime; /* no of jiffies in user mode */
860 unsigned long stime; /* no of jiffies in kernel mode */
861 unsigned long cutime; /* no of jiffies in user mode with children */
862 unsigned long cstime; /* no of jiffies in kernel mode with children */
863 unsigned long priority; /* nice value + 15 (kernel scheduling prio)*/
864 long nice; /* nice value */
865 long timeout; /* no of jiffies of next process timeout */
866 long itrealvalue; /* no jiffies before next SIGALRM */
867 unsigned long starttime; /* process started this no of jiffies after boot */
868 unsigned long vsize; /* virtual memory size (in bytes) */
869 long rss; /* resident set size (in pages) */
870 unsigned long rss_rlim; /* rss limit (in bytes) */
871 unsigned long startcode; /* address above program text can run */
872 unsigned long endcode; /* address below program text can run */
873 unsigned long startstack; /* address of start of stack */
874 unsigned long kstkesp; /* current value of 'esp' (stack pointer) */
875 unsigned long kstkeip; /* current value of 'eip' (instruction pointer) */
876 /* mfe: Linux > 2.1.7x have more signals (88) */
877 char signal[24]; /* pending signals */
878 char blocked[24]; /* blocked signals */
879 char sigignore[24]; /* ignored signals */
880 char sigcatch[24]; /* catched signals */
881 unsigned long wchan; /* 'channel' the process is waiting in */
882 unsigned long nswap; /* ? */
883 unsigned long cnswap; /* ? */
885 /* from 'status' */
886 int ruid; /* real uid */
887 int euid; /* effective uid */
888 int suid; /* saved uid */
889 int fuid; /* file access uid */
890 int rgid; /* real gid */
891 int egid; /* effective gid */
892 int sgid; /* saved gid */
893 int fgid; /* file access gid */
894 unsigned long vm_size; /* like vsize but on kb */
895 unsigned long vm_lock; /* locked pages in kb */
896 unsigned long vm_rss; /* like rss but in kb */
897 unsigned long vm_data; /* data size */
898 unsigned long vm_stack; /* stack size */
899 unsigned long vm_exe; /* executable size */
900 unsigned long vm_lib; /* library size */
903 sal_Bool osl_getProcStat(pid_t pid, struct osl_procStat* procstat)
905 int fd = 0;
906 sal_Bool bRet = sal_False;
907 char name[PATH_MAX + 1];
908 snprintf(name, sizeof(name), "/proc/%u/stat", pid);
910 if ((fd = open(name,O_RDONLY)) >=0 )
912 char* tmp=0;
913 char prstatbuf[512];
914 memset(prstatbuf,0,512);
915 bRet = safeRead(fd, prstatbuf, 511);
917 close(fd);
919 if (!bRet)
920 return sal_False;
922 tmp = strrchr(prstatbuf, ')');
923 *tmp = '\0';
924 memset(procstat->command, 0, sizeof(procstat->command));
926 sscanf(prstatbuf, "%d (%15c", &procstat->pid, procstat->command);
927 sscanf(tmp + 2,
928 "%c"
929 "%i %i %i %i %i"
930 "%lu %lu %lu %lu %lu"
931 "%lu %lu %lu %lu"
932 "%lu %li %li %li"
933 "%lu %lu %li %lu"
934 "%lu %lu %lu %lu %lu"
935 "%s %s %s %s"
936 "%lu %lu %lu",
937 &procstat->state,
938 &procstat->ppid, &procstat->pgrp, &procstat->session, &procstat->tty, &procstat->tpgid,
939 &procstat->flags, &procstat->minflt, &procstat->cminflt, &procstat->majflt, &procstat->cmajflt,
940 &procstat->utime, &procstat->stime, &procstat->cutime, &procstat->cstime,
941 &procstat->priority, &procstat->nice, &procstat->timeout, &procstat->itrealvalue,
942 &procstat->starttime, &procstat->vsize, &procstat->rss, &procstat->rss_rlim,
943 &procstat->startcode, &procstat->endcode, &procstat->startstack, &procstat->kstkesp, &procstat->kstkeip,
944 procstat->signal, procstat->blocked, procstat->sigignore, procstat->sigcatch,
945 &procstat->wchan, &procstat->nswap, &procstat->cnswap
948 return bRet;
951 sal_Bool osl_getProcStatus(pid_t pid, struct osl_procStat* procstat)
953 int fd = 0;
954 char name[PATH_MAX + 1];
955 sal_Bool bRet = sal_False;
957 snprintf(name, sizeof(name), "/proc/%u/status", pid);
959 if ((fd = open(name,O_RDONLY)) >=0 )
961 char* tmp=0;
962 char prstatusbuf[512];
963 memset(prstatusbuf,0,512);
964 bRet = safeRead(fd, prstatusbuf, 511);
966 close(fd);
968 if (!bRet)
969 return sal_False;
971 tmp = strstr(prstatusbuf,"Uid:");
972 if(tmp)
974 sscanf(tmp,"Uid:\t%d\t%d\t%d\t%d",
975 &procstat->ruid, &procstat->euid, &procstat->suid, &procstat->fuid
980 tmp = strstr(prstatusbuf,"Gid:");
981 if(tmp)
983 sscanf(tmp,"Gid:\t%d\t%d\t%d\t%d",
984 &procstat->rgid, &procstat->egid, &procstat->sgid, &procstat->fgid
988 tmp = strstr(prstatusbuf,"VmSize:");
989 if(tmp)
991 sscanf(tmp,
992 "VmSize: %lu kB\n"
993 "VmLck: %lu kB\n"
994 "VmRSS: %lu kB\n"
995 "VmData: %lu kB\n"
996 "VmStk: %lu kB\n"
997 "VmExe: %lu kB\n"
998 "VmLib: %lu kB\n",
999 &procstat->vm_size, &procstat->vm_lock, &procstat->vm_rss, &procstat->vm_data,
1000 &procstat->vm_stack, &procstat->vm_exe, &procstat->vm_lib
1004 tmp = strstr(prstatusbuf,"SigPnd:");
1005 if(tmp)
1007 sscanf(tmp, "SigPnd: %s SigBlk: %s SigIgn: %s %*s %s",
1008 procstat->signal, procstat->blocked, procstat->sigignore, procstat->sigcatch
1012 return bRet;
1015 #endif
1017 oslProcessError SAL_CALL osl_getProcessInfo(oslProcess Process, oslProcessData Fields, oslProcessInfo* pInfo)
1019 pid_t pid;
1021 if (Process == NULL)
1022 pid = getpid();
1023 else
1024 pid = ((oslProcessImpl*)Process)->m_pid;
1026 if (! pInfo || (pInfo->Size != sizeof(oslProcessInfo)))
1027 return osl_Process_E_Unknown;
1029 pInfo->Fields = 0;
1031 if (Fields & osl_Process_IDENTIFIER)
1033 pInfo->Ident = pid;
1034 pInfo->Fields |= osl_Process_IDENTIFIER;
1037 if (Fields & osl_Process_EXITCODE)
1039 if ((Process != NULL) &&
1040 osl_checkCondition(((oslProcessImpl*)Process)->m_terminated))
1042 pInfo->Code = ((oslProcessImpl*)Process)->m_status;
1043 pInfo->Fields |= osl_Process_EXITCODE;
1047 if (Fields & (osl_Process_HEAPUSAGE | osl_Process_CPUTIMES))
1050 #if defined(SOLARIS)
1052 int fd;
1053 sal_Char name[PATH_MAX + 1];
1055 snprintf(name, sizeof(name), "/proc/%u", pid);
1057 if ((fd = open(name, O_RDONLY)) >= 0)
1059 prstatus_t prstatus;
1061 if (ioctl(fd, PIOCSTATUS, &prstatus) >= 0)
1063 if (Fields & osl_Process_CPUTIMES)
1065 pInfo->UserTime.Seconds = prstatus.pr_utime.tv_sec;
1066 pInfo->UserTime.Nanosec = prstatus.pr_utime.tv_nsec;
1067 pInfo->SystemTime.Seconds = prstatus.pr_stime.tv_sec;
1068 pInfo->SystemTime.Nanosec = prstatus.pr_stime.tv_nsec;
1070 pInfo->Fields |= osl_Process_CPUTIMES;
1073 if (Fields & osl_Process_HEAPUSAGE)
1075 pInfo->HeapUsage = prstatus.pr_brksize;
1077 pInfo->Fields |= osl_Process_HEAPUSAGE;
1080 close(fd);
1082 return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
1084 else
1085 close(fd);
1088 #elif defined(LINUX)
1090 if ( (Fields & osl_Process_CPUTIMES) || (Fields & osl_Process_HEAPUSAGE) )
1092 struct osl_procStat procstat;
1093 memset(&procstat,0,sizeof(procstat));
1095 if ( (Fields & osl_Process_CPUTIMES) && osl_getProcStat(pid, &procstat) )
1098 * mfe:
1099 * We calculate only time of the process proper.
1100 * Threads are processes, we do not consider their time here!
1101 * (For this, cutime and cstime should be used, it seems not
1102 * to work in 2.0.36)
1105 long clktck;
1106 unsigned long hz;
1107 unsigned long userseconds;
1108 unsigned long systemseconds;
1110 clktck = sysconf(_SC_CLK_TCK);
1111 if (clktck < 0) {
1112 return osl_Process_E_Unknown;
1114 hz = (unsigned long) clktck;
1116 userseconds = procstat.utime/hz;
1117 systemseconds = procstat.stime/hz;
1119 pInfo->UserTime.Seconds = userseconds;
1120 pInfo->UserTime.Nanosec = procstat.utime - (userseconds * hz);
1121 pInfo->SystemTime.Seconds = systemseconds;
1122 pInfo->SystemTime.Nanosec = procstat.stime - (systemseconds * hz);
1124 pInfo->Fields |= osl_Process_CPUTIMES;
1127 if ( (Fields & osl_Process_HEAPUSAGE) && osl_getProcStatus(pid, &procstat) )
1130 * mfe:
1131 * vm_data (found in status) shows the size of the data segment
1132 * it a rough approximation of the core heap size
1134 pInfo->HeapUsage = procstat.vm_data*1024;
1136 pInfo->Fields |= osl_Process_HEAPUSAGE;
1140 return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
1141 #endif
1145 return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
1149 /***********************************************
1150 helper function for osl_joinProcessWithTimeout
1151 **********************************************/
1153 static int is_timeout(const struct timeval* tend)
1155 struct timeval tcurrent;
1156 gettimeofday(&tcurrent, NULL);
1157 return (tcurrent.tv_sec >= tend->tv_sec);
1160 /**********************************************
1161 kill(pid, 0) is useful for checking if a
1162 process is still alive, but remember that
1163 kill even returns 0 if the process is already
1164 a zombie.
1165 *********************************************/
1167 static int is_process_dead(pid_t pid)
1169 return ((-1 == kill(pid, 0)) && (ESRCH == errno));
1172 /**********************************************
1173 osl_joinProcessWithTimeout
1174 *********************************************/
1176 oslProcessError SAL_CALL osl_joinProcessWithTimeout(oslProcess Process, const TimeValue* pTimeout)
1178 oslProcessImpl* pChild = ChildList;
1179 oslProcessError osl_error = osl_Process_E_None;
1181 OSL_PRECOND(Process, "osl_joinProcess: Invalid parameter");
1182 OSL_ASSERT(ChildListMutex);
1184 if (NULL == Process || 0 == ChildListMutex)
1185 return osl_Process_E_Unknown;
1187 osl_acquireMutex(ChildListMutex);
1189 /* check if process is a child of ours */
1190 while (pChild != NULL)
1192 if (pChild == (oslProcessImpl*)Process)
1193 break;
1195 pChild = pChild->m_pnext;
1198 osl_releaseMutex(ChildListMutex);
1200 if (pChild != NULL)
1202 oslConditionResult cond_res = osl_waitCondition(pChild->m_terminated, pTimeout);
1204 if (osl_cond_result_timeout == cond_res)
1205 osl_error = osl_Process_E_TimedOut;
1206 else if (osl_cond_result_ok != cond_res)
1207 osl_error = osl_Process_E_Unknown;
1209 else /* alien process; StatusThread will not be able
1210 to set the condition terminated */
1212 pid_t pid = ((oslProcessImpl*)Process)->m_pid;
1214 if (pTimeout)
1216 int timeout = 0;
1217 struct timeval tend;
1219 gettimeofday(&tend, NULL);
1221 tend.tv_sec += pTimeout->Seconds;
1223 while (!is_process_dead(pid) && ((timeout = is_timeout(&tend)) == 0))
1224 sleep(1);
1226 if (timeout)
1227 osl_error = osl_Process_E_TimedOut;
1229 else /* infinite */
1231 while (!is_process_dead(pid))
1232 sleep(1);
1235 return osl_error;
1238 oslProcessError SAL_CALL osl_joinProcess(oslProcess Process)
1240 return osl_joinProcessWithTimeout(Process, NULL);
1243 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */