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 | |
69 | namespace |
70 | { |
71 | |
72 | struct 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 | |
88 | static oslProcessImpl* ChildList; |
89 | static 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 | |
98 | static 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 | |
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 ); |
163 | |
164 | /****************************************************************************** |
165 | * |
166 | * New io resource transfer functions |
167 | * |
168 | *****************************************************************************/ |
169 | |
170 | |
171 | sal_Bool osl_sendResourcePipe(oslPipe /*pPipe*/, oslSocket /*pSocket*/) |
172 | { |
173 | return osl_Process_E_InvalidError; |
174 | } |
175 | |
176 | oslSocket 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 | |
190 | extern "C" { |
191 | |
192 | static 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 | |
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 |
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 | |
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 |
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 | |
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 |
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 | |
732 | oslProcessError 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 | |
755 | oslProcess 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 | |
805 | void 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) |
848 | struct 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 ; /* resident set size (in pages) */ |
875 | unsigned long ; /* 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 ; /* 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 | |
908 | sal_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 | |
956 | sal_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 | |
1022 | oslProcessError 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 | |
1158 | static 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 | |
1172 | static bool is_process_dead(pid_t pid) |
1173 | { |
1174 | return ((-1 == kill(pid, 0)) && (ESRCH == errno)); |
1175 | } |
1176 | |
1177 | /********************************************** |
1178 | osl_joinProcessWithTimeout |
1179 | *********************************************/ |
1180 | |
1181 | oslProcessError 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 | |
1243 | oslProcessError SAL_CALL osl_joinProcess(oslProcess Process) |
1244 | { |
1245 | return osl_joinProcessWithTimeout(Process, NULL); |
1246 | } |
1247 | |
1248 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |
1249 | |