1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Communicator client code, released
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998-1999
21 * the Initial Developer. All Rights Reserved.
24 * Don Bragg <dbragg@netscape.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 /*****************************************************************************
42 * nsProcess is used to execute new processes and specify if you want to
43 * wait (blocking) or continue (non-blocking).
45 *****************************************************************************
50 #include "nsProcess.h"
61 #include "nsLiteralString.h"
62 #include "nsReadableUtils.h"
66 #if defined(XP_MACOSX)
67 #include <Processes.h>
68 #include "nsILocalFileMac.h"
71 //-------------------------------------------------------------------//
72 // nsIProcess implementation
73 //-------------------------------------------------------------------//
74 NS_IMPL_ISUPPORTS1(nsProcess
, nsIProcess
)
77 nsProcess::nsProcess()
84 nsProcess::Init(nsIFile
* executable
)
86 NS_ENSURE_ARG_POINTER(executable
);
89 //First make sure the file exists
90 nsresult rv
= executable
->IsFile(&isFile
);
91 if (NS_FAILED(rv
)) return rv
;
93 return NS_ERROR_FAILURE
;
95 //Store the nsIFile in mExecutable
96 mExecutable
= executable
;
97 //Get the path because it is needed by the NSPR process creation
99 rv
= mExecutable
->GetNativeTarget(mTargetPath
);
100 if (NS_FAILED(rv
) || mTargetPath
.IsEmpty() )
102 rv
= mExecutable
->GetNativePath(mTargetPath
);
109 static int assembleCmdLine(char *const *argv
, char **cmdLine
)
119 * Find out how large the command line buffer should be.
122 for (arg
= argv
; *arg
; arg
++) {
124 * \ and " need to be escaped by a \. In the worst case,
125 * every character is a \ or ", so the string of length
126 * may double. If we quote an argument, that needs two ".
127 * Finally, we need a space between arguments, and
128 * a null byte at the end of command line.
130 cmdLineSize
+= 2 * strlen(*arg
) /* \ and " need to be escaped */
131 + 2 /* we quote every argument */
132 + 1; /* space in between, or final null */
134 p
= *cmdLine
= (char *) PR_MALLOC(cmdLineSize
);
139 for (arg
= argv
; *arg
; arg
++) {
140 /* Add a space to separates the arguments */
148 /* If the argument contains white space, it needs to be quoted. */
149 if (strpbrk(*arg
, " \f\n\r\t\v")) {
160 } else if (*q
== '"') {
161 if (numBackslashes
) {
163 * Double the backslashes since they are followed
166 for (i
= 0; i
< 2 * numBackslashes
; i
++) {
171 /* To escape the quote */
175 if (numBackslashes
) {
177 * Backslashes are not followed by a quote, so
178 * don't need to double the backslashes.
180 for (i
= 0; i
< numBackslashes
; i
++) {
189 /* Now we are at the end of this argument */
190 if (numBackslashes
) {
192 * Double the backslashes if we have a quote string
193 * delimiter at the end.
198 for (i
= 0; i
< numBackslashes
; i
++) {
212 // XXXldb |args| has the wrong const-ness
214 nsProcess::Run(PRBool blocking
, const char **args
, PRUint32 count
,
217 NS_ENSURE_TRUE(mExecutable
, NS_ERROR_NOT_INITIALIZED
);
218 PRStatus status
= PR_SUCCESS
;
220 // make sure that when we allocate we have 1 greater than the
221 // count since we need to null terminate the list for the argv to
222 // pass into PR_CreateProcess
223 char **my_argv
= NULL
;
224 my_argv
= (char **)nsMemory::Alloc(sizeof(char *) * (count
+ 2) );
226 return NS_ERROR_OUT_OF_MEMORY
;
231 for (i
=0; i
< count
; i
++) {
232 my_argv
[i
+1] = const_cast<char*>(args
[i
]);
234 // we need to set argv[0] to the program name.
235 my_argv
[0] = mTargetPath
.BeginWriting();
236 // null terminate the array
237 my_argv
[count
+1] = NULL
;
239 #if defined(XP_WIN) && !defined (WINCE) /* wince uses nspr */
240 STARTUPINFO startupInfo
;
241 PROCESS_INFORMATION procInfo
;
245 if (assembleCmdLine(my_argv
, &cmdLine
) == -1) {
246 nsMemory::Free(my_argv
);
247 return NS_ERROR_FILE_EXECUTION_FAILED
;
250 ZeroMemory(&startupInfo
, sizeof(startupInfo
));
251 startupInfo
.cb
= sizeof(startupInfo
);
253 /* The CREATE_NO_WINDOW flag is important to prevent console
254 * windows from appearing. This makes behavior the same on all
255 * platforms. This won't work on win9x, however. The flag will
256 * not have any effect on non-console applications.
259 retVal
= CreateProcess(NULL
,
260 // const_cast<char*>(mTargetPath.get()),
262 NULL
, /* security attributes for the new
264 NULL
, /* security attributes for the primary
265 * thread in the new process */
266 FALSE
, /* inherit handles */
267 CREATE_NO_WINDOW
, /* creation flags */
269 NULL
, /* current drive and directory */
276 // if success, wait for process termination. the early returns and such
277 // are a bit ugly but preserving the logic of the nspr code I copied to
278 // minimize our risk abit.
280 if ( retVal
== TRUE
) {
282 unsigned long exitCode
;
284 dwRetVal
= WaitForSingleObject(procInfo
.hProcess
, INFINITE
);
285 if (dwRetVal
== WAIT_FAILED
) {
286 nsMemory::Free(my_argv
);
287 return NS_ERROR_FAILURE
;
289 if (GetExitCodeProcess(procInfo
.hProcess
, &exitCode
) == FALSE
) {
290 mExitValue
= exitCode
;
291 nsMemory::Free(my_argv
);
292 return NS_ERROR_FAILURE
;
294 mExitValue
= exitCode
;
295 CloseHandle(procInfo
.hProcess
);
296 CloseHandle(procInfo
.hThread
);
303 // map return value into success code
311 #else // Note, this must not be an #elif ...!
313 #if defined(XP_MACOSX)
318 nsCOMPtr
<nsILocalFileMac
> macExecutable
=
319 do_QueryInterface(mExecutable
);
320 macExecutable
->GetFSSpec(&resolvedSpec
);
322 LaunchParamBlockRec launchPB
;
323 launchPB
.launchAppSpec
= &resolvedSpec
;
324 launchPB
.launchAppParameters
= NULL
;
325 launchPB
.launchBlockID
= extendedBlock
;
326 launchPB
.launchEPBLength
= extendedBlockLen
;
327 launchPB
.launchFileFlags
= NULL
;
328 launchPB
.launchControlFlags
=
329 launchContinue
+ launchNoFileFlags
+ launchUseMinimum
;
331 launchPB
.launchControlFlags
+= launchDontSwitch
;
333 err
= LaunchApplication(&launchPB
);
335 // NOTE: blocking mode assumes you are running on a thread
336 // other than the UI thread that has the main event loop
337 if (blocking
&& err
== noErr
) {
340 info
.processInfoLength
= sizeof(ProcessInfoRec
);
341 info
.processName
= NULL
;
342 info
.processAppSpec
= NULL
;
344 err
= GetProcessInformation(&launchPB
.launchProcessSN
, &info
);
347 // The process is no longer in the process
348 // manager's internal list, assume the process is
355 // still running so sleep some more (200 msecs)
371 mProcess
= PR_CreateProcess(mTargetPath
.get(), my_argv
, NULL
,
374 status
= PR_WaitProcess(mProcess
, &mExitValue
);
376 status
= PR_CreateProcessDetached(mTargetPath
.get(), my_argv
, NULL
,
383 nsMemory::Free(my_argv
);
385 if (status
!= PR_SUCCESS
)
386 return NS_ERROR_FILE_EXECUTION_FAILED
;
391 NS_IMETHODIMP
nsProcess::InitWithPid(PRUint32 pid
)
393 return NS_ERROR_NOT_IMPLEMENTED
;
397 nsProcess::GetLocation(nsIFile
** aLocation
)
399 return NS_ERROR_NOT_IMPLEMENTED
;
403 nsProcess::GetPid(PRUint32
*aPid
)
405 return NS_ERROR_NOT_IMPLEMENTED
;
409 nsProcess::GetProcessName(char** aProcessName
)
411 return NS_ERROR_NOT_IMPLEMENTED
;
415 nsProcess::GetProcessSignature(PRUint32
*aProcessSignature
)
417 return NS_ERROR_NOT_IMPLEMENTED
;
425 rv
= PR_KillProcess(mProcess
) == PR_SUCCESS
? NS_OK
: NS_ERROR_FAILURE
;
431 nsProcess::GetExitValue(PRInt32
*aExitValue
)
433 *aExitValue
= mExitValue
;