VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/posix/process-posix.cpp@ 29624

Last change on this file since 29624 was 29624, checked in by vboxsync, 15 years ago

posix/process-posix: rtCheckCredentials for Solaris.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 18.3 KB
Line 
1/* $Id: process-posix.cpp 29624 2010-05-18 12:36:13Z vboxsync $ */
2/** @file
3 * IPRT - Process, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28
29/*******************************************************************************
30* Header Files *
31*******************************************************************************/
32#define LOG_GROUP RTLOGGROUP_PROCESS
33#include <unistd.h>
34#include <stdlib.h>
35#include <errno.h>
36#include <sys/types.h>
37#include <sys/stat.h>
38#include <sys/wait.h>
39#include <fcntl.h>
40#include <signal.h>
41#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
42# include <crypt.h>
43# include <pwd.h>
44# include <shadow.h>
45#endif
46#if defined(RT_OS_LINUX) || defined(RT_OS_OS2)
47# define HAVE_POSIX_SPAWN 1
48#endif
49#ifdef HAVE_POSIX_SPAWN
50# include <spawn.h>
51#endif
52#ifdef RT_OS_DARWIN
53# include <mach-o/dyld.h>
54#endif
55
56#include <iprt/process.h>
57#include "internal/iprt.h"
58
59#include <iprt/assert.h>
60#include <iprt/env.h>
61#include <iprt/err.h>
62#include <iprt/file.h>
63#include <iprt/pipe.h>
64#include <iprt/socket.h>
65#include <iprt/string.h>
66#include "internal/process.h"
67
68
69/**
70 * Check the credentials and return the gid/uid of user.
71 *
72 * @param pszUser username
73 * @param pszPasswd password
74 * @param gid where to store the GID of the user
75 * @param uid where to store the UID of the user
76 * @returns IPRT status code
77 */
78static int rtCheckCredentials(const char *pszUser, const char *pszPasswd, gid_t *gid, uid_t *uid)
79{
80#if defined(RT_OS_LINUX)
81 struct passwd *pw;
82
83 pw = getpwnam(pszUser);
84 if (!pw)
85 return VERR_PERMISSION_DENIED;
86
87 if (!pszPasswd)
88 pszPasswd = "";
89
90 struct spwd *spwd;
91 /* works only if /etc/shadow is accessible */
92 spwd = getspnam(pszUser);
93 if (spwd)
94 pw->pw_passwd = spwd->sp_pwdp;
95
96 struct crypt_data data;
97 char *pszEncPasswd = crypt_r(pszPasswd, pw->pw_passwd, &data);
98 if (strcmp(pszEncPasswd, pw->pw_passwd))
99 return VERR_PERMISSION_DENIED;
100
101 *gid = pw->pw_gid;
102 *uid = pw->pw_uid;
103 return VINF_SUCCESS;
104#elif defined(RT_OS_SOLARIS)
105 struct passwd *ppw, pw;
106 char szBuf[1024];
107
108 if (getpwnam_r(pszUser, &pw, szBuf, sizeof(szBuf), &ppw) != 0 || ppw == NULL)
109 return VERR_PERMISSION_DENIED;
110
111 if (!pszPasswd)
112 pszPasswd = "";
113
114 struct spwd spwd;
115 char szPwdBuf[1024];
116 /* works only if /etc/shadow is accessible */
117 if (getspnam_r(pszUser, &spwd, szPwdBuf, sizeof(szPwdBuf)) != NULL)
118 ppw->pw_passwd = spwd.sp_pwdp;
119
120 char *pszEncPasswd = crypt(pszPasswd, ppw->pw_passwd);
121 if (strcmp(pszEncPasswd, ppw->pw_passwd))
122 return VERR_PERMISSION_DENIED;
123
124 *gid = ppw->pw_gid;
125 *uid = ppw->pw_uid;
126 return VINF_SUCCESS;
127#else
128 return VERR_PERMISSION_DENIED;
129#endif
130}
131
132
133RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
134{
135 return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
136 NULL, NULL, NULL, /* standard handles */
137 NULL /*pszAsUser*/, NULL /* pszPassword*/,
138 pProcess);
139}
140
141
142RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
143 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
144 const char *pszPassword, PRTPROCESS phProcess)
145{
146 int rc;
147
148 /*
149 * Input validation
150 */
151 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
152 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
153 AssertReturn(!(fFlags & ~(RTPROC_FLAGS_DAEMONIZE_DEPRECATED | RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_SERVICE)), VERR_INVALID_PARAMETER);
154 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
155 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
156 const char * const *papszEnv = RTEnvGetExecEnvP(hEnv);
157 AssertPtrReturn(papszEnv, VERR_INVALID_HANDLE);
158 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
159 /** @todo search the PATH (add flag for this). */
160 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
161 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
162 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
163 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
164
165 /*
166 * Get the file descriptors for the handles we've been passed.
167 */
168 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
169 int aStdFds[3] = { -1, -1, -1 };
170 for (int i = 0; i < 3; i++)
171 {
172 if (paHandles[i])
173 {
174 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
175 switch (paHandles[i]->enmType)
176 {
177 case RTHANDLETYPE_FILE:
178 aStdFds[i] = paHandles[i]->u.hFile != NIL_RTFILE
179 ? (int)RTFileToNative(paHandles[i]->u.hFile)
180 : -2 /* close it */;
181 break;
182
183 case RTHANDLETYPE_PIPE:
184 aStdFds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
185 ? (int)RTPipeToNative(paHandles[i]->u.hPipe)
186 : -2 /* close it */;
187 break;
188
189 case RTHANDLETYPE_SOCKET:
190 aStdFds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
191 ? (int)RTSocketToNative(paHandles[i]->u.hSocket)
192 : -2 /* close it */;
193 break;
194
195 default:
196 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
197 }
198 /** @todo check the close-on-execness of these handles? */
199 }
200 }
201
202 for (int i = 0; i < 3; i++)
203 if (aStdFds[i] == i)
204 aStdFds[i] = -1;
205
206 for (int i = 0; i < 3; i++)
207 AssertMsgReturn(aStdFds[i] < 0 || aStdFds[i] > i,
208 ("%i := %i not possible because we're lazy\n", i, aStdFds[i]),
209 VERR_NOT_SUPPORTED);
210
211 /*
212 * Resolve the user id if specified.
213 */
214 uid_t uid = ~(uid_t)0;
215 gid_t gid = ~(gid_t)0;
216 if (pszAsUser)
217 {
218 rc = rtCheckCredentials(pszAsUser, pszPassword, &gid, &uid);
219 if (RT_FAILURE(rc))
220 return rc;
221 }
222
223 /*
224 * Check for execute access to the file.
225 */
226 if (access(pszExec, X_OK))
227 {
228 rc = RTErrConvertFromErrno(errno);
229 AssertMsgFailed(("'%s' %Rrc!\n", pszExec, rc));
230 return rc;
231 }
232
233 /*
234 * Spawn the child.
235 *
236 * HACK ALERT! Put the process into a new process group with pgid = pid
237 * to make sure it differs from that of the parent process to ensure that
238 * the IPRT waipit call doesn't race anyone (read XPCOM) doing group wide
239 * waits.
240 */
241 pid_t pid = -1;
242#ifdef HAVE_POSIX_SPAWN
243 /** @todo OS/2: implement DETACHED (BACKGROUND stuff), see VbglR3Daemonize. */
244 /** @todo Try do the detach thing with posix spawn. */
245 if ( !(fFlags & (RTPROC_FLAGS_DAEMONIZE_DEPRECATED | RTPROC_FLAGS_DETACHED))
246 && uid == ~(uid_t)0
247 && gid == ~(gid_t)0
248 )
249 {
250 /* Spawn attributes. */
251 posix_spawnattr_t Attr;
252 rc = posix_spawnattr_init(&Attr);
253 if (!rc)
254 {
255# ifndef RT_OS_OS2 /* We don't need this on OS/2 and I don't recall if it's actually implemented. */
256 rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP);
257 Assert(rc == 0);
258 if (!rc)
259 {
260 rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */);
261 Assert(rc == 0);
262 }
263# endif
264
265 /* File changes. */
266 posix_spawn_file_actions_t FileActions;
267 posix_spawn_file_actions_t *pFileActions = NULL;
268 if (aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1)
269 {
270 rc = posix_spawn_file_actions_init(&FileActions);
271 if (!rc)
272 {
273 pFileActions = &FileActions;
274 for (int i = 0; i < 3; i++)
275 {
276 int fd = aStdFds[i];
277 if (fd == -2)
278 rc = posix_spawn_file_actions_addclose(&FileActions, i);
279 else if (fd >= 0 && fd != i)
280 {
281 rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i);
282 if (!rc)
283 {
284 for (int j = i + 1; j < 3; j++)
285 if (aStdFds[j] == fd)
286 {
287 fd = -1;
288 break;
289 }
290 if (fd >= 0)
291 rc = posix_spawn_file_actions_addclose(&FileActions, fd);
292 }
293 }
294 if (rc)
295 break;
296 }
297 }
298 }
299
300 if (!rc)
301 rc = posix_spawn(&pid, pszExec, pFileActions, &Attr, (char * const *)papszArgs,
302 (char * const *)papszEnv);
303
304 /* cleanup */
305 int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2);
306 if (pFileActions)
307 {
308 rc2 = posix_spawn_file_actions_destroy(pFileActions);
309 Assert(rc2 == 0);
310 }
311
312 /* return on success.*/
313 if (!rc)
314 {
315 if (phProcess)
316 *phProcess = pid;
317 return VINF_SUCCESS;
318 }
319 }
320 }
321 else
322#endif
323 {
324 pid = fork();
325 if (!pid)
326 {
327 setpgid(0, 0); /* see comment above */
328
329 /*
330 * Change group and user if requested.
331 */
332#if 1 /** @todo This needs more work, see suplib/hardening. */
333 if (gid != ~(gid_t)0)
334 {
335 if (setgid(gid))
336 exit(126);
337 }
338
339 if (uid != ~(uid_t)0)
340 {
341 if (setuid(uid))
342 exit(126);
343 }
344#endif
345
346 /*
347 * Apply changes to the standard file descriptor and stuff.
348 */
349 for (int i = 0; i < 3; i++)
350 {
351 int fd = aStdFds[i];
352 if (fd == -2)
353 close(i);
354 else if (fd >= 0)
355 {
356 int rc2 = dup2(fd, i);
357 if (rc2 != i)
358 exit(125);
359 for (int j = i + 1; j < 3; j++)
360 if (aStdFds[j] == fd)
361 {
362 fd = -1;
363 break;
364 }
365 if (fd >= 0)
366 close(fd);
367 }
368 }
369
370 /*
371 * Daemonize the process if requested.
372 */
373 if (fFlags & (RTPROC_FLAGS_DAEMONIZE_DEPRECATED | RTPROC_FLAGS_DETACHED))
374 {
375 rc = RTProcDaemonizeUsingFork(true /*fNoChDir*/,
376 !(fFlags & RTPROC_FLAGS_DAEMONIZE_DEPRECATED) /*fNoClose*/,
377 NULL /* pszPidFile */);
378 if (RT_FAILURE(rc))
379 {
380 /* parent */
381 AssertReleaseMsgFailed(("RTProcDaemonize returns %Rrc errno=%d\n", rc, errno));
382 exit(127);
383 }
384 /* daemonized child */
385 }
386
387 /*
388 * Finally, execute the requested program.
389 */
390 rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv);
391 AssertReleaseMsgFailed(("execve returns %d errno=%d\n", rc, errno));
392 exit(127);
393 }
394 if (pid > 0)
395 {
396 if (phProcess)
397 *phProcess = pid;
398 return VINF_SUCCESS;
399 }
400 rc = errno;
401 }
402
403
404 return VERR_NOT_IMPLEMENTED;
405}
406
407
408RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
409{
410 int rc;
411 do rc = RTProcWaitNoResume(Process, fFlags, pProcStatus);
412 while (rc == VERR_INTERRUPTED);
413 return rc;
414}
415
416RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
417{
418 /*
419 * Validate input.
420 */
421 if (Process <= 0)
422 {
423 AssertMsgFailed(("Invalid Process=%d\n", Process));
424 return VERR_INVALID_PARAMETER;
425 }
426 if (fFlags & ~(RTPROCWAIT_FLAGS_NOBLOCK | RTPROCWAIT_FLAGS_BLOCK))
427 {
428 AssertMsgFailed(("Invalid flags %#x\n", fFlags));
429 return VERR_INVALID_PARAMETER;
430 }
431
432 /*
433 * Performe the wait.
434 */
435 int iStatus = 0;
436 int rc = waitpid(Process, &iStatus, fFlags & RTPROCWAIT_FLAGS_NOBLOCK ? WNOHANG : 0);
437 if (rc > 0)
438 {
439 /*
440 * Fill in the status structure.
441 */
442 if (pProcStatus)
443 {
444 if (WIFEXITED(iStatus))
445 {
446 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
447 pProcStatus->iStatus = WEXITSTATUS(iStatus);
448 }
449 else if (WIFSIGNALED(iStatus))
450 {
451 pProcStatus->enmReason = RTPROCEXITREASON_SIGNAL;
452 pProcStatus->iStatus = WTERMSIG(iStatus);
453 }
454 else
455 {
456 Assert(!WIFSTOPPED(iStatus));
457 pProcStatus->enmReason = RTPROCEXITREASON_ABEND;
458 pProcStatus->iStatus = iStatus;
459 }
460 }
461 return VINF_SUCCESS;
462 }
463
464 /*
465 * Child running?
466 */
467 if (!rc)
468 {
469 Assert(fFlags & RTPROCWAIT_FLAGS_NOBLOCK);
470 return VERR_PROCESS_RUNNING;
471 }
472
473 /*
474 * Figure out which error to return.
475 */
476 int iErr = errno;
477 if (iErr == ECHILD)
478 return VERR_PROCESS_NOT_FOUND;
479 return RTErrConvertFromErrno(iErr);
480}
481
482
483RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
484{
485 if (!kill(Process, SIGKILL))
486 return VINF_SUCCESS;
487 return RTErrConvertFromErrno(errno);
488}
489
490
491RTR3DECL(uint64_t) RTProcGetAffinityMask()
492{
493 // @todo
494 return 1;
495}
496
497
498RTR3DECL(int) RTProcDaemonizeUsingFork(bool fNoChDir, bool fNoClose, const char *pszPidfile)
499{
500 /*
501 * Fork the child process in a new session and quit the parent.
502 *
503 * - fork once and create a new session (setsid). This will detach us
504 * from the controlling tty meaning that we won't receive the SIGHUP
505 * (or any other signal) sent to that session.
506 * - The SIGHUP signal is ignored because the session/parent may throw
507 * us one before we get to the setsid.
508 * - When the parent exit(0) we will become an orphan and re-parented to
509 * the init process.
510 * - Because of the sometimes unexpected semantics of assigning the
511 * controlling tty automagically when a session leader first opens a tty,
512 * we will fork() once more to get rid of the session leadership role.
513 */
514
515 /* We start off by opening the pidfile, so that we can fail straight away
516 * if it already exists. */
517 int fdPidfile = -1;
518 if (pszPidfile != NULL)
519 {
520 /* @note the exclusive create is not guaranteed on all file
521 * systems (e.g. NFSv2) */
522 if ((fdPidfile = open(pszPidfile, O_RDWR | O_CREAT | O_EXCL, 0644)) == -1)
523 return RTErrConvertFromErrno(errno);
524 }
525
526 /* Ignore SIGHUP straight away. */
527 struct sigaction OldSigAct;
528 struct sigaction SigAct;
529 memset(&SigAct, 0, sizeof(SigAct));
530 SigAct.sa_handler = SIG_IGN;
531 int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct);
532
533 /* First fork, to become independent process. */
534 pid_t pid = fork();
535 if (pid == -1)
536 return RTErrConvertFromErrno(errno);
537 if (pid != 0)
538 {
539 /* Parent exits, no longer necessary. The child gets reparented
540 * to the init process. */
541 exit(0);
542 }
543
544 /* Create new session, fix up the standard file descriptors and the
545 * current working directory. */
546 pid_t newpgid = setsid();
547 int SavedErrno = errno;
548 if (rcSigAct != -1)
549 sigaction(SIGHUP, &OldSigAct, NULL);
550 if (newpgid == -1)
551 return RTErrConvertFromErrno(SavedErrno);
552
553 if (!fNoClose)
554 {
555 /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */
556 int fd = open("/dev/null", O_RDWR);
557 if (fd == -1) /* paranoia */
558 {
559 close(STDIN_FILENO);
560 close(STDOUT_FILENO);
561 close(STDERR_FILENO);
562 fd = open("/dev/null", O_RDWR);
563 }
564 if (fd != -1)
565 {
566 dup2(fd, STDIN_FILENO);
567 dup2(fd, STDOUT_FILENO);
568 dup2(fd, STDERR_FILENO);
569 if (fd > 2)
570 close(fd);
571 }
572 }
573
574 if (!fNoChDir)
575 {
576 int rcChdir = chdir("/");
577 }
578
579 /* Second fork to lose session leader status. */
580 pid = fork();
581 if (pid == -1)
582 return RTErrConvertFromErrno(errno);
583
584 if (pid != 0)
585 {
586 /* Write the pid file, this is done in the parent, before exiting. */
587 if (fdPidfile != -1)
588 {
589 char szBuf[256];
590 size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n", pid);
591 int rcWrite = write(fdPidfile, szBuf, cbPid);
592 close(fdPidfile);
593 }
594 exit(0);
595 }
596
597 return VINF_SUCCESS;
598}
599
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette