Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F35444340
nsPipeTransport.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
64 KB
Subscribers
None
nsPipeTransport.cpp
View Options
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "MPL"); you may not use this file
* except in compliance with the MPL. You may obtain a copy of
* the MPL at http://www.mozilla.org/MPL/
*
* Software distributed under the MPL is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the MPL for the specific language governing
* rights and limitations under the MPL.
*
* The Original Code is protoZilla.
*
* The Initial Developer of the Original Code is Ramalingam Saravanan.
* Portions created by Ramalingam Saravanan <svn@xmlterm.org> are
* Copyright (C) 2000 Ramalingam Saravanan. All Rights Reserved.
*
* Contributor(s):
* Patrick Brunschwig <patrick@mozilla-enigmail.org>
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License (the "GPL"), in which case
* the provisions of the GPL are applicable instead of
* those above. If you wish to allow use of your version of this
* file only under the terms of the GPL and not to allow
* others to use your version of this file under the MPL, indicate
* your decision by deleting the provisions above and replace them
* with the notice and other provisions required by the GPL.
* If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
// Logging of debug output
// The following define statement should occur before any include statements
#define FORCE_PR_LOG
/* Allow logging even in release build */
#include
"ipc.h"
#include
"prlog.h"
#include
"nsAutoLock.h"
#include
"plstr.h"
#include
"nsReadableUtils.h"
#include
"netCore.h"
#include
"nsIServiceManager.h"
#include
"nsIProxyObjectManager.h"
#include
"nsIURI.h"
#include
"nsIHttpChannel.h"
#include
"nsIIPCService.h"
#include
"nsPipeTransport.h"
#ifdef _IPC_MOZILLA_1_8
#include
"nsEventQueueUtils.h"
#else
#include
"nsThreadUtils.h"
#include
"nsXPCOMCIDInternal.h"
#endif
#ifdef PR_LOGGING
PRLogModuleInfo
*
gPipeTransportLog
=
NULL
;
#endif
#define ERROR_LOG(args) PR_LOG(gPipeTransportLog,PR_LOG_ERROR,args)
#define WARNING_LOG(args) PR_LOG(gPipeTransportLog,PR_LOG_WARNING,args)
#define DEBUG_LOG(args) PR_LOG(gPipeTransportLog,PR_LOG_DEBUG,args)
#ifdef XP_WIN
// Workaround for bug(?) in Win32 implementation of PR_Poll,
// which seems to return PR_POLL_NVAL instead of PR_POLL_READ
// See mozilla/nsprpub/pr/src/md/windows/w32poll.c)
#define POLL_READ_FLAGS (PR_POLL_READ | PR_POLL_NVAL)
#else
#define POLL_READ_FLAGS PR_POLL_READ
#endif
// If defined, use pollable events to interrupt pipe transport threads
#define PIPETRANSPORT_USE_POLLABLE_EVENT
static
const
PRUint32
kCharMax
=
NS_PIPE_TRANSPORT_DEFAULT_SEGMENT_SIZE
;
// Default time after which a process is assumed to be dead (in ms.)
#define DEFAULT_PROCESS_TIMEOUT_IN_MS 3600*1000
// Time to wait after transmitting any kill string to process (in milliseconds)
#define KILL_WAIT_TIME_IN_MS 20
///////////////////////////////////////////////////////////////////////////////
nsPipeTransport
::
nsPipeTransport
()
:
mFinalized
(
PR_FALSE
),
mNoProxy
(
PR_FALSE
),
mStartedRequest
(
PR_FALSE
),
mPipeState
(
PIPE_NOT_YET_OPENED
),
mStdoutStream
(
STREAM_NOT_YET_OPENED
),
mCancelStatus
(
NS_OK
),
mLoadFlags
(
LOAD_NORMAL
),
mNotificationFlags
(
0
),
mExecutable
(
""
),
mCommand
(
""
),
mKillString
(
""
),
mProcess
(
IPC_NULL_HANDLE
),
mKillWaitInterval
(
PR_MillisecondsToInterval
(
KILL_WAIT_TIME_IN_MS
)),
mExitCode
(
0
),
mBufferSegmentSize
(
NS_PIPE_TRANSPORT_DEFAULT_SEGMENT_SIZE
),
mBufferMaxSize
(
NS_PIPE_TRANSPORT_DEFAULT_BUFFER_SIZE
),
mHeadersMaxSize
(
NS_PIPE_TRANSPORT_DEFAULT_HEADERS_SIZE
),
mExecBuf
(
""
),
mStdinWrite
(
IPC_NULL_HANDLE
)
{
NS_INIT_ISUPPORTS
();
#ifdef PR_LOGGING
if
(
gPipeTransportLog
==
nsnull
)
{
gPipeTransportLog
=
PR_NewLogModule
(
"nsPipeTransport"
);
}
#endif
#ifdef FORCE_PR_LOG
nsresult
rv
;
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsPipeTransport:: <<<<<<<<< CTOR(%p): myThread=%p
\n
"
,
this
,
myThread
.
get
()));
#endif
}
nsPipeTransport
::~
nsPipeTransport
()
{
nsresult
rv
;
#ifdef FORCE_PR_LOG
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsPipeTransport:: >>>>>>>>> DTOR(%p): myThread=%p START
\n
"
,
this
,
myThread
.
get
()));
#endif
Finalize
(
PR_TRUE
);
// Release refs to objects that do not hold strong refs to this
mInputStream
=
nsnull
;
mOutputStream
=
nsnull
;
DEBUG_LOG
((
"nsPipeTransport:: ********* DTOR(%p) END
\n
"
,
this
));
}
//
// --------------------------------------------------------------------------
// nsISupports implementation...
// --------------------------------------------------------------------------
//
NS_IMPL_THREADSAFE_ISUPPORTS8
(
nsPipeTransport
,
nsIPipeTransport
,
nsIPipeTransportHeaders
,
nsIPipeTransportListener
,
nsIRequest
,
nsIOutputStream
,
nsIStreamListener
,
nsIInputStreamCallback
,
nsIOutputStreamCallback
)
///////////////////////////////////////////////////////////////////////////////
// nsIPipeTransport methods
///////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsPipeTransport
::
InitCommand
(
const
char
*
command
,
const
char
**
env
,
PRUint32
envCount
,
PRUint32
timeoutMS
,
const
char
*
killString
,
PRBool
noProxy
,
PRBool
mergeStderr
,
nsIPipeListener
*
console
)
{
nsresult
rv
;
DEBUG_LOG
((
"nsPipeTransport::InitCommand: command=%s [%d]
\n
"
,
command
,
envCount
));
if
(
!
command
)
return
NS_ERROR_FAILURE
;
mCommand
=
command
;
// Create a buffer of same size as the command string
PRUint32
len
=
strlen
(
command
);
char
*
buf
=
(
char
*
)
PR_Malloc
(
sizeof
(
char
)
*
(
len
+
1
)
);
// Parse command arguments separated by whitespace
PRUint32
j
;
char
quote
=
'\0'
;
PRBool
backquote
=
PR_FALSE
;
PRBool
inArg
=
PR_FALSE
;
PRUint32
bufCount
=
0
;
PRUint32
argCount
=
0
;
for
(
j
=
0
;
j
<
len
;
j
++
)
{
char
ch
=
command
[
j
];
if
(
!
quote
&&
!
backquote
)
{
// Unquoted character
if
((
ch
==
' '
)
||
(
ch
==
'\t'
)
||
(
ch
==
'\r'
)
||
(
ch
==
'\n'
))
{
// Whitespace (skip)
if
(
inArg
)
{
// End argument parsing; insert null character in buffer
buf
[
bufCount
++
]
=
'\0'
;
inArg
=
PR_FALSE
;
}
}
else
if
(
!
inArg
)
{
// Non-whitespace character; start parsing new argument
inArg
=
PR_TRUE
;
argCount
++
;
}
}
if
(
inArg
)
{
// Argument parsing
if
(
backquote
)
{
// Backquoted character; append to buffer
buf
[
bufCount
++
]
=
ch
;
backquote
=
PR_FALSE
;
}
else
if
(
ch
==
'\\'
)
{
// Backquote following character
backquote
=
PR_TRUE
;
}
else
if
(
quote
==
ch
)
{
// Matching end quote
quote
=
'\0'
;
}
else
if
(
!
quote
&&
((
ch
==
'"'
)
||
(
ch
==
'\''
))
)
{
// Start new quote
quote
=
ch
;
}
else
{
// Append character to buffer (quoted/unquoted)
buf
[
bufCount
++
]
=
ch
;
}
}
}
if
(
inArg
)
buf
[
bufCount
++
]
=
'\0'
;
// End argument parsing
PR_ASSERT
(
bufCount
<=
(
len
+
1
));
// No buffer overflow
if
(
quote
)
{
ERROR_LOG
((
"nsPipeTransport::InitCommand: Unmatched quote in command string
\n
"
));
PR_Free
(
buf
);
return
NS_ERROR_FAILURE
;
}
if
(
!
argCount
)
{
ERROR_LOG
((
"nsPipeTransport::InitCommand: Blank/null command string
\n
"
));
PR_Free
(
buf
);
return
NS_ERROR_FAILURE
;
}
DEBUG_LOG
((
"nsPipeTransport::InitCommand: argCount=%d
\n
"
,
argCount
));
// Argument list (includes command path as the first argument)
char
**
args
=
(
char
**
)
PR_Malloc
(
sizeof
(
char
*
)
*
(
argCount
+
1
)
);
if
(
!
args
)
return
NS_ERROR_OUT_OF_MEMORY
;
PRUint32
argOffset
=
0
;
for
(
j
=
0
;
j
<
argCount
;
j
++
)
{
args
[
j
]
=
buf
+
argOffset
;
argOffset
+=
strlen
(
args
[
j
])
+
1
;
}
PR_ASSERT
(
argOffset
==
bufCount
);
args
[
argCount
]
=
NULL
;
rv
=
Init
((
const
char
*
)
args
[
0
],
(
const
char
**
)
args
+
1
,
argCount
-1
,
env
,
envCount
,
timeoutMS
,
killString
,
noProxy
,
mergeStderr
,
console
);
PR_Free
(
buf
);
return
rv
;
}
NS_IMETHODIMP
nsPipeTransport
::
Init
(
const
char
*
executable
,
const
char
**
args
,
PRUint32
argCount
,
const
char
**
env
,
PRUint32
envCount
,
PRUint32
timeoutMS
,
const
char
*
killString
,
PRBool
noProxy
,
PRBool
mergeStderr
,
nsIPipeListener
*
console
)
{
nsresult
rv
;
PRUint32
j
;
DEBUG_LOG
((
"nsPipeTransport::Init: executable=%s [%d]
\n
"
,
executable
,
envCount
));
if
(
mPipeState
!=
PIPE_NOT_YET_OPENED
)
{
return
NS_ERROR_ALREADY_INITIALIZED
;
}
mNoProxy
=
noProxy
;
mConsole
=
console
;
PRIntervalTime
timeoutInterval
=
PR_MillisecondsToInterval
(
DEFAULT_PROCESS_TIMEOUT_IN_MS
);
if
(
timeoutMS
>
0
)
{
// Specified process timeout
timeoutInterval
=
PR_MillisecondsToInterval
(
timeoutMS
);
}
mExecutable
.
Assign
(
executable
);
mKillString
.
Assign
(
killString
);
// Create pipes for stdin/stdout/stderr
PRStatus
status
;
int
npipe
;
IPCFileDesc
*
stdinRead
=
nsnull
;
IPCFileDesc
*
stdoutWrite
=
nsnull
;
IPCFileDesc
*
stdoutRead
=
nsnull
;
IPCFileDesc
*
stderrWrite
=
nsnull
;
IPCFileDesc
*
stderrRead
=
nsnull
;
#ifdef XP_WIN
// PR_Poll does not seem to work with pipes in Win32
// Merge STDOUT and STDERR directly, if need be
npipe
=
2
;
#else
// Merging STDOUT and STDERR directly does not seem to work in Unix
// Use PR_Poll if STDERR is to be merged
npipe
=
mergeStderr
?
3
:
2
;
#endif
for
(
int
ipipe
=
0
;
ipipe
<
npipe
;
ipipe
++
)
{
// Create pipe pair
IPCFileDesc
*
fd
[
2
];
status
=
IPC_CreateInheritablePipe
(
&
fd
[
0
],
&
fd
[
1
],
(
ipipe
==
0
),
(
ipipe
!=
0
));
if
(
status
!=
PR_SUCCESS
)
{
ERROR_LOG
((
"nsPipeTransport::Init: Error in creating pipe %d
\n
"
,
ipipe
));
return
NS_ERROR_FAILURE
;
}
// Copy pipe file descriptors
if
(
ipipe
==
0
)
{
// STDIN
stdinRead
=
fd
[
0
];
mStdinWrite
=
fd
[
1
];
}
else
if
(
ipipe
==
1
)
{
// STDOUT
stdoutRead
=
fd
[
0
];
stdoutWrite
=
fd
[
1
];
}
else
{
// STDERR
stderrRead
=
fd
[
0
];
stderrWrite
=
fd
[
1
];
}
}
IPCFileDesc
*
stderrPipe
;
if
(
stderrWrite
)
{
// This STDOUT/STDERR merging technique works on Unix only (uses PR_Poll)
stderrPipe
=
stderrWrite
;
}
else
if
(
mergeStderr
)
{
// This STDOUT/STDERR merging technique works on Win32 only (uses same FD)
stderrPipe
=
stdoutWrite
;
}
else
{
// Re-direct STDERR to console
nsCOMPtr
<
nsIPipeListener
>
console
(
mConsole
);
if
(
!
console
)
{
nsCOMPtr
<
nsIIPCService
>
ipcserv
=
do_GetService
(
NS_IPCSERVICE_CONTRACTID
,
&
rv
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
nsCOMPtr
<
nsIPipeConsole
>
ipcConsole
;
rv
=
ipcserv
->
GetConsole
(
getter_AddRefs
(
ipcConsole
));
NS_ENSURE_SUCCESS
(
rv
,
rv
);
console
=
ipcConsole
;
}
rv
=
console
->
GetFileDesc
(
&
stderrPipe
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
DEBUG_LOG
((
"nsPipeTransport::Init: stderrPipe=0x%p
\n
"
,
stderrPipe
));
}
char
**
argList
=
NULL
;
if
(
argCount
<
0
)
return
NS_ERROR_FAILURE
;
// Extended copy of argument list (execpath, args, NULL)
argList
=
(
char
**
)
PR_Malloc
(
sizeof
(
char
*
)
*
(
argCount
+
2
)
);
if
(
!
argList
)
return
NS_ERROR_OUT_OF_MEMORY
;
argList
[
0
]
=
(
char
*
)
mExecutable
.
get
();
for
(
j
=
0
;
j
<
argCount
;
j
++
)
{
argList
[
j
+
1
]
=
(
char
*
)
args
[
j
];
DEBUG_LOG
((
"nsPipeTransport::Init: arg[%d] = %s
\n
"
,
j
+
1
,
args
[
j
]));
}
argList
[
argCount
+
1
]
=
NULL
;
char
**
envList
=
NULL
;
if
(
envCount
>
0
)
{
// Extended copy of environment variable list (env, NULL)
envList
=
(
char
**
)
PR_Malloc
(
sizeof
(
char
*
)
*
(
envCount
+
1
)
);
if
(
!
envList
)
return
NS_ERROR_OUT_OF_MEMORY
;
for
(
j
=
0
;
j
<
envCount
;
j
++
)
envList
[
j
]
=
(
char
*
)
env
[
j
];
envList
[
envCount
]
=
NULL
;
}
/* Create NSPR process */
mProcess
=
IPC_CreateProcessRedirected
(
mExecutable
.
get
(),
argList
,
envList
,
nsnull
,
stdinRead
,
stdoutWrite
,
stderrPipe
);
// Do some clean-up for pointers on stack
// before checking if process creation succeeded
// (Clean-up for pointers in member variables will be done by the DTOR)
// Free argument/environment lists
PR_Free
(
argList
);
if
(
envList
)
PR_Free
(
envList
);
if
(
mProcess
==
IPC_NULL_HANDLE
)
{
// Process creation failed
ERROR_LOG
((
"nsPipeTransport::Init: Error in creating process ...
\n
"
));
return
NS_ERROR_FILE_EXECUTION_FAILED
;
}
DEBUG_LOG
((
"nsPipeTransport::Init: Created process %p, %s
\n
"
,
mProcess
,
mExecutable
.
get
()
));
// Close process-side STDIN/STDOUT/STDERR pipes
IPC_Close
(
stdinRead
);
stdinRead
=
nsnull
;
IPC_Close
(
stdoutWrite
);
stdoutWrite
=
nsnull
;
if
(
stderrWrite
)
{
IPC_Close
(
stderrWrite
);
stderrWrite
=
nsnull
;
}
// Create polling helper class (will be deleted when thread terminates?)
nsStdoutPoller
*
stdoutPoller
=
new
nsStdoutPoller
();
if
(
!
stdoutPoller
)
return
NS_ERROR_OUT_OF_MEMORY
;
mStdoutPoller
=
stdoutPoller
;
// owning ref
// Initialize polling helper class
rv
=
stdoutPoller
->
Init
(
stdoutRead
,
stderrRead
,
timeoutInterval
,
mConsole
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
mPipeState
=
PIPE_OPEN
;
return
NS_OK
;
}
// Should only be called from the thread that created the nsIPipeTransport
nsresult
nsPipeTransport
::
Finalize
(
PRBool
destructor
)
{
if
(
mFinalized
)
return
NS_OK
;
mFinalized
=
PR_TRUE
;
nsresult
rv
=
NS_OK
;
DEBUG_LOG
((
"nsPipeTransport::Finalize:
\n
"
));
if
(
mPipeState
==
PIPE_CLOSED
)
return
NS_OK
;
nsCOMPtr
<
nsIPipeTransport
>
self
;
if
(
!
destructor
)
{
// Hold a reference to ourselves to prevent our DTOR from being called
// while finalizing. Automatically released upon returning.
self
=
this
;
}
mPipeState
=
PIPE_CLOSED
;
// Close standard output
mStdoutStream
=
STREAM_CLOSED
;
PRBool
alreadyInterrupted
=
PR_FALSE
;
if
(
mStdoutPoller
)
{
// Interrupt Stdout thread
// (calls to OnStopRequest are handled by that thread)
rv
=
mStdoutPoller
->
Interrupt
(
&
alreadyInterrupted
);
if
(
NS_FAILED
(
rv
))
{
ERROR_LOG
((
"nsPipeTransport::Finalize: Failed to interrupt Stdout thread, %x
\n
"
,
rv
));
rv
=
NS_ERROR_FAILURE
;
}
else
if
(
mNoProxy
)
{
// Join poller thread to free resources (may block)
rv
=
mStdoutPoller
->
Join
();
if
(
NS_FAILED
(
rv
))
{
ERROR_LOG
((
"nsPipeTransport::Finalize: Failed to join Stdout thread, %x
\n
"
,
rv
));
rv
=
NS_ERROR_FAILURE
;
}
}
}
// Kill process to wake up thread blocked for input from process
// NOTE: This should always be done after "interrupting" the thread
// so that the interrupt flag is set.
KillProcess
();
// Release refs to input arguments
mListener
=
nsnull
;
mContext
=
nsnull
;
mLoadGroup
=
nsnull
;
// Release owning refs
mConsole
=
nsnull
;
mHeaderProcessor
=
nsnull
;
// Release refs to objects that hold strong refs to this
mStdoutPoller
=
nsnull
;
// Clear buffer
mExecBuf
.
Assign
(
""
);
return
rv
;
}
void
nsPipeTransport
::
KillProcess
(
void
)
{
// Process cleanup
if
(
mProcess
==
IPC_NULL_HANDLE
)
return
;
if
((
mStdinWrite
!=
IPC_NULL_HANDLE
)
&&
mKillString
.
get
()
&&
(
strlen
(
mKillString
.
get
())
>
0
))
{
// Transmit kill string to process
PRInt32
writeCount
;
writeCount
=
IPC_Write
(
mStdinWrite
,
mKillString
.
get
(),
strlen
(
mKillString
.
get
()));
if
(
writeCount
!=
(
int
)
strlen
(
mKillString
.
get
()))
{
WARNING_LOG
((
"KillProcess: Failed to send kill string
\n
"
));
}
// Wait a few milliseconds for cleanup
PR_Sleep
(
mKillWaitInterval
);
}
// Close our end of STDIN pipe, if open
CloseStdin
();
PRStatus
status
;
// Kill process
status
=
IPC_KillProcess
(
mProcess
);
if
(
status
!=
PR_SUCCESS
)
DEBUG_LOG
((
"nsPipeTransport::KillProcess: Failed to kill process
\n
"
));
else
DEBUG_LOG
((
"nsPipeTransport::KillProcess: Killed process
\n
"
));
// Reap process (to avoid memory leaks in NSPR)
// **NOTE** This could cause this (UI?) thread to hang
status
=
IPC_WaitProcess
(
mProcess
,
&
mExitCode
);
if
(
status
!=
PR_SUCCESS
)
WARNING_LOG
((
"nsPipeTransport::KillProcess: Failed to reap process
\n
"
));
mProcess
=
IPC_NULL_HANDLE
;
return
;
}
NS_IMETHODIMP
nsPipeTransport
::
GetHeaderProcessor
(
nsIPipeTransportHeaders
*
*
_retval
)
{
if
(
!
_retval
)
return
NS_ERROR_NULL_POINTER
;
NS_IF_ADDREF
(
*
_retval
=
mHeaderProcessor
.
get
());
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
SetHeaderProcessor
(
nsIPipeTransportHeaders
*
aHeaderProcessor
)
{
DEBUG_LOG
((
"nsPipeTransport::SetHeaderProcessor:
\n
"
));
mHeaderProcessor
=
aHeaderProcessor
;
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
GetConsole
(
nsIPipeListener
*
*
_retval
)
{
DEBUG_LOG
((
"nsPipeTransport::GetConsole:
\n
"
));
if
(
!
_retval
)
return
NS_ERROR_NULL_POINTER
;
NS_IF_ADDREF
(
*
_retval
=
mConsole
.
get
());
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
IsAttached
(
PRBool
*
attached
)
{
nsresult
rv
;
DEBUG_LOG
((
"nsPipeTransport::IsAttached:
\n
"
));
if
(
mStdoutPoller
)
{
PRBool
interrupted
;
rv
=
mStdoutPoller
->
IsInterrupted
(
&
interrupted
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
*
attached
=
!
interrupted
;
}
else
{
*
attached
=
PR_FALSE
;
}
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
Join
()
{
nsresult
rv
;
DEBUG_LOG
((
"nsPipeTransport::Join:
\n
"
));
if
(
!
mNoProxy
)
return
NS_ERROR_FAILURE
;
// Close STDIN, if open
CloseStdin
();
if
(
mStdoutPoller
)
{
rv
=
mStdoutPoller
->
Join
();
NS_ENSURE_SUCCESS
(
rv
,
rv
);
mStdoutPoller
=
nsnull
;
}
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
Terminate
()
{
DEBUG_LOG
((
"nsPipeTransport::Terminate:
\n
"
));
// Clean up, killing process if need be
return
Finalize
(
PR_FALSE
);
}
NS_IMETHODIMP
nsPipeTransport
::
ExitCode
(
PRInt32
*
_retval
)
{
nsresult
rv
;
DEBUG_LOG
((
"nsPipeTransport::ExitCode:
\n
"
));
if
(
!
_retval
)
return
NS_ERROR_NULL_POINTER
;
if
(
mStdoutPoller
)
{
// Fail if poller has not been interrupted
PRBool
interrupted
;
rv
=
mStdoutPoller
->
IsInterrupted
(
&
interrupted
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
if
(
!
interrupted
)
return
NS_ERROR_FAILURE
;
}
// Kill process, if need be
// (Needed for synchronous reads where StopRequest is not called)
KillProcess
();
*
_retval
=
mExitCode
;
DEBUG_LOG
((
"nsPipeTransport::ExitCode: exit code = %d
\n
"
,
mExitCode
));
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
GetBufferSegmentSize
(
PRUint32
*
aBufferSegmentSize
)
{
DEBUG_LOG
((
"nsPipeTransport::GetBufferSegmentSize:
\n
"
));
*
aBufferSegmentSize
=
mBufferSegmentSize
;
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
SetBufferSegmentSize
(
PRUint32
aBufferSegmentSize
)
{
DEBUG_LOG
((
"nsPipeTransport::SetBufferSegmentSize:
\n
"
));
mBufferSegmentSize
=
aBufferSegmentSize
;
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
GetBufferMaxSize
(
PRUint32
*
aBufferMaxSize
)
{
DEBUG_LOG
((
"nsPipeTransport::GetBufferMaxSize:
\n
"
));
*
aBufferMaxSize
=
mBufferMaxSize
;
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
SetBufferMaxSize
(
PRUint32
aBufferMaxSize
)
{
DEBUG_LOG
((
"nsPipeTransport::SetBufferMaxSize:
\n
"
));
mBufferMaxSize
=
aBufferMaxSize
;
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
GetHeadersMaxSize
(
PRUint32
*
aHeadersMaxSize
)
{
DEBUG_LOG
((
"nsPipeTransport::GetHeadersMaxSize:
\n
"
));
*
aHeadersMaxSize
=
mHeadersMaxSize
;
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
SetHeadersMaxSize
(
PRUint32
aHeadersMaxSize
)
{
DEBUG_LOG
((
"nsPipeTransport::SetHeadersMaxSize:
\n
"
));
mHeadersMaxSize
=
aHeadersMaxSize
;
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
GetLoggingEnabled
(
PRBool
*
aLoggingEnabled
)
{
if
(
!
mStdoutPoller
)
return
NS_ERROR_NOT_INITIALIZED
;
return
mStdoutPoller
->
GetLoggingEnabled
(
aLoggingEnabled
);
}
NS_IMETHODIMP
nsPipeTransport
::
SetLoggingEnabled
(
PRBool
aLoggingEnabled
)
{
if
(
!
mStdoutPoller
)
return
NS_ERROR_NOT_INITIALIZED
;
return
mStdoutPoller
->
SetLoggingEnabled
(
aLoggingEnabled
);
}
NS_IMETHODIMP
nsPipeTransport
::
OpenInputStream
(
PRUint32
offset
,
PRUint32
count
,
PRUint32
flags
,
nsIInputStream
**
result
)
{
nsresult
rv
=
NS_OK
;
DEBUG_LOG
((
"nsPipeTransport::OpenInputStream:
\n
"
));
if
(
mPipeState
!=
PIPE_OPEN
)
return
NS_ERROR_NOT_INITIALIZED
;
// Check if Stdout stream has already been opened
if
(
mStdoutStream
!=
STREAM_NOT_YET_OPENED
)
return
NS_ERROR_NOT_AVAILABLE
;
mStdoutStream
=
STREAM_SYNC_OPEN
;
// Blocking input
PRBool
nonBlockingInput
=
PR_FALSE
;
// Blocking output
PRBool
nonBlockingOutput
=
PR_FALSE
;
// Open pipe to handle STDOUT
rv
=
NS_NewPipe
(
getter_AddRefs
(
mInputStream
),
getter_AddRefs
(
mOutputStream
),
mBufferSegmentSize
,
mBufferMaxSize
,
nonBlockingInput
,
nonBlockingOutput
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
// Spin up a new thread to handle STDOUT polling
rv
=
mStdoutPoller
->
AsyncStart
(
mOutputStream
,
nsnull
,
PR_FALSE
,
0
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
rv
=
mInputStream
->
QueryInterface
(
NS_GET_IID
(
nsIInputStream
),
(
void
**
)
result
);
return
rv
;
}
NS_IMETHODIMP
nsPipeTransport
::
OpenOutputStream
(
PRUint32
offset
,
PRUint32
count
,
PRUint32
flags
,
nsIOutputStream
**
result
)
{
DEBUG_LOG
((
"nsPipeTransport::OpenOutputStream:
\n
"
));
if
(
mPipeState
!=
PIPE_OPEN
)
return
NS_ERROR_NOT_INITIALIZED
;
return
this
->
QueryInterface
(
NS_GET_IID
(
nsIOutputStream
),
(
void
**
)
result
);
}
NS_IMETHODIMP
nsPipeTransport
::
GetListener
(
nsIStreamListener
**
result
)
{
DEBUG_LOG
((
"nsPipeTransport::GetListener:
\n
"
));
if
(
mPipeState
!=
PIPE_OPEN
)
return
NS_ERROR_NOT_INITIALIZED
;
return
this
->
QueryInterface
(
NS_GET_IID
(
nsIStreamListener
),
(
void
**
)
result
);
}
NS_IMETHODIMP
nsPipeTransport
::
AsyncRead
(
nsIStreamListener
*
listener
,
nsISupports
*
ctxt
,
PRUint32
offset
,
PRUint32
count
,
PRUint32
flags
,
nsIRequest
**
_retval
)
{
nsresult
rv
;
DEBUG_LOG
((
"nsPipeTransport::AsyncRead:
\n
"
));
if
(
!
_retval
)
return
NS_ERROR_NULL_POINTER
;
if
(
mPipeState
!=
PIPE_OPEN
)
return
NS_ERROR_NOT_INITIALIZED
;
// Check if Stdout stream has already been opened
if
(
mStdoutStream
!=
STREAM_NOT_YET_OPENED
)
return
NS_ERROR_NOT_AVAILABLE
;
mStdoutStream
=
STREAM_ASYNC_OPEN
;
nsCOMPtr
<
nsIPipeTransportListener
>
pipeListener
(
nsnull
);
if
(
listener
)
{
// Initialize listening interface
mListener
=
listener
;
mContext
=
ctxt
;
// Non-blocking input stream
PRBool
nonBlockingInput
=
PR_TRUE
;
// Always block output
PRBool
nonBlockingOutput
=
PR_FALSE
;
// Now generate proxied pipe observer/listener to enable async calling
// from the polling thread to the current (UI?) thread
nsCOMPtr
<
nsIProxyObjectManager
>
proxyMgr
=
do_GetService
(
NS_XPCOMPROXY_CONTRACTID
,
&
rv
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
// Open pipe to handle STDOUT
nsCOMPtr
<
nsIAsyncInputStream
>
asyncInputStream
;
nsCOMPtr
<
nsIAsyncOutputStream
>
asyncOutputStream
;
rv
=
NS_NewPipe2
(
getter_AddRefs
(
asyncInputStream
),
getter_AddRefs
(
asyncOutputStream
),
nonBlockingInput
,
nonBlockingOutput
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
mInputStream
=
asyncInputStream
;
mOutputStream
=
asyncOutputStream
;
#ifdef _IPC_MOZILLA_1_8
nsCOMPtr
<
nsIEventQueue
>
eventQ
;
if
(
!
mNoProxy
)
{
rv
=
NS_GetCurrentEventQ
(
getter_AddRefs
(
eventQ
));
NS_ENSURE_SUCCESS
(
rv
,
rv
);
}
#else
nsCOMPtr
<
nsIThread
>
eventQ
;
if
(
!
mNoProxy
)
{
rv
=
NS_GetCurrentThread
(
getter_AddRefs
(
eventQ
));
NS_ENSURE_SUCCESS
(
rv
,
rv
);
}
#endif
// Set input stream observer (using event queue, if need be)
rv
=
asyncInputStream
->
AsyncWait
((
nsIInputStreamCallback
*
)
this
,
0
,
0
,
eventQ
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
if
(
mNoProxy
)
{
pipeListener
=
this
;
}
else
{
nsCOMPtr
<
nsIPipeTransportListener
>
temListener
=
this
;
rv
=
proxyMgr
->
GetProxyForObject
(
NS_PROXY_TO_CURRENT_THREAD
,
//current thread
NS_GET_IID
(
nsIPipeTransportListener
),
temListener
,
NS_PROXY_SYNC
|
NS_PROXY_ALWAYS
,
getter_AddRefs
(
pipeListener
));
NS_ENSURE_SUCCESS
(
rv
,
rv
);
}
}
// Spin up a new thread to handle STDOUT polling
PRUint32
mimeHeadersMaxSize
=
mHeaderProcessor
?
mHeadersMaxSize
:
0
;
rv
=
mStdoutPoller
->
AsyncStart
(
mOutputStream
,
pipeListener
,
(
mNoProxy
!=
nsnull
),
mimeHeadersMaxSize
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
NS_ADDREF
(
*
_retval
=
this
);
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
WriteSync
(
const
char
*
buf
,
PRUint32
count
)
{
nsresult
rv
;
DEBUG_LOG
((
"nsPipeTransport::WriteSync: %d
\n
"
,
count
));
PRUint32
writeCount
;
rv
=
Write
(
buf
,
count
,
&
writeCount
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
if
(
writeCount
!=
count
)
return
NS_ERROR_FAILURE
;
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
CloseStdin
(
void
)
{
DEBUG_LOG
((
"nsPipeTransport::CloseStdin:
\n
"
));
// Close STDIN write pipe
// NOTE: This will prevent any kill string from being transmitted
if
(
mStdinWrite
!=
IPC_NULL_HANDLE
)
IPC_Close
(
mStdinWrite
);
mStdinWrite
=
IPC_NULL_HANDLE
;
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
WriteAsync
(
nsIInputStream
*
inStr
,
PRUint32
count
,
PRBool
closeAfterWrite
)
{
DEBUG_LOG
((
"nsPipeTransport::WriteAsync: %d
\n
"
,
count
));
if
(
mPipeState
!=
PIPE_OPEN
)
{
if
(
mPipeState
==
PIPE_NOT_YET_OPENED
)
return
NS_ERROR_NOT_INITIALIZED
;
if
(
mPipeState
==
PIPE_CLOSED
)
return
NS_BASE_STREAM_CLOSED
;
return
NS_ERROR_FAILURE
;
}
if
(
mStdinWrite
==
IPC_NULL_HANDLE
)
return
NS_BASE_STREAM_CLOSED
;
// Create stdin writing helper class
nsStdinWriter
*
stdinWriter
=
new
nsStdinWriter
();
if
(
!
stdinWriter
)
return
NS_ERROR_OUT_OF_MEMORY
;
nsCOMPtr
<
nsIPipeTransportWriter
>
pipeTransportWriter
;
pipeTransportWriter
=
stdinWriter
;
nsresult
rv
;
rv
=
pipeTransportWriter
->
WriteFromStream
(
inStr
,
count
,
mStdinWrite
,
closeAfterWrite
);
if
(
closeAfterWrite
)
{
mStdinWrite
=
IPC_NULL_HANDLE
;
// Different thread will close STDIN
}
return
rv
;
}
// Execute a command whose output is delimited by a prompt string
// ("Expect" style command execution)
NS_IMETHODIMP
nsPipeTransport
::
ExecPrompt
(
const
char
*
command
,
const
char
*
prompt
,
PRInt32
maxOutputLen
,
PRBool
clearPrev
,
char
*
*
_retval
)
{
nsresult
rv
;
DEBUG_LOG
((
"nsPipeTransport::ExecPrompt: command='%s', prompt='%s', maxOutputLen=%d, clearPrev=%p
\n
"
,
command
,
prompt
,
maxOutputLen
,
clearPrev
));
if
(
!
_retval
)
return
NS_ERROR_NULL_POINTER
;
if
(
!
mInputStream
)
{
nsCOMPtr
<
nsIInputStream
>
inputStream
;
rv
=
OpenInputStream
(
0
,
PRUint32
(
-1
),
0
,
getter_AddRefs
(
inputStream
));
NS_ENSURE_SUCCESS
(
rv
,
rv
);
}
if
(
mStdoutStream
!=
STREAM_SYNC_OPEN
)
return
NS_ERROR_NOT_AVAILABLE
;
if
(
clearPrev
)
{
// Clear any previous output data
char
buf
[
kCharMax
];
PRUint32
readCount
,
readMax
;
PRUint32
available
=
0
;
rv
=
mInputStream
->
Available
(
&
available
);
DEBUG_LOG
((
"nsPipeTransport::ExecPrompt: available=%d
\n
"
,
available
));
while
(
available
>
0
)
{
readMax
=
(
available
<
kCharMax
)
?
available
:
kCharMax
;
rv
=
mInputStream
->
Read
((
char
*
)
buf
,
readMax
,
&
readCount
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
if
(
readCount
<=
0
)
break
;
available
-=
readCount
;
}
// Clear Exec buffer as well
mExecBuf
.
Assign
(
""
);
}
PRUint32
commandLen
=
strlen
(
command
);
if
(
commandLen
>
0
)
{
// Transmit command
PRUint32
writeCount
;
rv
=
Write
(
command
,
commandLen
,
&
writeCount
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
}
PRInt32
returnCount
=
-1
;
PRUint32
promptLen
=
strlen
(
prompt
);
if
(
maxOutputLen
!=
0
)
{
char
buf
[
kCharMax
];
PRUint32
readCount
,
readMax
;
PRBool
matchWithoutNewline
=
(
promptLen
>
1
)
&&
(
prompt
[
0
]
==
'\n'
);
PRUint32
searchOffset
=
0
;
PRUint32
remainingCount
=
(
maxOutputLen
>
0
)
?
maxOutputLen
:
kCharMax
;
while
(
remainingCount
>
0
)
{
readMax
=
(
remainingCount
<
kCharMax
)
?
remainingCount
:
kCharMax
;
rv
=
mInputStream
->
Read
((
char
*
)
buf
,
kCharMax
,
&
readCount
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
if
(
readCount
<
0
)
return
NS_ERROR_FAILURE
;
if
(
readCount
==
0
)
break
;
// End-of-file
mExecBuf
.
Append
(
buf
,
readCount
);
if
(
matchWithoutNewline
&&
(
mExecBuf
.
Length
()
>=
promptLen
-1
))
{
if
(
PL_strncmp
(
mExecBuf
.
get
(),
prompt
+
1
,
promptLen
-1
)
==
0
)
{
// Prompt without newline matches output start; return null string
returnCount
=
0
;
mExecBuf
.
Cut
(
returnCount
,
promptLen
-1
);
break
;
}
matchWithoutNewline
=
PR_FALSE
;
}
if
((
promptLen
>
0
)
&&
(
mExecBuf
.
Length
()
>=
promptLen
))
{
returnCount
=
mExecBuf
.
Find
(
prompt
,
PR_FALSE
,
searchOffset
);
if
(
returnCount
>=
0
)
{
// Prompt found; delete it from line
if
(
prompt
[
0
]
==
'\n'
)
{
returnCount
++
;
// keep the newline
mExecBuf
.
Cut
(
returnCount
,
promptLen
-1
);
}
else
{
mExecBuf
.
Cut
(
returnCount
,
promptLen
);
}
break
;
}
// Increment search offset
searchOffset
=
mExecBuf
.
Length
()
-
promptLen
+
1
;
}
if
(
maxOutputLen
>
0
)
{
// Limited read
remainingCount
-=
readCount
;
}
else
{
// Unlimited read
remainingCount
=
kCharMax
;
}
}
if
(
returnCount
<
0
)
returnCount
=
mExecBuf
.
Length
();
// Return everything
}
// Duplicate output string and return it
nsCAutoString
outStr
(
""
);
if
(
returnCount
>
0
)
{
mExecBuf
.
Left
(
outStr
,
returnCount
);
mExecBuf
.
Cut
(
0
,
returnCount
);
}
*
_retval
=
PL_strdup
(
outStr
.
get
());
if
(
!*
_retval
)
return
NS_ERROR_OUT_OF_MEMORY
;
DEBUG_LOG
((
"nsPipeTransport::ExecPrompt: *_retval='%s'
\n
"
,
*
_retval
));
return
NS_OK
;
}
// Read the next line
NS_IMETHODIMP
nsPipeTransport
::
ReadLine
(
PRInt32
maxOutputLen
,
char
*
*
_retval
)
{
nsresult
rv
;
DEBUG_LOG
((
"nsPipeTransport::ReadLine: maxOutputLen=%d
\n
"
,
maxOutputLen
));
if
(
!
_retval
)
return
NS_ERROR_NULL_POINTER
;
if
(
!
mInputStream
)
{
nsCOMPtr
<
nsIInputStream
>
inputStream
;
rv
=
OpenInputStream
(
0
,
PRUint32
(
-1
),
0
,
getter_AddRefs
(
inputStream
));
NS_ENSURE_SUCCESS
(
rv
,
rv
);
}
if
(
mStdoutStream
!=
STREAM_SYNC_OPEN
)
return
NS_ERROR_NOT_AVAILABLE
;
PRInt32
returnCount
=
-1
;
if
(
maxOutputLen
!=
0
)
{
char
buf
[
kCharMax
];
PRUint32
readCount
,
readMax
;
PRUint32
remainingCount
=
(
maxOutputLen
>
0
)
?
maxOutputLen
:
kCharMax
;
if
(
mExecBuf
.
Length
()
>
0
)
{
mExecBuf
.
ReplaceSubstring
(
"
\r\n
"
,
"
\n
"
);
mExecBuf
.
ReplaceSubstring
(
"
\r
"
,
"
\n
"
);
returnCount
=
mExecBuf
.
Find
(
"
\n
"
,
PR_FALSE
,
0
);
DEBUG_LOG
((
"nsPipeTransport::ReadLine: returnCount=%d
\n
"
,
returnCount
));
}
if
(
returnCount
<
0
)
{
while
(
remainingCount
>
0
)
{
readMax
=
(
remainingCount
<
kCharMax
)
?
remainingCount
:
kCharMax
;
if
(
mStdoutPoller
)
{
// Fail if poller has been interrupted
PRBool
interrupted
;
rv
=
mStdoutPoller
->
IsInterrupted
(
&
interrupted
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
if
(
interrupted
)
return
NS_BASE_STREAM_CLOSED
;
}
rv
=
mInputStream
->
Read
((
char
*
)
buf
,
kCharMax
,
&
readCount
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
if
(
readCount
<
0
)
return
NS_ERROR_FAILURE
;
if
(
readCount
==
0
)
break
;
// End-of-file
mExecBuf
.
Append
(
buf
,
readCount
);
if
(
mExecBuf
.
Length
()
>=
1
)
{
mExecBuf
.
ReplaceSubstring
(
"
\r\n
"
,
"
\n
"
);
mExecBuf
.
ReplaceSubstring
(
"
\r
"
,
"
\n
"
);
returnCount
=
mExecBuf
.
Find
(
"
\n
"
,
PR_FALSE
,
0
);
if
(
returnCount
>=
0
)
{
break
;
}
}
if
(
maxOutputLen
>
0
)
{
// Limited read
remainingCount
-=
readCount
;
}
else
{
// Unlimited read
remainingCount
=
kCharMax
;
}
}
}
if
(
returnCount
<
0
)
returnCount
=
mExecBuf
.
Length
();
// Return everything
}
// Duplicate output string and return it
nsCAutoString
outStr
(
""
);
if
(
returnCount
>=
0
)
{
mExecBuf
.
Left
(
outStr
,
returnCount
);
mExecBuf
.
Cut
(
0
,
returnCount
+
1
);
}
*
_retval
=
PL_strdup
(
outStr
.
get
());
if
(
!*
_retval
)
return
NS_ERROR_OUT_OF_MEMORY
;
DEBUG_LOG
((
"nsPipeTransport::readLine: *_retval='%s'
\n
"
,
*
_retval
));
return
NS_OK
;
}
///////////////////////////////////////////////////////////////////////////////
// nsIRequest methods
///////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsPipeTransport
::
GetName
(
nsACString
&
result
)
{
DEBUG_LOG
((
"nsPipeTransport::GetName:
\n
"
));
if
(
!
mCommand
.
IsEmpty
())
{
result
=
mCommand
;
}
else
{
result
=
mExecutable
;
}
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
IsPending
(
PRBool
*
result
)
{
DEBUG_LOG
((
"nsPipeTransport::IsPending:
\n
"
));
*
result
=
(
mCancelStatus
==
NS_OK
);
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
GetStatus
(
nsresult
*
status
)
{
DEBUG_LOG
((
"nsPipeTransport::GetStatus:
\n
"
));
*
status
=
mCancelStatus
;
return
NS_OK
;
}
// NOTE: We assume that OnStopRequest should not be called if
// request is canceled. This may be wrong!
NS_IMETHODIMP
nsPipeTransport
::
Cancel
(
nsresult
status
)
{
#ifdef FORCE_PR_LOG
nsresult
rv
;
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsPipeTransport::Cancel, myThread=%p, status=%p
\n
"
,
myThread
.
get
(),
status
));
#endif
// Need a non-zero status code to cancel
if
(
status
==
NS_OK
)
return
NS_ERROR_FAILURE
;
if
(
mCancelStatus
==
NS_OK
)
mCancelStatus
=
status
;
StopRequest
(
status
);
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
Suspend
(
void
)
{
DEBUG_LOG
((
"nsPipeTransport::Suspend:
\n
"
));
return
NS_ERROR_NOT_IMPLEMENTED
;
}
NS_IMETHODIMP
nsPipeTransport
::
Resume
(
void
)
{
DEBUG_LOG
((
"nsPipeTransport::Resume:
\n
"
));
return
NS_ERROR_NOT_IMPLEMENTED
;
}
NS_IMETHODIMP
nsPipeTransport
::
GetLoadGroup
(
nsILoadGroup
*
*
aLoadGroup
)
{
DEBUG_LOG
((
"nsPipeTransport::GetLoadGroup:
\n
"
));
NS_IF_ADDREF
(
*
aLoadGroup
=
mLoadGroup
);
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
SetLoadGroup
(
nsILoadGroup
*
aLoadGroup
)
{
DEBUG_LOG
((
"nsPipeTransport::SetLoadGroup:
\n
"
));
mLoadGroup
=
aLoadGroup
;
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
GetLoadFlags
(
nsLoadFlags
*
aLoadFlags
)
{
DEBUG_LOG
((
"nsPipeTransport::GetLoadFlags:
\n
"
));
*
aLoadFlags
=
mLoadFlags
;
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
SetLoadFlags
(
nsLoadFlags
aLoadFlags
)
{
DEBUG_LOG
((
"nsPipeTransport::SetLoadFlags:
\n
"
));
mLoadFlags
=
aLoadFlags
;
return
NS_OK
;
}
///////////////////////////////////////////////////////////////////////////////
// nsIOutputStream methods
///////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsPipeTransport
::
Close
(
void
)
{
DEBUG_LOG
((
"nsPipeTransport::Close:
\n
"
));
return
CloseStdin
();
}
NS_IMETHODIMP
nsPipeTransport
::
Write
(
const
char
*
buf
,
PRUint32
count
,
PRUint32
*
_retval
)
{
DEBUG_LOG
((
"nsPipeTransport::Write: %d
\n
"
,
count
));
if
(
!
_retval
)
return
NS_ERROR_NULL_POINTER
;
*
_retval
=
0
;
if
(
mPipeState
!=
PIPE_OPEN
)
{
if
(
mPipeState
==
PIPE_NOT_YET_OPENED
)
return
NS_ERROR_NOT_INITIALIZED
;
if
(
mPipeState
==
PIPE_CLOSED
)
return
NS_BASE_STREAM_CLOSED
;
return
NS_ERROR_FAILURE
;
}
if
(
mStdinWrite
==
IPC_NULL_HANDLE
)
return
NS_BASE_STREAM_CLOSED
;
if
(
count
==
0
)
{
return
NS_OK
;
}
// Write data
PRInt32
writeCount
=
0
;
writeCount
=
IPC_Write
(
mStdinWrite
,
buf
,
count
);
if
(
writeCount
!=
(
PRInt32
)
count
)
{
PRErrorCode
errCode
=
IPC_GetError
();
DEBUG_LOG
((
"nsPipeTransport::Write: Error in writing to fd %p (count=%d, writeCount=%d, error code=%d)
\n
"
,
mStdinWrite
,
count
,
writeCount
,
errCode
));
}
if
(
writeCount
<
0
)
return
NS_ERROR_FAILURE
;
*
_retval
=
writeCount
;
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
Flush
(
void
)
{
// Do nothing
DEBUG_LOG
((
"nsPipeTransport::Flush:
\n
"
));
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
WriteFrom
(
nsIInputStream
*
inStr
,
PRUint32
count
,
PRUint32
*
_retval
)
{
return
NS_ERROR_NOT_IMPLEMENTED
;
}
NS_IMETHODIMP
nsPipeTransport
::
WriteSegments
(
nsReadSegmentFun
reader
,
void
*
closure
,
PRUint32
count
,
PRUint32
*
_retval
)
{
return
NS_ERROR_NOT_IMPLEMENTED
;
}
NS_IMETHODIMP
nsPipeTransport
::
IsNonBlocking
(
PRBool
*
result
)
{
DEBUG_LOG
((
"nsPipeTransport::IsNonBlocking:
\n
"
));
*
result
=
PR_TRUE
;
return
NS_OK
;
}
///////////////////////////////////////////////////////////////////////////////
// nsIRequestObserver methods
///////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsPipeTransport
::
OnStartRequest
(
nsIRequest
*
aRequest
,
nsISupports
*
aContext
)
{
DEBUG_LOG
((
"nsPipeTransport::OnStartRequest:
\n
"
));
return
NS_OK
;
}
NS_IMETHODIMP
nsPipeTransport
::
OnStopRequest
(
nsIRequest
*
aRequest
,
nsISupports
*
aContext
,
nsresult
aStatus
)
{
DEBUG_LOG
((
"nsPipeTransport::OnStopRequest:
\n
"
));
// Close STDIN, if open
CloseStdin
();
return
NS_OK
;
}
///////////////////////////////////////////////////////////////////////////////
// nsIStreamListener method
///////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsPipeTransport
::
OnDataAvailable
(
nsIRequest
*
aRequest
,
nsISupports
*
aContext
,
nsIInputStream
*
aInputStream
,
PRUint32
aSourceOffset
,
PRUint32
aLength
)
{
nsresult
rv
=
NS_OK
;
DEBUG_LOG
((
"nsPipeTransport::OnDataAVailable: %d
\n
"
,
aLength
));
char
buf
[
kCharMax
];
PRUint32
readCount
,
readMax
;
while
(
aLength
>
0
)
{
readMax
=
(
aLength
<
kCharMax
)
?
aLength
:
kCharMax
;
rv
=
aInputStream
->
Read
((
char
*
)
buf
,
readMax
,
&
readCount
);
if
(
NS_FAILED
(
rv
)){
DEBUG_LOG
((
"nsPipeTransport::OnDataAvailable: Error in reading from input stream, %p
\n
"
,
rv
));
return
rv
;
}
if
(
readCount
<=
0
)
return
NS_OK
;
rv
=
WriteSync
(
buf
,
readCount
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
aLength
-=
readCount
;
}
return
NS_OK
;
}
///////////////////////////////////////////////////////////////////////////////
// nsIInputStreamCallback methods:
// (Should be invoked in the thread creating nsIPipeTransport object,
// unless mNoProxy is true.)
///////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsPipeTransport
::
OnInputStreamReady
(
nsIAsyncInputStream
*
inStr
)
{
nsresult
rv
;
#ifdef FORCE_PR_LOG
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsPipeTransport::OnInputStreamReady, myThread=%p
\n
"
,
myThread
.
get
()));
#endif
if
(
mListener
)
{
if
(
!
mInputStream
)
return
NS_ERROR_NOT_INITIALIZED
;
PRUint32
available
;
rv
=
mInputStream
->
Available
(
&
available
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
DEBUG_LOG
((
"nsPipeTransport::OnInputStreamReady: available=%d
\n
"
,
available
));
rv
=
mListener
->
OnDataAvailable
((
nsIRequest
*
)
this
,
mContext
,
mInputStream
,
0
,
available
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
#ifdef _IPC_MOZILLA_1_8
nsCOMPtr
<
nsIEventQueue
>
eventQ
;
if
(
!
mNoProxy
)
{
rv
=
NS_GetCurrentEventQ
(
getter_AddRefs
(
eventQ
));
if
(
NS_FAILED
(
rv
))
return
rv
;
}
#else
// Mozilla >= 1.9
nsCOMPtr
<
nsIThread
>
eventQ
;
if
(
!
mNoProxy
)
{
rv
=
NS_GetCurrentThread
(
getter_AddRefs
(
eventQ
));
NS_ENSURE_SUCCESS
(
rv
,
rv
);
}
#endif
// Re-set input stream observer (using event queue, if need be)
rv
=
inStr
->
AsyncWait
((
nsIInputStreamCallback
*
)
this
,
0
,
0
,
eventQ
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
return
rv
;
}
return
NS_OK
;
}
///////////////////////////////////////////////////////////////////////////////
// nsIOutputStreamCallback methods:
// (Should be invoked in the thread creating nsIPipeTransport object)
///////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsPipeTransport
::
OnOutputStreamReady
(
nsIAsyncOutputStream
*
outStr
)
{
nsresult
rv
;
#ifdef FORCE_PR_LOG
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsPipeTransport::OnOutputStreamReady, myThread=%p
\n
"
,
myThread
.
get
()));
#endif
return
NS_OK
;
}
///////////////////////////////////////////////////////////////////////////////
// nsIPipeTransportHeaders methods:
// (Should be invoked in the thread creating nsIPipeTransport object)
///////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsPipeTransport
::
ParseMimeHeaders
(
const
char
*
mimeHeaders
,
PRUint32
count
,
PRInt32
*
retval
)
{
#ifdef FORCE_PR_LOG
nsresult
rv
;
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsPipeTransport::ParseMimeHeaders, myThread=%p
\n
"
,
myThread
.
get
()));
#endif
if
(
mHeaderProcessor
)
return
mHeaderProcessor
->
ParseMimeHeaders
(
mimeHeaders
,
count
,
retval
);
return
NS_ERROR_FAILURE
;
}
///////////////////////////////////////////////////////////////////////////////
// nsIPipeTransportListener methods:
// (Should be invoked in the thread creating nsIPipeTransport object,
// unless mNoProxy is true.)
///////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsPipeTransport
::
StartRequest
()
{
nsresult
rv
;
#ifdef FORCE_PR_LOG
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsPipeTransport::StartRequest, myThread=%p
\n
"
,
myThread
.
get
()));
#endif
if
(
mListener
)
{
// Starting processing of async output
rv
=
mListener
->
OnStartRequest
((
nsIRequest
*
)
this
,
mContext
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
mStartedRequest
=
PR_TRUE
;
}
return
NS_OK
;
}
// aStatus == NS_OK for normal termination of request (called by StdoutPoller)
// aStatus != NS_OK for cancellation of request (called by Cancel)
// When invoking this method from the polling thread via a proxy, ensure that
// the UI thread is not blocked for synchronous read by closing the pipe.
NS_IMETHODIMP
nsPipeTransport
::
StopRequest
(
nsresult
aStatus
)
{
#ifdef FORCE_PR_LOG
nsresult
rv
;
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsPipeTransport::StopRequest, myThread=%p, status=%p
\n
"
,
myThread
.
get
(),
aStatus
));
#endif
// NOTE: Should OnStopRequest be called if request is being cancelled?
// We are assuming here that it does not need to be.
// If this assumption is wrong, then there is a problem when saving
// downloaded files using xpfe/components/xfer/src/nsStreamXferOp:
// Canceling a download in progress calls nsStreamXferOp::OnStopRequest
// followed by nsStreamXferOp::Stop, causing the BufferedOutputStream
// to be closed each time, and causing nsBufferedOutputStream::Flush
// to segfault on the second close.
if
(
mStartedRequest
&&
mListener
&&
(
mCancelStatus
==
NS_OK
)
&&
(
aStatus
==
NS_OK
))
{
mStartedRequest
=
PR_FALSE
;
mCancelStatus
=
NS_BINDING_ABORTED
;
mListener
->
OnStopRequest
(
(
nsIRequest
*
)
this
,
mContext
,
aStatus
);
}
if
(
!
mNoProxy
)
Finalize
(
PR_FALSE
);
return
NS_OK
;
}
///////////////////////////////////////////////////////////////////////////////
// nsStdoutPoller implementation
// nsISupports implementation
NS_IMPL_THREADSAFE_ISUPPORTS2
(
nsStdoutPoller
,
nsIPipeTransportPoller
,
nsIRunnable
)
// nsStdoutPoller implementation
nsStdoutPoller
::
nsStdoutPoller
()
:
mFinalized
(
PR_FALSE
),
mInterrupted
(
PR_FALSE
),
mLoggingEnabled
(
PR_FALSE
),
mJoinableThread
(
PR_FALSE
),
mHeadersBuf
(
""
),
mHeadersBufSize
(
0
),
mHeadersLastNewline
(
0
),
mRequestStarted
(
PR_FALSE
),
mContentLength
(
-1
),
mStdoutRead
(
IPC_NULL_HANDLE
),
mStderrRead
(
IPC_NULL_HANDLE
),
mPollCount
(
0
),
mPollableEvent
(
nsnull
),
mPollFD
(
nsnull
)
{
NS_INIT_ISUPPORTS
();
#ifdef FORCE_PR_LOG
nsresult
rv
;
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsStdoutPoller:: <<<<<<<<< CTOR(%p): myThread=%p
\n
"
,
this
,
myThread
.
get
()));
#endif
mLock
=
PR_NewLock
();
}
nsStdoutPoller
::~
nsStdoutPoller
()
{
nsresult
rv
;
#ifdef FORCE_PR_LOG
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsStdoutPoller:: >>>>>>>>> DTOR(%p): myThread=%p
\n
"
,
this
,
myThread
.
get
()));
#endif
Finalize
(
PR_TRUE
);
// Free non-owning references/resources
if
(
mPollableEvent
)
PR_DestroyPollableEvent
(
mPollableEvent
);
if
(
mStdoutRead
!=
IPC_NULL_HANDLE
)
{
IPC_Close
(
mStdoutRead
);
mStdoutRead
=
IPC_NULL_HANDLE
;
}
if
(
mStderrRead
!=
IPC_NULL_HANDLE
)
{
IPC_Close
(
mStderrRead
);
mStderrRead
=
IPC_NULL_HANDLE
;
}
if
(
mPollFD
)
{
PR_Free
(
mPollFD
);
mPollFD
=
nsnull
;
}
// Clear header buffer
mHeadersBuf
.
Assign
(
""
);
PR_DestroyLock
(
mLock
);
}
///////////////////////////////////////////////////////////////////////////////
// nsStdoutPoller methods:
///////////////////////////////////////////////////////////////////////////////
nsresult
nsStdoutPoller
::
Init
(
IPCFileDesc
*
aStdoutRead
,
IPCFileDesc
*
aStderrRead
,
PRIntervalTime
aTimeoutInterval
,
nsIPipeListener
*
aConsole
)
{
// Should be invoked in the thread creating nsIPipeTransport object
// Should be closed only in the polling thread
mStdoutRead
=
aStdoutRead
;
mStderrRead
=
aStderrRead
;
mTimeoutInterval
=
aTimeoutInterval
;
mConsole
=
aConsole
;
// Initialize polling structure
mPollCount
=
1
;
#ifndef XP_WIN
// Note: No polling for Win32
#ifdef USE_POLLING
// Use pollable event to interrupt thread
mPollableEvent
=
PR_NewPollableEvent
();
mPollCount
++
;
#endif
#endif
if
(
mStderrRead
!=
IPC_NULL_HANDLE
)
mPollCount
++
;
mPollFD
=
(
PRPollDesc
*
)
PR_Malloc
(
sizeof
(
PRPollDesc
)
*
mPollCount
);
if
(
!
mPollFD
)
return
NS_ERROR_OUT_OF_MEMORY
;
memset
(
mPollFD
,
0
,
sizeof
(
PRPollDesc
)
*
mPollCount
);
#ifndef XP_WIN
// Note: No polling for Win32
if
(
mPollableEvent
)
{
// Read pollable event before all others
mPollFD
[
0
].
fd
=
mPollableEvent
;
mPollFD
[
0
].
in_flags
=
PR_POLL_READ
;
mPollFD
[
0
].
out_flags
=
0
;
}
if
(
mStderrRead
!=
IPC_NULL_HANDLE
)
{
// Read STDERR before STDOUT (is this always a good idea?)
mPollFD
[
mPollCount
-2
].
fd
=
mStderrRead
;
mPollFD
[
mPollCount
-2
].
in_flags
=
PR_POLL_READ
|
PR_POLL_EXCEPT
;
mPollFD
[
mPollCount
-2
].
out_flags
=
0
;
}
// Read STDOUT
mPollFD
[
mPollCount
-1
].
fd
=
mStdoutRead
;
mPollFD
[
mPollCount
-1
].
in_flags
=
PR_POLL_READ
|
PR_POLL_EXCEPT
;
mPollFD
[
mPollCount
-1
].
out_flags
=
0
;
#endif
return
NS_OK
;
}
NS_IMETHODIMP
nsStdoutPoller
::
AsyncStart
(
nsIOutputStream
*
aOutputStream
,
nsIPipeTransportListener
*
aProxyPipeListener
,
PRBool
joinable
,
PRUint32
aMimeHeadersMaxSize
)
{
// Should be invoked in the thread creating nsIPipeTransport object
nsresult
rv
=
NS_OK
;
DEBUG_LOG
((
"nsStdoutPoller::AsyncStart: %d
\n
"
,
aMimeHeadersMaxSize
));
mJoinableThread
=
joinable
;
mHeadersBufSize
=
aMimeHeadersMaxSize
;
mOutputStream
=
aOutputStream
;
mProxyPipeListener
=
aProxyPipeListener
;
// Spin up a new thread to handle STDOUT polling (non-joinable)
nsCOMPtr
<
nsIThread
>
stdoutThread
;
#ifdef _IPC_MOZILLA_1_8
PRThreadState
threadState
=
mJoinableThread
?
PR_JOINABLE_THREAD
:
PR_UNJOINABLE_THREAD
;
rv
=
NS_NewThread
(
getter_AddRefs
(
stdoutThread
),
(
nsIRunnable
*
)
this
,
0
,
threadState
);
#else
rv
=
NS_NewThread
(
getter_AddRefs
(
stdoutThread
),
(
nsIRunnable
*
)
this
);
#endif
NS_ENSURE_SUCCESS
(
rv
,
rv
);
mStdoutThread
=
stdoutThread
;
return
NS_OK
;
}
nsresult
nsStdoutPoller
::
Finalize
(
PRBool
destructor
)
{
nsresult
rv
=
NS_OK
;
if
(
mFinalized
)
return
NS_OK
;
mFinalized
=
PR_TRUE
;
{
nsAutoLock
lock
(
mLock
);
// Set thread interrupted flag to avoid race conditions
// when freeing mStdoutThread/mPollableEvent
mInterrupted
=
PR_TRUE
;
}
DEBUG_LOG
((
"nsStdoutPoller::Finalize:
\n
"
));
nsCOMPtr
<
nsIPipeTransportPoller
>
self
;
if
(
!
destructor
)
{
// Hold a reference to ourselves to prevent our DTOR from being called
// while finalizing. Automatically released upon returning.
self
=
this
;
}
// Release refs to input arguments
mOutputStream
=
nsnull
;
mProxyPipeListener
=
nsnull
;
mConsole
=
nsnull
;
// Release ref to owned thread
mStdoutThread
=
nsnull
;
return
rv
;
}
NS_IMETHODIMP
nsStdoutPoller
::
GetLoggingEnabled
(
PRBool
*
aLoggingEnabled
)
{
nsAutoLock
lock
(
mLock
);
DEBUG_LOG
((
"nsStdoutPoller::GetLoggingEnabled:
\n
"
));
*
aLoggingEnabled
=
mLoggingEnabled
;
return
NS_OK
;
}
NS_IMETHODIMP
nsStdoutPoller
::
SetLoggingEnabled
(
PRBool
aLoggingEnabled
)
{
nsAutoLock
lock
(
mLock
);
DEBUG_LOG
((
"nsStdoutPoller::SetLoggingEnabled: %d
\n
"
,
aLoggingEnabled
));
mLoggingEnabled
=
aLoggingEnabled
;
return
NS_OK
;
}
NS_IMETHODIMP
nsStdoutPoller
::
IsInterrupted
(
PRBool
*
interrupted
)
{
nsAutoLock
lock
(
mLock
);
#ifdef FORCE_PR_LOG
nsresult
rv
;
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsStdoutPoller::IsInterrupted: %p, myThread=%p
\n
"
,
mInterrupted
,
myThread
.
get
()));
#endif
if
(
!
interrupted
)
return
NS_ERROR_NULL_POINTER
;
*
interrupted
=
mInterrupted
;
return
NS_OK
;
}
/** Joins polling thread (blocks until thread terminates)
*/
NS_IMETHODIMP
nsStdoutPoller
::
Join
()
{
nsresult
rv
;
if
(
!
mJoinableThread
)
return
NS_ERROR_FAILURE
;
if
(
!
mStdoutThread
)
return
NS_OK
;
#ifdef _IPC_MOZILLA_1_8
rv
=
mStdoutThread
->
Join
();
#else
rv
=
mStdoutThread
->
Shutdown
();
#endif
mStdoutThread
=
nsnull
;
return
rv
;
}
/** Interrupts polling thread. Maybe called from any thread.
* Once interrupted, thread always remains interrupted.
*/
NS_IMETHODIMP
nsStdoutPoller
::
Interrupt
(
PRBool
*
alreadyInterrupted
)
{
{
nsAutoLock
lock
(
mLock
);
if
(
!
alreadyInterrupted
)
*
alreadyInterrupted
=
mInterrupted
;
if
(
mInterrupted
)
return
NS_OK
;
mInterrupted
=
PR_TRUE
;
}
nsresult
rv
;
#ifdef FORCE_PR_LOG
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsStdoutPoller::Interrupt: myThread=%p
\n
"
,
myThread
.
get
()));
#endif
if
(
mPollableEvent
)
{
// Interrupt thread
PRStatus
status
;
// Set pollable event to wake up thread
status
=
PR_SetPollableEvent
(
mPollableEvent
);
if
(
status
!=
PR_SUCCESS
)
return
NS_ERROR_FAILURE
;
}
else
if
(
mStdoutThread
)
{
// Interrupt thread; may fail
#ifdef _IPC_MOZILLA_1_8
mStdoutThread
->
Interrupt
();
#endif
}
return
NS_OK
;
}
nsresult
nsStdoutPoller
::
GetPolledFD
(
PRFileDesc
*&
aFileDesc
)
{
nsresult
rv
;
PRInt32
pollRetVal
;
aFileDesc
=
nsnull
;
if
(
mPollCount
==
1
)
{
// Read from STDOUT (blocking)
DEBUG_LOG
((
"nsStdoutPoller::GetPolledFD: Blocked read from STDOUT
\n
"
));
aFileDesc
=
mPollFD
[
0
].
fd
;
return
NS_OK
;
}
DEBUG_LOG
((
"nsStdoutPoller::GetPolledFD: ***PR_Poll 0x%p,%d,%d
\n
"
,
mPollFD
,
mPollCount
,
mTimeoutInterval
));
pollRetVal
=
PR_Poll
(
mPollFD
,
mPollCount
,
mTimeoutInterval
);
DEBUG_LOG
((
"nsStdoutPoller::GetPolledFD: PR_Poll returned value = %d
\n
"
,
pollRetVal
));
if
(
pollRetVal
<
0
)
{
// PR_Poll error exit
PRErrorCode
errCode
=
PR_GetError
();
if
(
errCode
==
PR_PENDING_INTERRUPT_ERROR
)
{
// Note: Interrupted; need to close all FDs
#ifdef FORCE_PR_LOG
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsStdoutPoller::GetPolledFD: Interrupted (NSPR) while polling, myThread=0x%p
\n
"
,
myThread
.
get
()));
#endif
}
ERROR_LOG
((
"nsStdoutPoller::GetPolledFD: PR_Poll error exit
\n
"
));
return
NS_ERROR_FAILURE
;
}
if
(
pollRetVal
==
0
)
{
// PR_Poll timed out
ERROR_LOG
((
"nsStdoutPoller::GetPolledFD: PR_Poll timed out
\n
"
));
return
NS_ERROR_FAILURE
;
}
// PR_Poll input available (pollRetVal > 0); process it
PRBool
errFlags
=
PR_FALSE
;
for
(
int
j
=
0
;
j
<
mPollCount
;
j
++
)
{
DEBUG_LOG
((
"nsStdoutPoller::GetPolledFD: mPollFD[%d].out_flags=0x%p
\n
"
,
j
,
mPollFD
[
j
].
out_flags
));
if
(
mPollFD
[
j
].
out_flags
)
{
// Out flags set for FD
if
(
mPollFD
[
j
].
fd
==
mPollableEvent
)
{
// Pollable event; return with null FD and normal status
DEBUG_LOG
((
"nsStdoutPoller::GetPolledFD: mPollFD[%d]: Pollable event
\n
"
,
j
));
PR_WaitForPollableEvent
(
mPollableEvent
);
return
NS_OK
;
}
else
if
(
mPollFD
[
j
].
out_flags
&
POLL_READ_FLAGS
)
{
// Data available for reading from file descriptor (normal return)
aFileDesc
=
mPollFD
[
j
].
fd
;
DEBUG_LOG
((
"nsStdoutPoller::GetPolledFD: mPollFD[%d]: Ready for reading
\n
"
,
j
));
return
NS_OK
;
}
else
{
// Exception/error condition; check next FD
#ifdef FORCE_PR_LOG
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
WARNING_LOG
((
"nsStdoutPoller::GetPolledFD: mPollFD[%d]: Exception/error 0x%x, myThread=0x%x
\n
"
,
j
,
mPollFD
[
j
].
out_flags
,
myThread
.
get
()));
#endif
errFlags
=
PR_TRUE
;
}
}
}
// Return with null FD and normal status
return
NS_OK
;
}
nsresult
nsStdoutPoller
::
HeaderSearch
(
const
char
*
buf
,
PRUint32
count
,
PRUint32
*
headerOffset
)
{
nsresult
rv
=
NS_OK
;
*
headerOffset
=
0
;
if
(
!
mProxyPipeListener
)
return
NS_OK
;
if
(
mRequestStarted
)
return
NS_OK
;
DEBUG_LOG
((
"nsStdoutPoller::HeaderSearch: count=%d, bufSize=%d
\n
"
,
count
,
mHeadersBufSize
));
PRBool
headerFound
=
PR_FALSE
;
PRBool
startRequest
=
PR_FALSE
;
if
(
mHeadersBufSize
<=
0
)
{
// Not looking for MIME headers; start request
startRequest
=
PR_TRUE
;
}
else
{
PRUint32
headersAvailable
=
mHeadersBufSize
-
mHeadersBuf
.
Length
();
NS_ASSERTION
(
headersAvailable
>
0
,
"headersAvailable <= 0"
);
PRBool
lastSegment
=
(
headersAvailable
<=
count
);
PRUint32
offset
=
0
;
if
(
!
buf
||
(
count
<=
0
))
{
// Error/end-of-file; end headers search unsuccessfully
startRequest
=
PR_TRUE
;
}
else
{
PRUint32
scanLen
=
lastSegment
?
headersAvailable
:
count
;
if
(
mHeadersBuf
.
Length
()
==
0
)
mHeadersLastNewline
=
1
;
offset
=
scanLen
;
PRUint32
j
=
0
;
while
(
j
<
scanLen
)
{
if
(
mHeadersLastNewline
>
0
)
{
if
((
mHeadersLastNewline
==
1
)
&&
(
buf
[
j
]
==
'\r'
))
{
// Skip over a single CR following a newline
j
++
;
mHeadersLastNewline
++
;
if
(
j
>=
scanLen
)
break
;
}
if
(
buf
[
j
]
==
'\n'
)
{
// End-of-headers found
offset
=
j
+
1
;
headerFound
=
PR_TRUE
;
break
;
}
}
if
(
buf
[
j
]
==
'\n'
)
mHeadersLastNewline
=
1
;
else
mHeadersLastNewline
=
0
;
j
++
;
}
DEBUG_LOG
((
"nsStdoutPoller::HeaderSearch: headerFound=%d, offset=%d
\n
"
,
headerFound
,
offset
));
// Copy header portion to header buffer
mHeadersBuf
.
Append
(
buf
,
offset
);
if
(
lastSegment
)
startRequest
=
PR_TRUE
;
}
*
headerOffset
=
offset
;
}
if
(
headerFound
||
startRequest
)
{
PRBool
skipHeaders
=
PR_FALSE
;
if
(
mHeadersBufSize
>
0
)
{
// Try to parse headers
PRInt32
contentLength
=
-1
;
rv
=
mProxyPipeListener
->
ParseMimeHeaders
(
mHeadersBuf
.
get
(),
mHeadersBuf
.
Length
(),
&
contentLength
);
if
(
NS_SUCCEEDED
(
rv
))
{
// Headers parsed successfully
mContentLength
=
contentLength
;
skipHeaders
=
PR_TRUE
;
}
}
// Call pipe listener to trigger OnStartRequest
mRequestStarted
=
PR_TRUE
;
DEBUG_LOG
((
"nsStdoutPoller::HeaderSearch: Calling mProxyPipeListener->StartRequest
\n
"
));
rv
=
mProxyPipeListener
->
StartRequest
();
NS_ENSURE_SUCCESS
(
rv
,
rv
);
if
(
!
skipHeaders
&&
(
mHeadersBufSize
>
0
))
{
// Header search/parse failed; flush buffered data
if
(
mOutputStream
)
{
PRUint32
writeCount
=
0
;
rv
=
mOutputStream
->
Write
(
mHeadersBuf
.
get
(),
mHeadersBuf
.
Length
(),
&
writeCount
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
}
}
// Clear header buffer
mHeadersBuf
.
Assign
(
""
);
}
return
NS_OK
;
}
///////////////////////////////////////////////////////////////////////////////
// nsIRunnable methods:
// (runs as a new thread)
///////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsStdoutPoller
::
Run
()
{
nsresult
rv
=
NS_OK
;
#ifdef FORCE_PR_LOG
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsStdoutPoller::Run: myThread=%p
\n
"
,
myThread
.
get
()));
#endif
if
(
!
mPollCount
)
return
NS_ERROR_NOT_INITIALIZED
;
// Polling loop
while
(
1
)
{
IPCFileDesc
*
readHandle
;
#ifdef XP_WIN
// No polling for Win32
readHandle
=
mStdoutRead
;
#else
// Poll to determine file descriptor to read from
rv
=
GetPolledFD
(
readHandle
);
NS_ENSURE_SUCCESS
(
rv
,
rv
);
if
(
!
readHandle
)
{
// Null handle means end-of-file/error
DEBUG_LOG
((
"nsStdoutPoller::Run: Terminating polling
\n
"
));
break
;
}
#endif
char
buf
[
kCharMax
];
PRInt32
readCount
;
// Read data from handle (blocking)
readCount
=
IPC_Read
(
readHandle
,
(
char
*
)
buf
,
kCharMax
);
DEBUG_LOG
((
"nsStdoutPoller::Run: Read %d chars
\n
"
,
readCount
));
if
(
readCount
<
0
)
{
PRErrorCode
errCode
=
IPC_GetError
();
if
(
errCode
==
PR_PENDING_INTERRUPT_ERROR
)
{
// Note: Interrupted; need to close all FDs; exit loop normally
DEBUG_LOG
((
"nsStdoutPoller::Run: Interrupted (NSPR) while reading
\n
"
));
rv
=
NS_OK
;
break
;
}
else
{
// Exit loop with error status
WARNING_LOG
((
"nsStdoutPoller::Run: Error in reading from fd %p (readCount=%d, error code=%d)
\n
"
,
readHandle
,
readCount
,
(
int
)
errCode
));
rv
=
NS_ERROR_FAILURE
;
break
;
}
}
if
(
readCount
==
0
)
{
// End of file; exit loop with normal status
DEBUG_LOG
((
"nsStdoutPoller::Run: End-of-file in reading
\n
"
));
if
(
mConsole
)
{
// Try to join console (i.e. wait for console thread to terminate)
DEBUG_LOG
((
"nsStdoutPoller::Run: ***** Joining console *****
\n
"
));
mConsole
->
Join
();
}
rv
=
NS_OK
;
break
;
}
PRBool
interrupted
;
rv
=
IsInterrupted
(
&
interrupted
);
if
(
NS_FAILED
(
rv
))
break
;
if
(
interrupted
)
{
// Thread termination signal received; discard read data and return
WARNING_LOG
((
"nsStdoutPoller::Run: Thread interrupted; data discarded
\n
"
));
rv
=
NS_OK
;
break
;
}
if
(
mLoggingEnabled
&&
mConsole
)
{
// Log data read to console
mConsole
->
WriteBuf
(
buf
,
readCount
);
}
PRUint32
headerOffset
=
0
;
rv
=
HeaderSearch
(
buf
,
readCount
,
&
headerOffset
);
if
(
NS_FAILED
(
rv
))
break
;
if
(
readCount
>
(
int
)
headerOffset
)
{
// Write non-header portion to buffer output stream
if
(
mOutputStream
)
{
PRUint32
writeCount
=
0
;
rv
=
mOutputStream
->
Write
((
char
*
)
buf
+
headerOffset
,
readCount
-
headerOffset
,
&
writeCount
);
if
(
NS_FAILED
(
rv
))
break
;
DEBUG_LOG
((
"nsStdoutPoller::Run: writeCount=%d
\n
"
,
writeCount
));
NS_ASSERTION
(
writeCount
>
0
,
"writeCount <= 0"
);
}
}
}
// Clear any NSPR interrupt
PR_ClearInterrupt
();
// Flush any MIME header stuff
PRUint32
dummy
;
HeaderSearch
(
nsnull
,
0
,
&
dummy
);
PRBool
alreadyInterrupted
=
PR_FALSE
;
Interrupt
(
&
alreadyInterrupted
);
if
(
mOutputStream
)
{
// Close output stream (unblocks synchronous reads)
mOutputStream
->
Close
();
}
if
(
mProxyPipeListener
&&
mRequestStarted
)
{
// Call pipe listener to trigger OnStopRequest,
// usually on reaching end-of-file or end-of-content on Stdout,
// which should be considered as normal exit.
// Occasionally this might be due to a read error on Stdout.
DEBUG_LOG
((
"nsStdoutPoller::Run: Calling mProxyPipeListener->StopRequest
\n
"
));
mProxyPipeListener
->
StopRequest
(
NS_OK
);
mRequestStarted
=
PR_FALSE
;
}
// Kill process, and release owning references
Finalize
(
PR_FALSE
);
DEBUG_LOG
((
"nsStdoutPoller::Run: exiting, rv=%p
\n
"
,
rv
));
return
rv
;
}
///////////////////////////////////////////////////////////////////////////////
// nsStdinWriter implementation
// nsISupports implementation
NS_IMPL_THREADSAFE_ISUPPORTS2
(
nsStdinWriter
,
nsIPipeTransportWriter
,
nsIRunnable
)
// nsStdinWriter implementation
nsStdinWriter
::
nsStdinWriter
()
:
mInputStream
(
nsnull
),
mCount
(
0
),
mPipe
(
IPC_NULL_HANDLE
),
mCloseAfterWrite
(
PR_FALSE
)
{
NS_INIT_ISUPPORTS
();
#ifdef FORCE_PR_LOG
nsresult
rv
;
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsStdinWriter:: <<<<<<<<< CTOR(%p): myThread=%p
\n
"
,
this
,
myThread
.
get
()));
#endif
}
nsStdinWriter
::~
nsStdinWriter
()
{
nsresult
rv
;
#ifdef FORCE_PR_LOG
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsStdinWriter:: >>>>>>>>> DTOR(%p): myThread=%p
\n
"
,
this
,
myThread
.
get
()));
#endif
if
(
mPipe
!=
IPC_NULL_HANDLE
)
{
IPC_Close
(
mPipe
);
mPipe
=
IPC_NULL_HANDLE
;
}
// Release ref to input stream
mInputStream
=
nsnull
;
}
///////////////////////////////////////////////////////////////////////////////
// nsStdinWriter methods:
///////////////////////////////////////////////////////////////////////////////
nsresult
nsStdinWriter
::
WriteFromStream
(
nsIInputStream
*
inStr
,
PRUint32
count
,
IPCFileDesc
*
pipe
,
PRBool
closeAfterWrite
)
{
DEBUG_LOG
((
"nsStdinWriter::WriteFromStream: count=%d
\n
"
,
count
));
mInputStream
=
inStr
;
mCount
=
count
;
mPipe
=
pipe
;
mCloseAfterWrite
=
closeAfterWrite
;
// Spin up a new thread to handle writing (non-joinable)
nsCOMPtr
<
nsIThread
>
thread
;
nsresult
rv
=
NS_NewThread
(
getter_AddRefs
(
thread
),
(
nsIRunnable
*
)
this
);
return
rv
;
}
///////////////////////////////////////////////////////////////////////////////
// nsIRunnable methods:
// (runs as a new thread)
///////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsStdinWriter
::
Run
()
{
nsresult
rv
=
NS_OK
;
#ifdef FORCE_PR_LOG
nsCOMPtr
<
nsIThread
>
myThread
;
rv
=
IPC_GET_THREAD
(
myThread
);
DEBUG_LOG
((
"nsStdinWriter::Run: myThread=%p
\n
"
,
myThread
.
get
()));
#endif
char
buf
[
kCharMax
];
PRUint32
readCount
,
readMax
;
DEBUG_LOG
((
"nsStdinWriter::Run: mCount=%d
\n
"
,
mCount
));
while
(
mCount
>
0
)
{
readMax
=
(
mCount
<
kCharMax
)
?
mCount
:
kCharMax
;
rv
=
mInputStream
->
Read
((
char
*
)
buf
,
readMax
,
&
readCount
);
if
(
NS_FAILED
(
rv
))
break
;
if
(
readCount
<=
0
)
{
rv
=
NS_ERROR_FAILURE
;
break
;
}
mCount
-=
readCount
;
PRInt32
writeCount
=
0
;
writeCount
=
IPC_Write
(
mPipe
,
buf
,
readCount
);
if
(
writeCount
!=
(
int
)
readCount
)
{
PRErrorCode
errCode
=
IPC_GetError
();
DEBUG_LOG
((
"nsStdinWriter::Run: Error in writing to fd %p (count=%d, writeCount=%d, error code=%d)
\n
"
,
mPipe
,
readCount
,
writeCount
,
(
int
)
errCode
));
rv
=
NS_ERROR_FAILURE
;
break
;
}
}
if
(
mCloseAfterWrite
)
{
DEBUG_LOG
((
"nsStdinWriter::Run: Closing pipe/inputStream
\n
"
,
rv
));
// Close pipe
IPC_Close
(
mPipe
);
mPipe
=
IPC_NULL_HANDLE
;
// Close input stream
mInputStream
->
Close
();
}
DEBUG_LOG
((
"nsStdinWriter::Run: exiting, rv=%p
\n
"
,
rv
));
return
rv
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sun, Feb 8, 2:58 PM (14 m, 14 s)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
98/5e/41f317e7d08dfe9e8736eb035ca7
Attached To
rENIG Enigmail
Event Timeline
Log In to Comment