VirtualBox

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

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

process-posix: use crypt_r() instead of crypt()

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 17.6 KB
Line 
1/* $Id: process-posix.cpp 29602 2010-05-18 09:00:39Z 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)
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#else
105 return VERR_PERMISSION_DENIED;
106#endif
107}
108
109
110RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
111{
112 return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
113 NULL, NULL, NULL, /* standard handles */
114 NULL /*pszAsUser*/, NULL /* pszPassword*/,
115 pProcess);
116}
117
118
119RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
120 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
121 const char *pszPassword, PRTPROCESS phProcess)
122{
123 int rc;
124
125 /*
126 * Input validation
127 */
128 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
129 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
130 AssertReturn(!(fFlags & ~(RTPROC_FLAGS_DAEMONIZE_DEPRECATED | RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_SERVICE)), VERR_INVALID_PARAMETER);
131 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
132 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
133 const char * const *papszEnv = RTEnvGetExecEnvP(hEnv);
134 AssertPtrReturn(papszEnv, VERR_INVALID_HANDLE);
135 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
136 /** @todo search the PATH (add flag for this). */
137 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
138 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
139 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
140 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
141
142 /*
143 * Get the file descriptors for the handles we've been passed.
144 */
145 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
146 int aStdFds[3] = { -1, -1, -1 };
147 for (int i = 0; i < 3; i++)
148 {
149 if (paHandles[i])
150 {
151 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
152 switch (paHandles[i]->enmType)
153 {
154 case RTHANDLETYPE_FILE:
155 aStdFds[i] = paHandles[i]->u.hFile != NIL_RTFILE
156 ? (int)RTFileToNative(paHandles[i]->u.hFile)
157 : -2 /* close it */;
158 break;
159
160 case RTHANDLETYPE_PIPE:
161 aStdFds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
162 ? (int)RTPipeToNative(paHandles[i]->u.hPipe)
163 : -2 /* close it */;
164 break;
165
166 case RTHANDLETYPE_SOCKET:
167 aStdFds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
168 ? (int)RTSocketToNative(paHandles[i]->u.hSocket)
169 : -2 /* close it */;
170 break;
171
172 default:
173 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
174 }
175 /** @todo check the close-on-execness of these handles? */
176 }
177 }
178
179 for (int i = 0; i < 3; i++)
180 if (aStdFds[i] == i)
181 aStdFds[i] = -1;
182
183 for (int i = 0; i < 3; i++)
184 AssertMsgReturn(aStdFds[i] < 0 || aStdFds[i] > i,
185 ("%i := %i not possible because we're lazy\n", i, aStdFds[i]),
186 VERR_NOT_SUPPORTED);
187
188 /*
189 * Resolve the user id if specified.
190 */
191 uid_t uid = ~(uid_t)0;
192 gid_t gid = ~(gid_t)0;
193 if (pszAsUser)
194 {
195 rc = rtCheckCredentials(pszAsUser, pszPassword, &gid, &uid);
196 if (RT_FAILURE(rc))
197 return rc;
198 }
199
200 /*
201 * Check for execute access to the file.
202 */
203 if (access(pszExec, X_OK))
204 {
205 rc = RTErrConvertFromErrno(errno);
206 AssertMsgFailed(("'%s' %Rrc!\n", pszExec, rc));
207 return rc;
208 }
209
210 /*
211 * Spawn the child.
212 *
213 * HACK ALERT! Put the process into a new process group with pgid = pid
214 * to make sure it differs from that of the parent process to ensure that
215 * the IPRT waipit call doesn't race anyone (read XPCOM) doing group wide
216 * waits.
217 */
218 pid_t pid = -1;
219#ifdef HAVE_POSIX_SPAWN
220 /** @todo OS/2: implement DETACHED (BACKGROUND stuff), see VbglR3Daemonize. */
221 /** @todo Try do the detach thing with posix spawn. */
222 if ( !(fFlags & (RTPROC_FLAGS_DAEMONIZE_DEPRECATED | RTPROC_FLAGS_DETACHED))
223 && uid == ~(uid_t)0
224 && gid == ~(gid_t)0
225 )
226 {
227 /* Spawn attributes. */
228 posix_spawnattr_t Attr;
229 rc = posix_spawnattr_init(&Attr);
230 if (!rc)
231 {
232# ifndef RT_OS_OS2 /* We don't need this on OS/2 and I don't recall if it's actually implemented. */
233 rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP);
234 Assert(rc == 0);
235 if (!rc)
236 {
237 rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */);
238 Assert(rc == 0);
239 }
240# endif
241
242 /* File changes. */
243 posix_spawn_file_actions_t FileActions;
244 posix_spawn_file_actions_t *pFileActions = NULL;
245 if (aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1)
246 {
247 rc = posix_spawn_file_actions_init(&FileActions);
248 if (!rc)
249 {
250 pFileActions = &FileActions;
251 for (int i = 0; i < 3; i++)
252 {
253 int fd = aStdFds[i];
254 if (fd == -2)
255 rc = posix_spawn_file_actions_addclose(&FileActions, i);
256 else if (fd >= 0 && fd != i)
257 {
258 rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i);
259 if (!rc)
260 {
261 for (int j = i + 1; j < 3; j++)
262 if (aStdFds[j] == fd)
263 {
264 fd = -1;
265 break;
266 }
267 if (fd >= 0)
268 rc = posix_spawn_file_actions_addclose(&FileActions, fd);
269 }
270 }
271 if (rc)
272 break;
273 }
274 }
275 }
276
277 if (!rc)
278 rc = posix_spawn(&pid, pszExec, pFileActions, &Attr, (char * const *)papszArgs,
279 (char * const *)papszEnv);
280
281 /* cleanup */
282 int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2);
283 if (pFileActions)
284 {
285 rc2 = posix_spawn_file_actions_destroy(pFileActions);
286 Assert(rc2 == 0);
287 }
288
289 /* return on success.*/
290 if (!rc)
291 {
292 if (phProcess)
293 *phProcess = pid;
294 return VINF_SUCCESS;
295 }
296 }
297 }
298 else
299#endif
300 {
301 pid = fork();
302 if (!pid)
303 {
304 setpgid(0, 0); /* see comment above */
305
306 /*
307 * Change group and user if requested.
308 */
309#if 1 /** @todo This needs more work, see suplib/hardening. */
310 if (gid != ~(gid_t)0)
311 {
312 if (setgid(gid))
313 exit(126);
314 }
315
316 if (uid != ~(uid_t)0)
317 {
318 if (setuid(uid))
319 exit(126);
320 }
321#endif
322
323 /*
324 * Apply changes to the standard file descriptor and stuff.
325 */
326 for (int i = 0; i < 3; i++)
327 {
328 int fd = aStdFds[i];
329 if (fd == -2)
330 close(i);
331 else if (fd >= 0)
332 {
333 int rc2 = dup2(fd, i);
334 if (rc2 != i)
335 exit(125);
336 for (int j = i + 1; j < 3; j++)
337 if (aStdFds[j] == fd)
338 {
339 fd = -1;
340 break;
341 }
342 if (fd >= 0)
343 close(fd);
344 }
345 }
346
347 /*
348 * Daemonize the process if requested.
349 */
350 if (fFlags & (RTPROC_FLAGS_DAEMONIZE_DEPRECATED | RTPROC_FLAGS_DETACHED))
351 {
352 rc = RTProcDaemonizeUsingFork(true /*fNoChDir*/,
353 !(fFlags & RTPROC_FLAGS_DAEMONIZE_DEPRECATED) /*fNoClose*/,
354 NULL /* pszPidFile */);
355 if (RT_FAILURE(rc))
356 {
357 /* parent */
358 AssertReleaseMsgFailed(("RTProcDaemonize returns %Rrc errno=%d\n", rc, errno));
359 exit(127);
360 }
361 /* daemonized child */
362 }
363
364 /*
365 * Finally, execute the requested program.
366 */
367 rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv);
368 AssertReleaseMsgFailed(("execve returns %d errno=%d\n", rc, errno));
369 exit(127);
370 }
371 if (pid > 0)
372 {
373 if (phProcess)
374 *phProcess = pid;
375 return VINF_SUCCESS;
376 }
377 rc = errno;
378 }
379
380
381 return VERR_NOT_IMPLEMENTED;
382}
383
384
385RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
386{
387 int rc;
388 do rc = RTProcWaitNoResume(Process, fFlags, pProcStatus);
389 while (rc == VERR_INTERRUPTED);
390 return rc;
391}
392
393RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
394{
395 /*
396 * Validate input.
397 */
398 if (Process <= 0)
399 {
400 AssertMsgFailed(("Invalid Process=%d\n", Process));
401 return VERR_INVALID_PARAMETER;
402 }
403 if (fFlags & ~(RTPROCWAIT_FLAGS_NOBLOCK | RTPROCWAIT_FLAGS_BLOCK))
404 {
405 AssertMsgFailed(("Invalid flags %#x\n", fFlags));
406 return VERR_INVALID_PARAMETER;
407 }
408
409 /*
410 * Performe the wait.
411 */
412 int iStatus = 0;
413 int rc = waitpid(Process, &iStatus, fFlags & RTPROCWAIT_FLAGS_NOBLOCK ? WNOHANG : 0);
414 if (rc > 0)
415 {
416 /*
417 * Fill in the status structure.
418 */
419 if (pProcStatus)
420 {
421 if (WIFEXITED(iStatus))
422 {
423 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
424 pProcStatus->iStatus = WEXITSTATUS(iStatus);
425 }
426 else if (WIFSIGNALED(iStatus))
427 {
428 pProcStatus->enmReason = RTPROCEXITREASON_SIGNAL;
429 pProcStatus->iStatus = WTERMSIG(iStatus);
430 }
431 else
432 {
433 Assert(!WIFSTOPPED(iStatus));
434 pProcStatus->enmReason = RTPROCEXITREASON_ABEND;
435 pProcStatus->iStatus = iStatus;
436 }
437 }
438 return VINF_SUCCESS;
439 }
440
441 /*
442 * Child running?
443 */
444 if (!rc)
445 {
446 Assert(fFlags & RTPROCWAIT_FLAGS_NOBLOCK);
447 return VERR_PROCESS_RUNNING;
448 }
449
450 /*
451 * Figure out which error to return.
452 */
453 int iErr = errno;
454 if (iErr == ECHILD)
455 return VERR_PROCESS_NOT_FOUND;
456 return RTErrConvertFromErrno(iErr);
457}
458
459
460RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
461{
462 if (!kill(Process, SIGKILL))
463 return VINF_SUCCESS;
464 return RTErrConvertFromErrno(errno);
465}
466
467
468RTR3DECL(uint64_t) RTProcGetAffinityMask()
469{
470 // @todo
471 return 1;
472}
473
474
475RTR3DECL(int) RTProcDaemonizeUsingFork(bool fNoChDir, bool fNoClose, const char *pszPidfile)
476{
477 /*
478 * Fork the child process in a new session and quit the parent.
479 *
480 * - fork once and create a new session (setsid). This will detach us
481 * from the controlling tty meaning that we won't receive the SIGHUP
482 * (or any other signal) sent to that session.
483 * - The SIGHUP signal is ignored because the session/parent may throw
484 * us one before we get to the setsid.
485 * - When the parent exit(0) we will become an orphan and re-parented to
486 * the init process.
487 * - Because of the sometimes unexpected semantics of assigning the
488 * controlling tty automagically when a session leader first opens a tty,
489 * we will fork() once more to get rid of the session leadership role.
490 */
491
492 /* We start off by opening the pidfile, so that we can fail straight away
493 * if it already exists. */
494 int fdPidfile = -1;
495 if (pszPidfile != NULL)
496 {
497 /* @note the exclusive create is not guaranteed on all file
498 * systems (e.g. NFSv2) */
499 if ((fdPidfile = open(pszPidfile, O_RDWR | O_CREAT | O_EXCL, 0644)) == -1)
500 return RTErrConvertFromErrno(errno);
501 }
502
503 /* Ignore SIGHUP straight away. */
504 struct sigaction OldSigAct;
505 struct sigaction SigAct;
506 memset(&SigAct, 0, sizeof(SigAct));
507 SigAct.sa_handler = SIG_IGN;
508 int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct);
509
510 /* First fork, to become independent process. */
511 pid_t pid = fork();
512 if (pid == -1)
513 return RTErrConvertFromErrno(errno);
514 if (pid != 0)
515 {
516 /* Parent exits, no longer necessary. The child gets reparented
517 * to the init process. */
518 exit(0);
519 }
520
521 /* Create new session, fix up the standard file descriptors and the
522 * current working directory. */
523 pid_t newpgid = setsid();
524 int SavedErrno = errno;
525 if (rcSigAct != -1)
526 sigaction(SIGHUP, &OldSigAct, NULL);
527 if (newpgid == -1)
528 return RTErrConvertFromErrno(SavedErrno);
529
530 if (!fNoClose)
531 {
532 /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */
533 int fd = open("/dev/null", O_RDWR);
534 if (fd == -1) /* paranoia */
535 {
536 close(STDIN_FILENO);
537 close(STDOUT_FILENO);
538 close(STDERR_FILENO);
539 fd = open("/dev/null", O_RDWR);
540 }
541 if (fd != -1)
542 {
543 dup2(fd, STDIN_FILENO);
544 dup2(fd, STDOUT_FILENO);
545 dup2(fd, STDERR_FILENO);
546 if (fd > 2)
547 close(fd);
548 }
549 }
550
551 if (!fNoChDir)
552 {
553 int rcChdir = chdir("/");
554 }
555
556 /* Second fork to lose session leader status. */
557 pid = fork();
558 if (pid == -1)
559 return RTErrConvertFromErrno(errno);
560
561 if (pid != 0)
562 {
563 /* Write the pid file, this is done in the parent, before exiting. */
564 if (fdPidfile != -1)
565 {
566 char szBuf[256];
567 size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n", pid);
568 int rcWrite = write(fdPidfile, szBuf, cbPid);
569 close(fdPidfile);
570 }
571 exit(0);
572 }
573
574 return VINF_SUCCESS;
575}
576
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