1
/* ***** BEGIN LICENSE BLOCK *****
2
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
2
4
* The contents of this file are subject to the Mozilla Public
3
* License Version 1.1 (the "License"); you may not use this file
4
* except in compliance with the License. You may obtain a copy of
5
* the License at http://www.mozilla.org/MPL/
5
* License Version 1.1 (the "MPL"); you may not use this file
6
* except in compliance with the MPL. You may obtain a copy of
7
* the MPL at http://www.mozilla.org/MPL/
7
* Software distributed under the License is distributed on an "AS
9
* Software distributed under the MPL is distributed on an "AS
8
10
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9
* implied. See the License for the specific language governing
10
* rights and limitations under the License.
11
* implied. See the MPL for the specific language governing
12
* rights and limitations under the MPL.
12
14
* The Original Code is the Netscape Portable Runtime (NSPR).
20
22
* Ramalingam Saravanan <svn@xmlterm.org>
21
23
* Patrick Brunschwig <patrick@mozilla-enigmail.org>
23
* Alternatively, the contents of this file may be used under the
24
* terms of the GNU General Public License Version 2 or later (the
25
* "GPL"), in which case the provisions of the GPL are applicable
26
* instead of those above. If you wish to allow use of your
27
* version of this file only under the terms of the GPL and not to
28
* allow others to use your version of this file under the MPL,
29
* indicate your decision by deleting the provisions above and
30
* replace them with the notice and other provisions required by
31
* the GPL. If you do not delete the provisions above, a recipient
32
* may use your version of this file under either the MPL or the
25
* Alternatively, the contents of this file may be used under the terms of
26
* either the GNU General Public License Version 2 or later (the "GPL"), or
27
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28
* in which case the provisions of the GPL or the LGPL are applicable instead
29
* of those above. If you wish to allow use of your version of this file only
30
* under the terms of either the GPL or the LGPL, and not to allow others to
31
* use your version of this file under the terms of the MPL, indicate your
32
* decision by deleting the provisions above and replace them with the notice
33
* and other provisions required by the GPL or the LGPL. If you do not delete
34
* the provisions above, a recipient may use your version of this file under
35
* the terms of any one of the MPL, the GPL or the LGPL.
36
* ***** END LICENSE BLOCK ***** */
36
// Logging of debug output
37
// The following define statement should occur before any include statements
38
#define FORCE_PR_LOG /* Allow logging even in release build */
41
40
#include "IPCProcess.h"
44
extern PRLogModuleInfo* gIPCServiceLog;
47
#define ERROR_LOG(args) PR_LOG(gIPCServiceLog,PR_LOG_ERROR,args)
48
#define WARNING_LOG(args) PR_LOG(gIPCServiceLog,PR_LOG_WARNING,args)
49
#define DEBUG_LOG(args) PR_LOG(gIPCServiceLog,PR_LOG_DEBUG,args)
51
///////////////////////////////////////////////////////////////////////////////
54
44
#include <windows.h>
225
229
int argNeedQuotes;
228
* Find out how large the command line buffer should be.
231
UINT codePage = CP_UTF8; // the code page to use for the parameter strings
233
// Find out how large the command line buffer should be.
231
236
for (arg = argv; *arg; arg++) {
233
* \ and " need to be escaped by a \. In the worst case,
234
* every character is a \ or ", so the string of length
235
* may double. If we quote an argument, that needs two ".
236
* Finally, we need a space between arguments, and
237
* a null byte at the end of command line.
239
cmdLineSize += 2 * strlen(*arg) /* \ and " need to be escaped */
240
+ 2 /* we quote every argument */
241
+ 1; /* space in between, or final null */
238
// \ and " need to be escaped by a \. In the worst case,
239
// every character is a \ or ", so the string of length
240
// may double. If we quote an argument, that needs two ".
241
// Finally, we need a space between arguments, and
242
// a null byte at the end of command line.
244
cmdLineSize += 2 * strlen(*arg) + // \ and " need to be escaped
245
2+ // we quote every argument
246
1; // space in between, or final null
243
248
p = cmdLine = (char *) PR_MALLOC(cmdLineSize*sizeof(char));
311
315
if (argNeedQuotes) {
317
PRInt32 numChars = MultiByteToWideChar(CP_ACP, 0, cmdLine, -1, NULL, 0);
321
PRInt32 numChars = MultiByteToWideChar(codePage, 0, cmdLine, -1, NULL, 0);
318
322
*wideCmdLine = (PRUnichar *) PR_MALLOC(numChars*sizeof(PRUnichar));
319
MultiByteToWideChar(CP_ACP, 0, cmdLine, -1, *wideCmdLine, numChars);
323
MultiByteToWideChar(codePage, 0, cmdLine, -1, *wideCmdLine, numChars);
320
324
PR_Free(cmdLine);
330
334
* in *envBlock. Note that if envp is NULL, a NULL pointer is returned
331
335
* in *envBlock. Returns -1 on failure.
333
static int assembleEnvBlock(char *const *envp, char **envBlock)
337
static int assembleEnvBlock(char *const *envp, PRUnichar **envBlock)
339
char *cwdStart, *cwdEnd;
347
curEnv = GetEnvironmentStrings();
351
if (cwdStart[0] == '=' && cwdStart[1] != '\0'
352
&& cwdStart[2] == ':' && cwdStart[3] == '=') {
355
cwdStart += strlen(cwdStart) + 1;
359
cwdEnd += strlen(cwdEnd) + 1;
361
if (cwdEnd[0] != '=' || cwdEnd[1] == '\0'
362
|| cwdEnd[2] != ':' || cwdEnd[3] != '=') {
365
cwdEnd += strlen(cwdEnd) + 1;
368
envBlockSize = cwdEnd - cwdStart;
370
for (env = envp; *env; env++) {
371
envBlockSize += strlen(*env) + 1;
341
PRInt32 envBlockSize = 0;
348
PRInt32 len, maxLen = 0;
349
for (env = envp; *env; env++) {
350
len = MultiByteToWideChar(CP_UTF8, 0, *env, -1, NULL, 0);
352
maxLen = (len > maxLen ? len : maxLen);
375
p = *envBlock = (char*) PR_MALLOC(envBlockSize);
377
FreeEnvironmentStrings(curEnv);
356
PRUnichar* tempStr = (PRUnichar *) PR_MALLOC(maxLen * sizeof(PRUnichar));
357
p = *envBlock = (PRUnichar *) PR_MALLOC(envBlockSize * sizeof(PRUnichar));
359
for (env = envp; *env; env++) {
361
len = MultiByteToWideChar(CP_UTF8, 0, *env, -1, NULL, 0);
362
MultiByteToWideChar(CP_UTF8, 0, *env, -1, tempStr, len);
366
// copy data (one by one)
385
FreeEnvironmentStrings(curEnv);
387
for (env = envp; *env; env++) {
409
DEBUG_LOG(("IPCProcess: createProcess %p, %p, %p\n", std_in,
413
391
// Determine OS Version
414
392
OSVERSIONINFO osvi;
415
BOOL bIsWindows2KOrLater = FALSE;
393
BOOL bIsWin2KOrLater = FALSE;
417
395
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
418
396
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
420
398
if (GetVersionEx(&osvi)) {
421
bIsWindows2KOrLater = (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
399
bIsWin2KOrLater = (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
422
400
(osvi.dwMajorVersion >= 5);
446
424
// we need to set argv[0] to the program name.
447
425
my_argv[0] = const_cast<char*>(path);
448
PRInt32 numChars = MultiByteToWideChar(CP_ACP, 0, my_argv[0], -1, NULL, 0);
426
PRInt32 numChars = MultiByteToWideChar(CP_UTF8, 0, my_argv[0], -1, NULL, 0);
449
427
PRUnichar* wideFile = (PRUnichar *) PR_MALLOC(numChars * sizeof(PRUnichar));
450
MultiByteToWideChar(CP_ACP, 0, my_argv[0], -1, wideFile, numChars);
428
MultiByteToWideChar(CP_UTF8, 0, my_argv[0], -1, wideFile, numChars);
452
430
// null terminate the array
453
431
my_argv[count+1] = NULL;
455
433
PRUnichar *cmdLine = NULL;
456
if (assembleCmdLine(argv, &cmdLine) == -1)
434
if (assembleCmdLine(argv, &cmdLine) != 0)
457
435
return IPC_NULL_HANDLE;
459
char *envBlock = NULL;
460
if (assembleEnvBlock(envp, &envBlock) == -1)
437
PRUnichar *envBlock = NULL;
438
if (assembleEnvBlock(envp, &envBlock) != 0) {
461
440
return IPC_NULL_HANDLE;
463
443
PRUnichar* wideCwd = NULL;
465
445
if (cwd != NULL) {
466
numChars = MultiByteToWideChar(CP_ACP, 0, cwd, -1, NULL, 0);
446
numChars = MultiByteToWideChar(CP_UTF8, 0, cwd, -1, NULL, 0);
467
447
PRUnichar* wideCwd = (PRUnichar *) PR_MALLOC(numChars * sizeof(PRUnichar));
468
MultiByteToWideChar(CP_ACP, 0, cwd, -1, wideCwd, numChars);
469
DEBUG_LOG(("IPCProcess: createProcess converted cwd to %s\n", NS_ConvertUTF16toUTF8(wideCwd).get()));
448
MultiByteToWideChar(CP_UTF8, 0, cwd, -1, wideCwd, numChars);
472
451
// Fill in the process's startup information
473
STARTUPINFOW startupInfo;
474
memset( &startupInfo, 0, sizeof(STARTUPINFOW) );
475
startupInfo.cb = sizeof(STARTUPINFOW);
476
startupInfo.dwFlags = STARTF_USESTDHANDLES;
477
startupInfo.hStdInput = std_in ? (HANDLE) std_in : GetStdHandle(STD_INPUT_HANDLE);
478
startupInfo.hStdOutput = std_out ? (HANDLE) std_out : GetStdHandle(STD_OUTPUT_HANDLE);
479
startupInfo.hStdError = std_err ? (HANDLE) std_err : GetStdHandle(STD_ERROR_HANDLE);
480
DWORD dwCreationFlags = CREATE_DEFAULT_ERROR_MODE /*| CREATE_UNICODE_ENVIRONMENT */;
453
memset( &sInfo, 0, sizeof(STARTUPINFOW) );
454
sInfo.cb = sizeof(STARTUPINFOW);
455
sInfo.dwFlags = STARTF_USESTDHANDLES;
456
sInfo.hStdInput = std_in ? (HANDLE)std_in : GetStdHandle(STD_INPUT_HANDLE);
457
sInfo.hStdOutput = std_out ? (HANDLE)std_out : GetStdHandle(STD_OUTPUT_HANDLE);
458
sInfo.hStdError = std_err ? (HANDLE)std_err : GetStdHandle(STD_ERROR_HANDLE);
459
DWORD dwCreationFlags = CREATE_DEFAULT_ERROR_MODE | CREATE_UNICODE_ENVIRONMENT;
483
462
BOOL bIsConsoleAttached = (GetConsoleTitle(buf, 127) > 0);
485
if (bIsWindows2KOrLater && !bIsConsoleAttached) {
464
if (bIsWin2KOrLater && !bIsConsoleAttached) {
486
465
// Create and hide child process console
487
466
// Does not work from Win2K console (and not at all on Win95!)
488
startupInfo.dwFlags |= STARTF_USESHOWWINDOW;
489
startupInfo.wShowWindow = SW_HIDE;
467
sInfo.dwFlags |= STARTF_USESHOWWINDOW;
468
sInfo.wShowWindow = SW_HIDE;
490
469
dwCreationFlags |= CREATE_SHARED_WOW_VDM;
491
470
dwCreationFlags |= CREATE_NEW_CONSOLE;
503
482
PROCESS_INFORMATION processInfo;
505
bRetVal = CreateProcessW(wideFile, // executable
506
cmdLine, // command line
507
NULL, // process security
508
NULL, // thread security
509
TRUE, // inherit handles
510
dwCreationFlags, // creation flags
511
envBlock, // environment
513
&startupInfo, // startup info
514
&processInfo ); // process info (returned)
516
DEBUG_LOG(("IPCProcess: created process %s (%d) %p: %s\n", NS_ConvertUTF16toUTF8(cmdLine).get(),
517
(int) bRetVal, processInfo.hProcess, envBlock));
484
bRetVal = CreateProcessW(wideFile, // executable
485
cmdLine, // command line
486
NULL, // process security
487
NULL, // thread security
488
TRUE, // inherit handles
489
dwCreationFlags, // creation flags
490
envBlock, // environment
492
&sInfo, // startup info
493
&processInfo ); // process info (returned)
520
497
PR_Free(cmdLine);
523
500
PR_Free(wideCwd);
526
503
PR_DELETE(envBlock);
528
505
nsMemory::Free(my_argv);
530
508
// Close handle to primary thread of process (we don't need it)
531
509
CloseHandle(processInfo.hThread);
534
513
return IPC_NULL_HANDLE;
536
516
return processInfo.hProcess;
636
611
* between 0 and 255. So here on Windows, we use the exit code
637
612
* 256 to indicate that the process is killed.
639
DEBUG_LOG(("IPCProcess: killing %p\n", process));
641
if (TerminateProcess((HANDLE) process, 256))
615
return TerminateProcess((HANDLE) process, 256) ? PR_SUCCESS : PR_FAILURE;
618
PRStatus IPC_GetProcessIdWin32(IPCProcess* process, PRInt32 *pid)
624
HMODULE kernelDLL = ::LoadLibraryW(L"kernel32.dll");
626
GetProcessIdPtr getProcessId = (GetProcessIdPtr)GetProcAddress(kernelDLL,
629
*pid = getProcessId((HANDLE) process);
631
FreeLibrary(kernelDLL);
648
637
PRInt32 IPC_ReadWin32(IPCFileDesc* fd, void *buf, PRInt32 amount)