cid#1640468 Dereference after null check
[LibreOffice.git] / sal / osl / unx / process.cxx
blob13320263c65aba1a6cb6bf8ce1cb6afee74af675
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>
24 #include <fcntl.h>
25 #include <limits.h>
26 #include <sys/time.h>
27 #include <sys/wait.h>
28 #include <unistd.h>
31 * ToDo:
32 * - cleanup of process status things
33 * - cleanup of process spawning
34 * - cleanup of resource transfer
37 #if defined(__sun)
38 // The procfs may only be used without LFS in 32bits.
39 # ifdef _FILE_OFFSET_BITS
40 # undef _FILE_OFFSET_BITS
41 # endif
42 #endif
44 #if defined(FREEBSD) || defined(NETBSD) || defined(DRAGONFLY)
45 #include <machine/param.h>
46 #endif
48 #ifdef IOS
49 #include <signal.h>
50 #endif
52 #include "system.hxx"
53 #include "unixerrnostring.hxx"
54 #if defined(__sun)
55 # include <sys/procfs.h>
56 #endif
57 #include <osl/diagnose.h>
58 #include <osl/mutex.h>
59 #include <osl/process.h>
60 #include <osl/conditn.h>
61 #include <osl/thread.h>
62 #include <osl/file.h>
63 #include <osl/file.hxx>
64 #include <sal/log.hxx>
66 #include "createfilehandlefromfd.hxx"
67 #include "file_url.hxx"
68 #include "readwrite_helper.hxx"
69 #include "secimpl.hxx"
71 #define MAX_ARGS 255
72 #define MAX_ENVS 255
74 namespace
77 struct oslProcessImpl {
78 pid_t m_pid;
79 oslCondition m_terminated;
80 int m_status;
81 oslProcessImpl* m_pnext;
84 struct ProcessData
86 const char* m_pszArgs[MAX_ARGS + 1];
87 const char* m_pszDir;
88 char* m_pszEnv[MAX_ENVS + 1];
89 uid_t m_uid;
90 gid_t m_gid;
91 char* m_name;
92 oslCondition m_started;
93 oslProcessImpl* m_pProcImpl;
94 oslFileHandle *m_pInputWrite;
95 oslFileHandle *m_pOutputRead;
96 oslFileHandle *m_pErrorRead;
99 oslProcessImpl* ChildList;
100 oslMutex ChildListMutex;
102 } //Anonymous namespace
104 static oslProcessError osl_psz_executeProcess(char *pszImageName,
105 char *pszArguments[],
106 oslProcessOption Options,
107 oslSecurity Security,
108 char *pszDirectory,
109 char *pszEnvironments[],
110 oslProcess *pProcess,
111 oslFileHandle *pInputWrite,
112 oslFileHandle *pOutputRead,
113 oslFileHandle *pErrorRead );
115 extern "C" {
117 static void ChildStatusProc(void *pData)
119 osl_setThreadName("osl_executeProcess");
121 pid_t pid = -1;
122 int status = 0;
123 int channel[2] = { -1, -1 };
124 ProcessData data;
125 ProcessData *pdata;
126 int stdOutput[2] = { -1, -1 }, stdInput[2] = { -1, -1 }, stdError[2] = { -1, -1 };
128 pdata = static_cast<ProcessData *>(pData);
130 /* make a copy of our data, because forking will only copy
131 our local stack of the thread, so the process data will not be accessible
132 in our child process */
133 memcpy(&data, pData, sizeof(data));
135 #ifdef NO_CHILD_PROCESSES
136 #define fork() (errno = EINVAL, -1)
137 #endif
138 if (socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == -1)
140 status = errno;
141 SAL_WARN("sal.osl", "executeProcess socketpair() errno " << status);
144 (void) fcntl(channel[0], F_SETFD, FD_CLOEXEC);
145 (void) fcntl(channel[1], F_SETFD, FD_CLOEXEC);
147 /* Create redirected IO pipes */
148 if ( status == 0 && data.m_pInputWrite && pipe( stdInput ) == -1 )
150 status = errno;
151 assert(status != 0);
152 SAL_WARN("sal.osl", "executeProcess pipe(stdInput) errno " << status);
155 if ( status == 0 && data.m_pOutputRead && pipe( stdOutput ) == -1 )
157 status = errno;
158 assert(status != 0);
159 SAL_WARN("sal.osl", "executeProcess pipe(stdOutput) errno " << status);
162 if ( status == 0 && data.m_pErrorRead && pipe( stdError ) == -1 )
164 status = errno;
165 assert(status != 0);
166 SAL_WARN("sal.osl", "executeProcess pipe(stdError) errno " << status);
169 if ( (status == 0) && ((pid = fork()) == 0) )
171 /* Child */
172 int chstatus = 0;
173 int errno_copy;
175 if (channel[0] != -1) close(channel[0]);
177 if ((data.m_uid != uid_t(-1)) && ((data.m_uid != getuid()) || (data.m_gid != getgid())))
179 OSL_ASSERT(geteuid() == 0); /* must be root */
181 if (! INIT_GROUPS(data.m_name, data.m_gid) || (setuid(data.m_uid) != 0))
183 // ignore; can't do much about it here after fork
186 unsetenv("HOME");
189 if (data.m_pszDir)
190 chstatus = chdir(data.m_pszDir);
192 if (chstatus == 0 && ((data.m_uid == uid_t(-1)) || ((data.m_uid == getuid()) && (data.m_gid == getgid()))))
194 int i;
195 for (i = 0; data.m_pszEnv[i] != nullptr; i++)
197 if (strchr(data.m_pszEnv[i], '=') == nullptr)
199 unsetenv(data.m_pszEnv[i]); /*TODO: check error return*/
201 else
203 putenv(data.m_pszEnv[i]); /*TODO: check error return*/
207 /* Connect std IO to pipe ends */
209 /* Write end of stdInput not used in child process */
210 if (stdInput[1] != -1) close( stdInput[1] );
212 /* Read end of stdOutput not used in child process */
213 if (stdOutput[0] != -1) close( stdOutput[0] );
215 /* Read end of stdError not used in child process */
216 if (stdError[0] != -1) close( stdError[0] );
218 /* Redirect pipe ends to std IO */
220 if ( stdInput[0] != STDIN_FILENO )
222 dup2( stdInput[0], STDIN_FILENO );
223 if (stdInput[0] != -1) close( stdInput[0] );
226 if ( stdOutput[1] != STDOUT_FILENO )
228 dup2( stdOutput[1], STDOUT_FILENO );
229 if (stdOutput[1] != -1) close( stdOutput[1] );
232 if ( stdError[1] != STDERR_FILENO )
234 dup2( stdError[1], STDERR_FILENO );
235 if (stdError[1] != -1) close( stdError[1] );
238 // No need to check the return value of execv. If we return from
239 // it, an error has occurred.
240 execv(data.m_pszArgs[0], const_cast<char **>(data.m_pszArgs));
243 /* if we reach here, something went wrong */
244 errno_copy = errno;
245 if ( !safeWrite(channel[1], &errno_copy, sizeof(errno_copy)) )
247 // ignore; can't do much about it here after fork
250 if ( channel[1] != -1 )
251 close(channel[1]);
253 _exit(255);
255 else
256 { /* Parent */
257 int i = -1;
258 if (channel[1] != -1) close(channel[1]);
260 /* Close unused pipe ends */
261 if (stdInput[0] != -1) close( stdInput[0] );
262 if (stdOutput[1] != -1) close( stdOutput[1] );
263 if (stdError[1] != -1) close( stdError[1] );
265 if (pid > 0)
267 while ((i = read(channel[0], &status, sizeof(status))) < 0)
269 if (errno != EINTR)
270 break;
274 if (channel[0] != -1) close(channel[0]);
276 if ((pid > 0) && (i == 0))
278 pid_t child_pid;
279 osl_acquireMutex(ChildListMutex);
281 pdata->m_pProcImpl->m_pid = pid;
282 pdata->m_pProcImpl->m_pnext = ChildList;
283 ChildList = pdata->m_pProcImpl;
285 /* Store used pipe ends in data structure */
287 if ( pdata->m_pInputWrite )
288 *(pdata->m_pInputWrite) = osl::detail::createFileHandleFromFD( stdInput[1] );
290 if ( pdata->m_pOutputRead )
291 *(pdata->m_pOutputRead) = osl::detail::createFileHandleFromFD( stdOutput[0] );
293 if ( pdata->m_pErrorRead )
294 *(pdata->m_pErrorRead) = osl::detail::createFileHandleFromFD( stdError[0] );
296 osl_releaseMutex(ChildListMutex);
298 osl_setCondition(pdata->m_started);
302 child_pid = waitpid(pid, &status, 0);
303 } while ( 0 > child_pid && EINTR == errno );
305 if ( child_pid < 0)
307 SAL_WARN("sal.osl", "Failed to wait for child process: " << UnixErrnoString(errno));
310 We got another error than EINTR. Anyway we have to wake up the
311 waiting thread under any circumstances */
313 child_pid = pid;
316 if ( child_pid > 0 )
318 oslProcessImpl* pChild;
320 osl_acquireMutex(ChildListMutex);
322 pChild = ChildList;
324 /* check if it is one of our child processes */
325 while (pChild != nullptr)
327 if (pChild->m_pid == child_pid)
329 if (WIFEXITED(status))
330 pChild->m_status = WEXITSTATUS(status);
331 else if (WIFSIGNALED(status))
332 pChild->m_status = 128 + WTERMSIG(status);
333 else
334 pChild->m_status = -1;
336 // coverity[lock_order : FALSE] - incorrect report of lock order error
337 osl_setCondition(pChild->m_terminated);
340 pChild = pChild->m_pnext;
343 osl_releaseMutex(ChildListMutex);
346 else
348 SAL_WARN("sal.osl", "ChildStatusProc : starting '" << data.m_pszArgs[0] << "' failed");
349 SAL_WARN("sal.osl", "Failed to launch child process, child reports " << UnixErrnoString(status));
351 /* Close pipe ends */
352 if ( pdata->m_pInputWrite )
353 *pdata->m_pInputWrite = nullptr;
355 if ( pdata->m_pOutputRead )
356 *pdata->m_pOutputRead = nullptr;
358 if ( pdata->m_pErrorRead )
359 *pdata->m_pErrorRead = nullptr;
361 if (stdInput[1] != -1) close( stdInput[1] );
362 if (stdOutput[0] != -1) close( stdOutput[0] );
363 if (stdError[0] != -1) close( stdError[0] );
365 /* if pid > 0 then a process was created, even if it later failed
366 e.g. bash searching for a command to execute, and we still
367 need to clean it up to avoid "defunct" processes */
368 if (pid > 0)
370 pid_t child_pid;
373 child_pid = waitpid(pid, &status, 0);
374 } while ( 0 > child_pid && EINTR == errno );
377 /* notify (and unblock) parent thread */
378 osl_setCondition(pdata->m_started);
385 oslProcessError SAL_CALL osl_executeProcess_WithRedirectedIO(
386 rtl_uString *ustrImageName,
387 rtl_uString *ustrArguments[],
388 sal_uInt32 nArguments,
389 oslProcessOption Options,
390 oslSecurity Security,
391 rtl_uString *ustrWorkDir,
392 rtl_uString *ustrEnvironment[],
393 sal_uInt32 nEnvironmentVars,
394 oslProcess *pProcess,
395 oslFileHandle *pInputWrite,
396 oslFileHandle *pOutputRead,
397 oslFileHandle *pErrorRead
400 OUString image;
401 if (ustrImageName == nullptr)
403 if (nArguments == 0)
405 return osl_Process_E_InvalidError;
407 image = OUString::unacquired(ustrArguments);
409 else
411 osl::FileBase::RC e = osl::FileBase::getSystemPathFromFileURL(
412 OUString::unacquired(&ustrImageName), image);
413 if (e != osl::FileBase::E_None)
415 SAL_INFO(
416 "sal.osl",
417 "getSystemPathFromFileURL("
418 << OUString::unacquired(&ustrImageName)
419 << ") failed with " << e);
420 return osl_Process_E_Unknown;
424 if ((Options & osl_Process_SEARCHPATH) != 0)
426 OUString path;
427 if (osl::detail::find_in_PATH(image, path))
429 image = path;
433 oslProcessError Error;
434 char* pszWorkDir=nullptr;
435 char** pArguments=nullptr;
436 char** pEnvironment=nullptr;
437 unsigned int idx;
439 char szImagePath[PATH_MAX] = "";
440 if (!image.isEmpty()
441 && (UnicodeToText(
442 szImagePath, SAL_N_ELEMENTS(szImagePath), image.getStr(),
443 image.getLength())
444 == 0))
446 int e = errno;
447 SAL_INFO("sal.osl", "UnicodeToText(" << image << ") failed with " << e);
448 return osl_Process_E_Unknown;
451 char szWorkDir[PATH_MAX] = "";
452 if ( ustrWorkDir != nullptr && ustrWorkDir->length )
454 oslFileError e = FileURLToPath( szWorkDir, PATH_MAX, ustrWorkDir );
455 if (e != osl_File_E_None)
457 SAL_INFO(
458 "sal.osl",
459 "FileURLToPath(" << OUString::unacquired(&ustrWorkDir)
460 << ") failed with " << e);
461 return osl_Process_E_Unknown;
463 pszWorkDir = szWorkDir;
466 if ( nArguments > 0 )
468 pArguments = static_cast<char**>(malloc( ( nArguments + 2 ) * sizeof(char*) ));
471 for ( idx = 0 ; idx < nArguments ; ++idx )
473 rtl_String* strArg =nullptr;
475 rtl_uString2String( &strArg,
476 rtl_uString_getStr(ustrArguments[idx]),
477 rtl_uString_getLength(ustrArguments[idx]),
478 osl_getThreadTextEncoding(),
479 OUSTRING_TO_OSTRING_CVTFLAGS );
481 pArguments[idx]=strdup(rtl_string_getStr(strArg));
482 rtl_string_release(strArg);
483 pArguments[idx+1]=nullptr;
486 for ( idx = 0 ; idx < nEnvironmentVars ; ++idx )
488 rtl_String* strEnv=nullptr;
490 if ( pEnvironment == nullptr )
492 pEnvironment = static_cast<char**>(malloc( ( nEnvironmentVars + 2 ) * sizeof(char*) ));
495 rtl_uString2String( &strEnv,
496 rtl_uString_getStr(ustrEnvironment[idx]),
497 rtl_uString_getLength(ustrEnvironment[idx]),
498 osl_getThreadTextEncoding(),
499 OUSTRING_TO_OSTRING_CVTFLAGS );
501 pEnvironment[idx]=strdup(rtl_string_getStr(strEnv));
502 rtl_string_release(strEnv);
503 pEnvironment[idx+1]=nullptr;
506 Error = osl_psz_executeProcess(szImagePath,
507 pArguments,
508 Options,
509 Security,
510 pszWorkDir,
511 pEnvironment,
512 pProcess,
513 pInputWrite,
514 pOutputRead,
515 pErrorRead
518 if ( pArguments != nullptr )
520 for ( idx = 0 ; idx < nArguments ; ++idx )
522 if ( pArguments[idx] != nullptr )
524 free(pArguments[idx]);
527 free(pArguments);
530 if ( pEnvironment != nullptr )
532 for ( idx = 0 ; idx < nEnvironmentVars ; ++idx )
534 if ( pEnvironment[idx] != nullptr )
536 free(pEnvironment[idx]);
539 free(pEnvironment);
542 return Error;
545 oslProcessError SAL_CALL osl_executeProcess(
546 rtl_uString *ustrImageName,
547 rtl_uString *ustrArguments[],
548 sal_uInt32 nArguments,
549 oslProcessOption Options,
550 oslSecurity Security,
551 rtl_uString *ustrWorkDir,
552 rtl_uString *ustrEnvironment[],
553 sal_uInt32 nEnvironmentVars,
554 oslProcess *pProcess
557 return osl_executeProcess_WithRedirectedIO(
558 ustrImageName,
559 ustrArguments,
560 nArguments,
561 Options,
562 Security,
563 ustrWorkDir,
564 ustrEnvironment,
565 nEnvironmentVars,
566 pProcess,
567 nullptr,
568 nullptr,
569 nullptr
573 oslProcessError osl_psz_executeProcess(char *pszImageName,
574 char *pszArguments[],
575 oslProcessOption Options,
576 oslSecurity Security,
577 char *pszDirectory,
578 char *pszEnvironments[],
579 oslProcess *pProcess,
580 oslFileHandle *pInputWrite,
581 oslFileHandle *pOutputRead,
582 oslFileHandle *pErrorRead
585 int i;
586 ProcessData Data;
587 oslThread hThread;
589 memset(&Data,0,sizeof(ProcessData));
590 Data.m_pInputWrite = pInputWrite;
591 Data.m_pOutputRead = pOutputRead;
592 Data.m_pErrorRead = pErrorRead;
594 OSL_ASSERT(pszImageName != nullptr);
596 if ( pszImageName == nullptr )
598 return osl_Process_E_NotFound;
601 Data.m_pszArgs[0] = strdup(pszImageName);
602 Data.m_pszArgs[1] = nullptr;
604 if ( pszArguments != nullptr )
606 for (i = 0; ((i + 2) < MAX_ARGS) && (pszArguments[i] != nullptr); i++)
607 Data.m_pszArgs[i+1] = strdup(pszArguments[i]);
608 Data.m_pszArgs[i+2] = nullptr;
611 Data.m_pszDir = (pszDirectory != nullptr) ? strdup(pszDirectory) : nullptr;
613 if (pszEnvironments != nullptr)
615 for (i = 0; ((i + 1) < MAX_ENVS) && (pszEnvironments[i] != nullptr); i++)
616 Data.m_pszEnv[i] = strdup(pszEnvironments[i]);
617 Data.m_pszEnv[i+1] = nullptr;
619 else
620 Data.m_pszEnv[0] = nullptr;
622 if (Security != nullptr)
624 Data.m_uid = static_cast<oslSecurityImpl*>(Security)->m_pPasswd.pw_uid;
625 Data.m_gid = static_cast<oslSecurityImpl*>(Security)->m_pPasswd.pw_gid;
626 Data.m_name = static_cast<oslSecurityImpl*>(Security)->m_pPasswd.pw_name;
628 else
629 Data.m_uid = uid_t(-1);
631 Data.m_pProcImpl = static_cast<oslProcessImpl*>(malloc(sizeof(oslProcessImpl)));
632 Data.m_pProcImpl->m_pid = 0;
633 Data.m_pProcImpl->m_terminated = osl_createCondition();
634 Data.m_pProcImpl->m_pnext = nullptr;
636 if (ChildListMutex == nullptr)
637 ChildListMutex = osl_createMutex();
639 Data.m_started = osl_createCondition();
641 hThread = osl_createThread(ChildStatusProc, &Data);
643 if (hThread != nullptr)
645 osl_waitCondition(Data.m_started, nullptr);
647 osl_destroyCondition(Data.m_started);
649 for (i = 0; Data.m_pszArgs[i] != nullptr; i++)
650 free(const_cast<char *>(Data.m_pszArgs[i]));
652 for (i = 0; Data.m_pszEnv[i] != nullptr; i++)
653 free(Data.m_pszEnv[i]);
655 if ( Data.m_pszDir != nullptr )
657 free(const_cast<char *>(Data.m_pszDir));
660 osl_destroyThread(hThread);
662 if (Data.m_pProcImpl->m_pid != 0)
664 assert(hThread != nullptr);
666 *pProcess = Data.m_pProcImpl;
668 if (Options & osl_Process_WAIT)
669 osl_joinProcess(*pProcess);
671 return osl_Process_E_None;
674 osl_destroyCondition(Data.m_pProcImpl->m_terminated);
675 free(Data.m_pProcImpl);
677 return osl_Process_E_Unknown;
680 oslProcessError SAL_CALL osl_terminateProcess(oslProcess Process)
682 if (Process == nullptr)
683 return osl_Process_E_Unknown;
685 if (kill(static_cast<oslProcessImpl*>(Process)->m_pid, SIGKILL) != 0)
687 switch (errno)
689 case EPERM:
690 return osl_Process_E_NoPermission;
692 case ESRCH:
693 return osl_Process_E_NotFound;
695 default:
696 return osl_Process_E_Unknown;
700 return osl_Process_E_None;
703 oslProcess SAL_CALL osl_getProcess(oslProcessIdentifier Ident)
705 oslProcessImpl *pProcImpl;
707 if (kill(Ident, 0) != -1)
709 oslProcessImpl* pChild;
711 if (ChildListMutex == nullptr)
712 ChildListMutex = osl_createMutex();
714 osl_acquireMutex(ChildListMutex);
716 pChild = ChildList;
718 /* check if it is one of our child processes */
719 while (pChild != nullptr)
721 if (Ident == static_cast<sal_uInt32>(pChild->m_pid))
722 break;
724 pChild = pChild->m_pnext;
727 pProcImpl = static_cast<oslProcessImpl*>(malloc(sizeof(oslProcessImpl)));
728 pProcImpl->m_pid = Ident;
729 pProcImpl->m_terminated = osl_createCondition();
731 if (pChild != nullptr)
733 /* process is a child so insert into list */
734 pProcImpl->m_pnext = pChild->m_pnext;
735 pChild->m_pnext = pProcImpl;
737 pProcImpl->m_status = pChild->m_status;
739 // coverity[lock_order : FALSE] - incorrect report of lock order error
740 if (osl_checkCondition(pChild->m_terminated))
742 // coverity[lock_order : FALSE] - incorrect report of lock order error
743 osl_setCondition(pProcImpl->m_terminated);
746 else
747 pProcImpl->m_pnext = nullptr;
749 osl_releaseMutex(ChildListMutex);
751 else
752 pProcImpl = nullptr;
754 return pProcImpl;
757 void SAL_CALL osl_freeProcessHandle(oslProcess Process)
759 if (Process == nullptr)
760 return;
762 oslProcessImpl *pChild, *pPrev = nullptr;
764 OSL_ASSERT(ChildListMutex != nullptr);
766 if ( ChildListMutex == nullptr )
768 return;
771 osl_acquireMutex(ChildListMutex);
773 pChild = ChildList;
775 /* remove process from child list */
776 while (pChild != nullptr)
778 if (pChild == static_cast<oslProcessImpl*>(Process))
780 if (pPrev != nullptr)
781 pPrev->m_pnext = pChild->m_pnext;
782 else
783 ChildList = pChild->m_pnext;
785 break;
788 pPrev = pChild;
789 pChild = pChild->m_pnext;
792 osl_releaseMutex(ChildListMutex);
794 osl_destroyCondition(static_cast<oslProcessImpl*>(Process)->m_terminated);
796 free(Process);
799 #if defined(LINUX)
800 namespace {
802 struct osl_procStat
804 /* from 'stat' */
805 pid_t pid; /* pid */
806 char command[16]; /* 'argv[0]' */ /* mfe: it all right char comm[16] in kernel! */
807 char state; /* state (running, stopped, ...) */
808 pid_t ppid; /* parent pid */
809 pid_t pgrp; /* parent group */
810 int session; /* session ID */
811 int tty; /* no of tty */
812 pid_t tpgid; /* group of process owning the tty */
813 unsigned long flags; /* flags dunno */
814 unsigned long minflt; /* minor page faults */
815 unsigned long cminflt; /* minor page faults with children */
816 unsigned long majflt; /* major page faults */
817 unsigned long cmajflt; /* major page faults with children */
818 unsigned long utime; /* no of jiffies in user mode */
819 unsigned long stime; /* no of jiffies in kernel mode */
820 unsigned long cutime; /* no of jiffies in user mode with children */
821 unsigned long cstime; /* no of jiffies in kernel mode with children */
822 unsigned long priority; /* nice value + 15 (kernel scheduling prio)*/
823 long nice; /* nice value */
824 long timeout; /* no of jiffies of next process timeout */
825 long itrealvalue; /* no jiffies before next SIGALRM */
826 unsigned long starttime; /* process started this no of jiffies after boot */
827 unsigned long vsize; /* virtual memory size (in bytes) */
828 long rss; /* resident set size (in pages) */
829 unsigned long rss_rlim; /* rss limit (in bytes) */
830 unsigned long startcode; /* address above program text can run */
831 unsigned long endcode; /* address below program text can run */
832 unsigned long startstack; /* address of start of stack */
833 unsigned long kstkesp; /* current value of 'esp' (stack pointer) */
834 unsigned long kstkeip; /* current value of 'eip' (instruction pointer) */
835 /* mfe: Linux > 2.1.7x have more signals (88) */
836 char signal[24]; /* pending signals */
837 char blocked[24]; /* blocked signals */
838 char sigignore[24]; /* ignored signals */
839 char sigcatch[24]; /* caught signals */
840 unsigned long wchan; /* 'channel' the process is waiting in */
841 unsigned long nswap; /* ? */
842 unsigned long cnswap; /* ? */
844 /* from 'status' */
845 int ruid; /* real uid */
846 int euid; /* effective uid */
847 int suid; /* saved uid */
848 int fuid; /* file access uid */
849 int rgid; /* real gid */
850 int egid; /* effective gid */
851 int sgid; /* saved gid */
852 int fgid; /* file access gid */
853 unsigned long vm_size; /* like vsize but on kb */
854 unsigned long vm_lock; /* locked pages in kb */
855 unsigned long vm_rss; /* like rss but in kb */
856 unsigned long vm_data; /* data size */
857 unsigned long vm_stack; /* stack size */
858 unsigned long vm_exe; /* executable size */
859 unsigned long vm_lib; /* library size */
864 static bool osl_getProcStat(pid_t pid, struct osl_procStat* procstat)
866 int fd = 0;
867 bool bRet = false;
868 char name[PATH_MAX + 1];
869 snprintf(name, sizeof(name), "/proc/%u/stat", pid);
871 if ((fd = open(name,O_RDONLY)) >=0 )
873 char* tmp=nullptr;
874 char prstatbuf[512];
875 memset(prstatbuf,0,512);
876 bRet = safeRead(fd, prstatbuf, 511);
878 close(fd);
880 if (!bRet)
881 return false;
883 tmp = strrchr(prstatbuf, ')');
884 if(tmp)
886 *tmp = '\0';
888 memset(procstat->command, 0, sizeof(procstat->command));
890 sscanf(prstatbuf, "%d (%15c", &procstat->pid, procstat->command);
891 sscanf(tmp + 2,
892 "%c"
893 "%i %i %i %i %i"
894 "%lu %lu %lu %lu %lu"
895 "%lu %lu %lu %lu"
896 "%lu %li %li %li"
897 "%lu %lu %li %lu"
898 "%lu %lu %lu %lu %lu"
899 "%23s %23s %23s %23s"
900 "%lu %lu %lu",
901 &procstat->state,
902 &procstat->ppid, &procstat->pgrp, &procstat->session, &procstat->tty, &procstat->tpgid,
903 &procstat->flags, &procstat->minflt, &procstat->cminflt, &procstat->majflt, &procstat->cmajflt,
904 &procstat->utime, &procstat->stime, &procstat->cutime, &procstat->cstime,
905 &procstat->priority, &procstat->nice, &procstat->timeout, &procstat->itrealvalue,
906 &procstat->starttime, &procstat->vsize, &procstat->rss, &procstat->rss_rlim,
907 &procstat->startcode, &procstat->endcode, &procstat->startstack, &procstat->kstkesp, &procstat->kstkeip,
908 procstat->signal, procstat->blocked, procstat->sigignore, procstat->sigcatch,
909 &procstat->wchan, &procstat->nswap, &procstat->cnswap
912 else
914 bRet = false;
917 return bRet;
920 static bool osl_getProcStatus(pid_t pid, struct osl_procStat* procstat)
922 int fd = 0;
923 char name[PATH_MAX + 1];
924 bool bRet = false;
926 snprintf(name, sizeof(name), "/proc/%u/status", pid);
928 if ((fd = open(name,O_RDONLY)) >=0 )
930 char* tmp=nullptr;
931 char prstatusbuf[512];
932 memset(prstatusbuf,0,512);
933 bRet = safeRead(fd, prstatusbuf, 511);
935 close(fd);
937 if (!bRet)
938 return false;
940 tmp = strstr(prstatusbuf,"Uid:");
941 if(tmp)
943 sscanf(tmp,"Uid:\t%d\t%d\t%d\t%d",
944 &procstat->ruid, &procstat->euid, &procstat->suid, &procstat->fuid
948 tmp = strstr(prstatusbuf,"Gid:");
949 if(tmp)
951 sscanf(tmp,"Gid:\t%d\t%d\t%d\t%d",
952 &procstat->rgid, &procstat->egid, &procstat->sgid, &procstat->fgid
956 tmp = strstr(prstatusbuf,"VmSize:");
957 if(tmp)
959 sscanf(tmp,
960 "VmSize: %lu kB\n"
961 "VmLck: %lu kB\n"
962 "VmRSS: %lu kB\n"
963 "VmData: %lu kB\n"
964 "VmStk: %lu kB\n"
965 "VmExe: %lu kB\n"
966 "VmLib: %lu kB\n",
967 &procstat->vm_size, &procstat->vm_lock, &procstat->vm_rss, &procstat->vm_data,
968 &procstat->vm_stack, &procstat->vm_exe, &procstat->vm_lib
972 tmp = strstr(prstatusbuf,"SigPnd:");
973 if(tmp)
975 sscanf(tmp, "SigPnd: %23s SigBlk: %23s SigIgn: %23s %*s %23s",
976 procstat->signal, procstat->blocked, procstat->sigignore, procstat->sigcatch
980 return bRet;
983 #endif
985 oslProcessError SAL_CALL osl_getProcessInfo(oslProcess Process, oslProcessData Fields, oslProcessInfo* pInfo)
987 pid_t pid;
989 if (Process == nullptr)
990 pid = getpid();
991 else
992 pid = static_cast<oslProcessImpl*>(Process)->m_pid;
994 if (! pInfo || (pInfo->Size != sizeof(oslProcessInfo)))
995 return osl_Process_E_Unknown;
997 pInfo->Fields = 0;
999 if (Fields & osl_Process_IDENTIFIER)
1001 pInfo->Ident = pid;
1002 pInfo->Fields |= osl_Process_IDENTIFIER;
1005 if (Fields & osl_Process_EXITCODE)
1007 if ((Process != nullptr) &&
1008 osl_checkCondition(static_cast<oslProcessImpl*>(Process)->m_terminated))
1010 pInfo->Code = static_cast<oslProcessImpl*>(Process)->m_status;
1011 pInfo->Fields |= osl_Process_EXITCODE;
1015 if (Fields & (osl_Process_HEAPUSAGE | osl_Process_CPUTIMES))
1018 #if defined(__sun)
1020 int fd;
1021 char name[PATH_MAX + 1];
1023 snprintf(name, sizeof(name), "/proc/%ld", (long)pid);
1025 if ((fd = open(name, O_RDONLY)) >= 0)
1027 prstatus_t prstatus;
1029 if (ioctl(fd, PIOCSTATUS, &prstatus) >= 0)
1031 if (Fields & osl_Process_CPUTIMES)
1033 pInfo->UserTime.Seconds = prstatus.pr_utime.tv_sec;
1034 pInfo->UserTime.Nanosec = prstatus.pr_utime.tv_nsec;
1035 pInfo->SystemTime.Seconds = prstatus.pr_stime.tv_sec;
1036 pInfo->SystemTime.Nanosec = prstatus.pr_stime.tv_nsec;
1038 pInfo->Fields |= osl_Process_CPUTIMES;
1041 if (Fields & osl_Process_HEAPUSAGE)
1043 pInfo->HeapUsage = prstatus.pr_brksize;
1045 pInfo->Fields |= osl_Process_HEAPUSAGE;
1048 close(fd);
1050 return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
1052 else
1053 close(fd);
1056 #elif defined(LINUX)
1058 if ( (Fields & osl_Process_CPUTIMES) || (Fields & osl_Process_HEAPUSAGE) )
1060 struct osl_procStat procstat;
1061 memset(&procstat,0,sizeof(procstat));
1063 if ( (Fields & osl_Process_CPUTIMES) && osl_getProcStat(pid, &procstat) )
1066 * mfe:
1067 * We calculate only time of the process proper.
1068 * Threads are processes, we do not consider their time here!
1069 * (For this, cutime and cstime should be used, it seems not
1070 * to work in 2.0.36)
1073 long clktck;
1074 unsigned long hz;
1075 unsigned long userseconds;
1076 unsigned long systemseconds;
1078 clktck = sysconf(_SC_CLK_TCK);
1079 if (clktck <= 0) {
1080 return osl_Process_E_Unknown;
1082 hz = static_cast<unsigned long>(clktck);
1084 userseconds = procstat.utime/hz;
1085 systemseconds = procstat.stime/hz;
1087 pInfo->UserTime.Seconds = userseconds;
1088 pInfo->UserTime.Nanosec = procstat.utime - (userseconds * hz);
1089 pInfo->SystemTime.Seconds = systemseconds;
1090 pInfo->SystemTime.Nanosec = procstat.stime - (systemseconds * hz);
1092 pInfo->Fields |= osl_Process_CPUTIMES;
1095 if ( (Fields & osl_Process_HEAPUSAGE) && osl_getProcStatus(pid, &procstat) )
1098 * mfe:
1099 * vm_data (found in status) shows the size of the data segment
1100 * it a rough approximation of the core heap size
1102 pInfo->HeapUsage = procstat.vm_data*1024;
1104 pInfo->Fields |= osl_Process_HEAPUSAGE;
1108 return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
1109 #endif
1113 return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
1116 /** Helper function for osl_joinProcessWithTimeout
1119 static bool is_timeout(const struct timeval* tend)
1121 struct timeval tcurrent;
1122 gettimeofday(&tcurrent, nullptr);
1123 return (tcurrent.tv_sec >= tend->tv_sec);
1126 /* kill(pid, 0) is useful for checking if a
1127 process is still alive, but remember that
1128 kill even returns 0 if the process is already
1129 a zombie. */
1131 static bool is_process_dead(pid_t pid)
1133 return ((kill(pid, 0) == -1) && (ESRCH == errno));
1136 oslProcessError SAL_CALL osl_joinProcessWithTimeout(oslProcess Process, const TimeValue* pTimeout)
1138 oslProcessImpl* pChild = ChildList;
1139 oslProcessError osl_error = osl_Process_E_None;
1141 OSL_PRECOND(Process, "osl_joinProcess: Invalid parameter");
1142 OSL_ASSERT(ChildListMutex);
1144 if (Process == nullptr || ChildListMutex == nullptr)
1145 return osl_Process_E_Unknown;
1147 osl_acquireMutex(ChildListMutex);
1149 /* check if process is a child of ours */
1150 while (pChild != nullptr)
1152 if (pChild == static_cast<oslProcessImpl*>(Process))
1153 break;
1155 pChild = pChild->m_pnext;
1158 osl_releaseMutex(ChildListMutex);
1160 if (pChild != nullptr)
1162 oslConditionResult cond_res = osl_waitCondition(pChild->m_terminated, pTimeout);
1164 if (cond_res == osl_cond_result_timeout)
1165 osl_error = osl_Process_E_TimedOut;
1166 else if (cond_res != osl_cond_result_ok)
1167 osl_error = osl_Process_E_Unknown;
1169 else /* alien process; StatusThread will not be able
1170 to set the condition terminated */
1172 pid_t pid = static_cast<oslProcessImpl*>(Process)->m_pid;
1174 if (pTimeout)
1176 bool timeout = false;
1177 struct timeval tend;
1179 gettimeofday(&tend, nullptr);
1181 tend.tv_sec += pTimeout->Seconds;
1183 while (!is_process_dead(pid) && !(timeout = is_timeout(&tend)))
1184 sleep(1);
1186 if (timeout)
1187 osl_error = osl_Process_E_TimedOut;
1189 else /* infinite */
1191 while (!is_process_dead(pid))
1192 sleep(1);
1195 return osl_error;
1198 oslProcessError SAL_CALL osl_joinProcess(oslProcess Process)
1200 return osl_joinProcessWithTimeout(Process, nullptr);
1203 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */