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