- Timestamp:
- Oct 13, 2010 5:34:05 PM (14 years ago)
- Location:
- trunk/src/VBox/Main/xpcom
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/xpcom/server.cpp
r33044 r33112 28 28 #include <nsGenericFactory.h> 29 29 30 #include "prio.h" 31 #include "prproces.h" 32 30 33 #include "xpcom/server.h" 31 34 … … 43 46 #include <iprt/path.h> 44 47 #include <iprt/timer.h> 48 #include <iprt/env.h> 45 49 46 50 #include <signal.h> // for the signal handler … … 756 760 } 757 761 762 static nsresult vboxsvcSpawnDaemonByReExec(const char *pszPath) 763 { 764 PRFileDesc *readable = nsnull, *writable = nsnull; 765 PRProcessAttr *attr = nsnull; 766 nsresult rv = NS_ERROR_FAILURE; 767 PRFileDesc *devNull; 768 // The ugly casts are necessary because the PR_CreateProcessDetached has 769 // a const array of writable strings as a parameter. It won't write. */ 770 char * const args[] = { (char *)pszPath, (char *)"--auto-shutdown", 0 }; 771 772 // Use a pipe to determine when the daemon process is in the position 773 // to actually process requests. The daemon will write "READY" to the pipe. 774 if (PR_CreatePipe(&readable, &writable) != PR_SUCCESS) 775 goto end; 776 PR_SetFDInheritable(writable, PR_TRUE); 777 778 attr = PR_NewProcessAttr(); 779 if (!attr) 780 goto end; 781 782 if (PR_ProcessAttrSetInheritableFD(attr, writable, VBOXSVC_STARTUP_PIPE_NAME) != PR_SUCCESS) 783 goto end; 784 785 devNull = PR_Open("/dev/null", PR_RDWR, 0); 786 if (!devNull) 787 goto end; 788 789 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, devNull); 790 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, devNull); 791 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardError, devNull); 792 793 if (PR_CreateProcessDetached(pszPath, args, nsnull, attr) != PR_SUCCESS) 794 goto end; 795 796 // Close /dev/null 797 PR_Close(devNull); 798 // Close the child end of the pipe to make it the only owner of the 799 // file descriptor, so that unexpected closing can be detected. 800 PR_Close(writable); 801 writable = nsnull; 802 803 char msg[10]; 804 memset(msg, '\0', sizeof(msg)); 805 if ( PR_Read(readable, msg, sizeof(msg)-1) != 5 806 || strcmp(msg, "READY")) 807 goto end; 808 809 rv = NS_OK; 810 811 end: 812 if (readable) 813 PR_Close(readable); 814 if (writable) 815 PR_Close(writable); 816 if (attr) 817 PR_DestroyProcessAttr(attr); 818 return rv; 819 } 820 758 821 int main(int argc, char **argv) 759 822 { … … 770 833 { "--daemonize", 'd', RTGETOPT_REQ_NOTHING }, 771 834 { "--pidfile", 'p', RTGETOPT_REQ_STRING }, 772 { "--pipe", 'P', RTGETOPT_REQ_UINT32 },773 835 }; 774 836 775 837 bool fDaemonize = false; 776 int daemon_pipe_wr = -1;838 PRFileDesc *daemon_pipe_wr = nsnull; 777 839 778 840 RTGETOPTSTATE GetOptState; … … 814 876 } 815 877 816 /* This is just an internal hack for passing the pipe write fd817 along to the final child. Internal use only. */818 case 'P':819 {820 daemon_pipe_wr = ValueUnion.u32;821 break;822 }823 824 878 case 'h': 825 879 { … … 839 893 } 840 894 841 #ifdef RT_OS_OS2 /** @todo There is almost no need to make a special case of OS/2 here. Just the execv call needs to be told to create a background process... */842 843 /* nothing to do here, the process is supposed to be already844 * started daemonized when it is necessary */845 NOREF(fDaemonize);846 847 #else // !RT_OS_OS2848 849 895 if (fDaemonize) 850 896 { 851 /* create a pipe for communication between child and parent */ 852 int daemon_pipe_fds[2] = {-1, -1}; 853 if (pipe(daemon_pipe_fds) < 0) 854 { 855 RTMsgError("pipe() failed (errno = %d)", errno); 856 return 1; 857 } 858 daemon_pipe_wr = daemon_pipe_fds[1]; 859 int daemon_pipe_rd = daemon_pipe_fds[0]; 860 861 pid_t childpid = fork(); 862 if (childpid == -1) 863 { 864 RTMsgError("fork() failed (errno = %d)", errno); 865 return 1; 866 } 867 868 if (childpid != 0) 869 { 870 /* we're the parent process */ 871 bool fSuccess = false; 872 873 /* close the writing end of the pipe */ 874 close(daemon_pipe_wr); 875 876 /* try to read a message from the pipe */ 877 char msg[10 + 1]; 878 RT_ZERO(msg); /* initialize so it's NULL terminated */ 879 if (read(daemon_pipe_rd, msg, sizeof(msg) - 1) > 0) 880 { 881 if (strcmp(msg, "READY") == 0) 882 fSuccess = true; 883 else 884 RTMsgError("Unknown message from child process (%s)", msg); 885 } 886 else 887 RTMsgError("0 bytes read from child process"); 888 889 /* close the reading end of the pipe as well and exit */ 890 close(daemon_pipe_rd); 891 return fSuccess ? 0 : 1; 892 } 893 /* we're the child process */ 894 895 /* Create a new SID for the child process */ 896 pid_t sid = setsid(); 897 if (sid < 0) 898 { 899 RTMsgError("setsid() failed (errno = %d)", errno); 900 return 1; 901 } 902 903 /* Need to do another for to get rid of the session leader status. 904 * Otherwise any accidentally opened tty will automatically become a 905 * controlling tty for the daemon process. */ 906 childpid = fork(); 907 if (childpid == -1) 908 { 909 RTMsgError("second fork() failed (errno = %d)", errno); 910 return 1; 911 } 912 913 if (childpid != 0) 914 { 915 /* we're the parent process, just a dummy so terminate now */ 916 exit(0); 917 } 918 919 /* Close all file handles except for the write end of the pipe. */ 920 int fdMax; 921 struct rlimit lim; 922 if (getrlimit(RLIMIT_NOFILE, &lim) == 0) 923 fdMax = (int)RT_MIN(lim.rlim_cur, 65535); /* paranoia */ 924 else 925 fdMax = 1024; 926 for (int fd = 0; fd < fdMax; fd++) 927 if (fd != daemon_pipe_wr) 928 close(fd); 929 930 /* Make sure the pipe isn't any of the standard handles. */ 931 if (daemon_pipe_wr <= 2) 932 { 933 if (dup2(daemon_pipe_wr, 3) == 3) 934 { 935 close(daemon_pipe_wr); 936 daemon_pipe_wr = 3; 937 } 938 } 939 940 /* Redirect the standard handles to NULL by opening /dev/null three times. */ 941 open("/dev/null", O_RDWR, 0); 942 open("/dev/null", O_RDWR, 0); 943 open("/dev/null", O_RDWR, 0); 944 945 /* 946 * On leopard we're no longer allowed to use some of the core API's 947 * after forking - this will cause us to hit an int3. 948 * So, we'll have to execv VBoxSVC once again and hand it the pipe 949 * and all other relevant options. 950 * 951 * On FreeBSD the fork approach doesn't work. The child fails 952 * during initialization of XPCOM for some unknown reason and 953 * exits making it impossible to autostart VBoxSVC when starting 954 * a frontend (debugger and strace don't contain any useful info). 955 */ 956 const char *apszArgs[7 + 2]; 957 unsigned i = 0; 958 apszArgs[i++] = argv[0]; 959 apszArgs[i++] = "--pipe"; 960 char szPipeArg[32]; 961 RTStrPrintf(szPipeArg, sizeof(szPipeArg), "%d", daemon_pipe_wr); 962 apszArgs[i++] = szPipeArg; 963 if (g_pszPidFile) 964 { 965 apszArgs[i++] = "--pidfile"; 966 apszArgs[i++] = g_pszPidFile; 967 } 968 if (gAutoShutdown) 969 apszArgs[i++] = "--auto-shutdown"; 970 apszArgs[i++] = NULL; Assert(i <= RT_ELEMENTS(apszArgs)); 971 execv(apszArgs[0], (char * const *)apszArgs); 897 vboxsvcSpawnDaemonByReExec(argv[0]); 972 898 exit(126); 973 899 } 974 900 975 #endif // !RT_OS_OS2976 977 901 nsresult rc; 902 903 daemon_pipe_wr = PR_GetInheritedFD(VBOXSVC_STARTUP_PIPE_NAME); 904 RTEnvUnset("NSPR_INHERIT_FDS"); 978 905 979 906 do … … 1067 994 } 1068 995 1069 if (daemon_pipe_wr >= 0)996 if (daemon_pipe_wr != nsnull) 1070 997 { 1071 998 RTPrintf("\nStarting event loop....\n[send TERM signal to quit]\n"); 1072 999 /* now we're ready, signal the parent process */ 1073 write(daemon_pipe_wr, "READY", strlen("READY"));1000 PR_Write(daemon_pipe_wr, "READY", strlen("READY")); 1074 1001 /* close writing end of the pipe, its job is done */ 1075 close(daemon_pipe_wr);1002 PR_Close(daemon_pipe_wr); 1076 1003 } 1077 1004 else -
trunk/src/VBox/Main/xpcom/server.h
r28800 r33112 28 28 #define VBOXSVC_IPC_NAME "VBoxSVC-" VBOX_VERSION_STRING 29 29 30 31 /** 32 * Tag for the file descriptor passing for the daemonizing control. 33 */ 34 #define VBOXSVC_STARTUP_PIPE_NAME "vboxsvc:startup-pipe" 35 30 36 #endif /* ____H_LINUX_SERVER */ -
trunk/src/VBox/Main/xpcom/server_module.cpp
r30631 r33112 34 34 #include <ipcdclient.h> 35 35 36 #include "prio.h" 37 #include "prproces.h" 38 36 39 // official XPCOM headers don't define it yet 37 40 #define IPC_DCONNECTSERVICE_CONTRACTID \ … … 91 94 NS_DECL_CLASSINFO (VirtualBox) 92 95 NS_IMPL_CI_INTERFACE_GETTER1 (VirtualBox, IVirtualBox) 96 97 static nsresult vboxsvcSpawnDaemon(void) 98 { 99 PRFileDesc *readable = nsnull, *writable = nsnull; 100 PRProcessAttr *attr = nsnull; 101 nsresult rv = NS_ERROR_FAILURE; 102 PRFileDesc *devNull; 103 // The ugly casts are necessary because the PR_CreateProcessDetached has 104 // a const array of writable strings as a parameter. It won't write. */ 105 char * const args[] = { (char *)VBoxSVCPath, (char *)"--auto-shutdown", 0 }; 106 107 // Use a pipe to determine when the daemon process is in the position 108 // to actually process requests. The daemon will write "READY" to the pipe. 109 if (PR_CreatePipe(&readable, &writable) != PR_SUCCESS) 110 goto end; 111 PR_SetFDInheritable(writable, PR_TRUE); 112 113 attr = PR_NewProcessAttr(); 114 if (!attr) 115 goto end; 116 117 if (PR_ProcessAttrSetInheritableFD(attr, writable, VBOXSVC_STARTUP_PIPE_NAME) != PR_SUCCESS) 118 goto end; 119 120 devNull = PR_Open("/dev/null", PR_RDWR, 0); 121 if (!devNull) 122 goto end; 123 124 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, devNull); 125 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, devNull); 126 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardError, devNull); 127 128 if (PR_CreateProcessDetached(VBoxSVCPath, args, nsnull, attr) != PR_SUCCESS) 129 goto end; 130 131 // Close /dev/null 132 PR_Close(devNull); 133 // Close the child end of the pipe to make it the only owner of the 134 // file descriptor, so that unexpected closing can be detected. 135 PR_Close(writable); 136 writable = nsnull; 137 138 char msg[10]; 139 memset(msg, '\0', sizeof(msg)); 140 if ( PR_Read(readable, msg, sizeof(msg)-1) != 5 141 || strcmp(msg, "READY")) 142 goto end; 143 144 rv = NS_OK; 145 146 end: 147 if (readable) 148 PR_Close(readable); 149 if (writable) 150 PR_Close(writable); 151 if (attr) 152 PR_DestroyProcessAttr(attr); 153 return rv; 154 } 155 93 156 94 157 /** … … 181 244 startedOnce = true; 182 245 183 #ifdef RT_OS_OS2 184 char * const args[] = { VBoxSVCPath, "--automate", 0 }; 185 /* use NSPR because we want the process to be detached right 186 * at startup (it isn't possible to detach it later on), 187 * RTProcCreate() isn't yet capable of doing that. */ 188 PRStatus rv = PR_CreateProcessDetached (VBoxSVCPath, 189 args, NULL, NULL); 190 if (rv != PR_SUCCESS) 191 { 192 rc = NS_ERROR_FAILURE; 246 rc = vboxsvcSpawnDaemon(); 247 if (NS_FAILED(rc)) 193 248 break; 194 }195 #else196 const char *args[] = { VBoxSVCPath, "--automate", 0 };197 RTPROCESS pid = NIL_RTPROCESS;198 vrc = RTProcCreate (VBoxSVCPath, args, RTENV_DEFAULT, 0, &pid);199 if (RT_FAILURE(vrc))200 {201 rc = NS_ERROR_FAILURE;202 break;203 }204 205 /* need to wait for the pid to avoid zombie VBoxSVC.206 * ignore failure since it just means we'll have a zombie207 * VBoxSVC until we exit */208 int vrc2 = RTProcWait(pid, RTPROCWAIT_FLAGS_BLOCK, NULL);209 AssertRC(vrc2);210 #endif211 249 212 250 /* wait for the server process to establish a connection */
Note:
See TracChangeset
for help on using the changeset viewer.