Age | Commit message (Collapse) | Author |
|
Once again we've been bitten by comparison failure between "c:\somepath" and
"C:\somepath". Normalize paths in both Python helper scripts to make that
comparison more robust.
|
|
Apparently something in the Linux system header chain #defines a macro Status
as 'int'. That's just Bad in C++ land. It should at the very least be a
typedef! #undefining it in llprocess.h permits the viewer to build.
|
|
|
|
Include logic to engage Linden apr_procattr_autokill_set() extension: on
Windows, magic CreateProcess() flag must be pushed down into apr_proc_create()
level. When using an APR package without that extension, present
implementation should lock (e.g.) SLVoice.exe lifespan to viewer's on Windows
XP but probably won't on Windows 7: need magic flag on CreateProcess().
Using APR child-termination callback requires us to define state (e.g.
LLProcess::RUNNING). Take the opportunity to present Status, capturing state
and (if terminated) rc or signal number; but since most of the time all caller
really wants is to log the outcome, also present status string, encapsulating
logic to examine state and describe exited-with-rc vs. killed-by-signal.
New Status logic may report clearer results in the case of a Windows child
process killed by exception.
Clarify that static LLProcess::isRunning(handle) overload is only for use when
the original LLProcess object has been destroyed: really only for unit tests.
We necessarily retain our original platform-specific implementations for just
that one method. (Nonstatic isRunning() no longer calls static method.)
Clarify log output from llprocess_test.cpp in a couple places.
|
|
Any RAII class should either be noncopyable or should deal appropriately with
a copy operation. ManageAPR is intended only for extremely simple cases, and
hence should be noncopyable.
|
|
|
|
TeamCity requires that certain characters (notably "'") must be escaped when
embedded in service messages:
http://confluence.jetbrains.net/display/TCD65/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ServiceMessages
TUT frequently outputs messages containing "'", e.g. from ensure_equals()
failure. We've seen TC output nesting get confused when it fails to process
service messages properly due to parsing unescaped messages.
Along with test<n> number, report test name (from set_test_name()) when
available.
Eliminate horsing around to produce normal output on both std::cout and
possible output file. When output file is specified, use
boost::iostreams::tee_device to do fanout for us.
Improve placement (and possibly reliability) of service messages.
Clean up a startling amount of redundancy in service-message production.
|
|
Windows 7 and friends tend to create a process already implicitly allocated to
a job object, and a process can only belong to a single job object. Passing
CREATE_BREAKAWAY_FROM_JOB in CreateProcessA()'s dwCreationFlags seems to
bypass the access-denied error observed with AssignProcessToJobObject()
otherwise.
This change should (!) enable OS lifespan management for SLVoice.exe et al.
|
|
|
|
On Posix, these and the corresponding getProcessID()/getProcessHandle()
accessors produce the same pid_t value; but on Windows, it's useful to
distinguish an int-like 'id' useful to human log readers versus an opaque
'handle' for passing to platform-specific API functions. So make the
distinction in a platform-independent way.
|
|
|
|
On Posix platforms, the OS argument mechanism makes quoting/reparsing
unnecessary anyway, so this only affects Windows.
Add optional 'triggers' parameter to LLStringUtils::quote() (default: space
and double-quote). Only if the passed string contains a character in
'triggers' will it be double-quoted.
This is observed to fix a Windows-specific problem in which plugin child
process would fail to start because it wasn't expecting a quoted number.
Use LLStringUtils::quote() more consistently in LLProcess implementation for
logging.
|
|
That is, we try to pass through each args entry as a separate child-process
arvg[] entry, whitespace and all.
|
|
|
|
|
|
If LLProcess can't set the right flag on a Windows Job Object, the object
isn't useful to us, so we might as well discard it.
quote() is sufficiently general that it belongs in LLStringUtil instead of
buried as a static helper function in llprocess.cpp.
|
|
|
|
This appears to close a long-pending action item, as it seems the original
LLProcessLauncher implementation was in fact cloned-and-edited from this logic
in LLVivoxVoiceClient::stateMachine().
In any case, leveraging LLProcess buys us:
- reduced redundancy; fewer maintenance points
- logging for both success and errors
- (possibly) better SLVoice.exe lifespan management.
|
|
Much as I dislike viewer log spam, seems to me starting a child process,
killing it and observing its termination are noteworthy events.
New logging makes LLExternalEditor launch message redundant; removed.
|
|
|
|
The idea is that, with the right flag settings, this will cause the OS to
terminate remaining viewer child processes when the viewer terminates --
whether or not it terminates intentionally. Of course, if LLProcess's caller
specifies autokill=false, e.g. to run the viewer updater, that asserts that we
WANT the child to persist beyond the viewer session itself.
|
|
|
|
Using a Params block gives compile-time checking against attribute typos. One
might inadvertently set myLLSD["autofill"] = false and only discover it when
things behave strangely at runtime; but trying to set myParams.autofill will
produce a compile error.
However, it's excellent that the same LLProcess::create() method can accept
either LLProcess::Params or a properly-constructed LLSD block.
|
|
This allows callers to pass either LLSD formatted as before -- which all
callers still do -- or an actual LLProcess::Params block.
|
|
|
|
LLProcessLauncher had the somewhat fuzzy mandate of (1) accumulating
parameters with which to launch a child process and (2) sometimes tracking the
lifespan of the ensuing child process. But a valid LLProcessLauncher object
might or might not have ever been associated with an actual child process.
LLProcess specifically tracks a child process. In effect, it's a fairly thin
wrapper around a process HANDLE (on Windows) or pid_t (elsewhere), with
lifespan management thrown in. A static LLProcess::create() method launches a
new child; create() accepts an LLSD bundle with child parameters. So building
up a parameter bundle is deferred to LLSD rather than conflated with the
process management object.
Reconcile all known LLProcessLauncher consumers in the viewer code base,
notably the class unit tests.
|
|
by everyone
|
|
moved LLInitParam, and LLRegistry to llcommon
moved LLUIColor, LLTrans, and LLXUIParser to llui
reviewed by Nat
|
|
|
|
Despite LLProcessLauncher's list-of-argument-strings API, on Windows it must
ram them all into a single command-line string anyway. This means that if
arguments contain spaces (or anything else that would confuse Windows command-
line parsing), the target process won't receive the intended arguments.
Introduce double quotes for any arguments not already double-quoted by caller.
|
|
Apparently our TeamCity build machines are still not up to Python 2.6.
|
|
|
|
typedef LLProcessLauncher::ll_pid_t to be HANDLE on Windows, pid_t elsewhere.
Then we can define getProcessID() returning ll_pid_t on all platforms,
retaining getProcessHandle() for hypothetical existing consumers... of which
there are none in practice.
This lets us define isRunning(ll_pid_t) to encapsulate the platform-specific
logic to actually check on a running child process, turning non-static
isRunning() into a fairly trivial wrapper.
|
|
|
|
Instead of free python() and python_out() functions containing a local
temporary LLProcessLauncher instance, with a 'tweak' callback param to
"do stuff" to that inaccessible object, change to a PythonProcessLauncher
class that sets up a (public) LLProcessLauncher member, then allows you to
run() or run() and then readfile() the output. Now you can construct an
instance and tweak to your heart's content -- without funky callback syntax --
before running the script.
Move all such helpers from TUT fixture struct to namespace scope. While
fixture-struct methods can freely call one another, introducing a nested class
gets awkward: constructor must explicitly require and bind a fixture-struct
pointer or reference. Namespace scope solves this.
(Truthfully, I only put them in the fixture struct originally because I
thought it necessary for calling ensure() et al. But ensure() and friends are
free functions; need only qualify them with tut:: namespace.)
|
|
NamedTempFile makes no attempt to deal with copying, therefore make it
noncopyable.
|
|
Run INTEGRATION_TEST_llprocesslauncher using setpython.py so we can find the
Python interpreter of interest.
Introduce python() function to run a Python script specified using
NamedTempFile conventions.
Introduce a convention by which we can read output from a Python script using
only the limited pre-January-2012 LLProcessLauncher API. Introduce
python_out() function to leverage that convention.
Exercise a couple of LLProcessLauncher methods using all the above.
|
|
On a Posix platform (vfork()/execv() implementation), if for any reason the
execv() failed (e.g. executable not on PATH), the viewer would never know, nor
the user: the vfork() child produced no output, and terminated with rc 0! Add
logging, make child terminate with nonzero rc.
Remove pointless addArgument(const char*) overload: this does nothing for you
that the compiler won't do implicitly.
In llupdateinstaller.cpp, remove pointless c_str() call in addArgument() arg:
we were starting with a std::string, then extracting its c_str(), only to
construct a whole new std::string from it!
|
|
|
|
Specifically:
Introduce ManageAPR class in indra/test/manageapr.h. This is useful for a
simple test program without lots of static constructors.
Extract NamedTempFile from llsdserialize_test.cpp to indra/test/
namedtempfile.h. Refactor to use APR file operations rather than platform-
dependent APIs.
Use NamedTempFile for llprocesslauncher_test.cpp.
|
|
|
|
Add unit tests to verify basic functionality.
|
|
|
|
Defend test against the ambiguous answer to that question by not recording, or
testing for, EOF history events.
Enrich output for history-verification failures: display whole history array.
|
|
|
|
Previous logic was vulnerable to the case in which both pipes reached EOF in
the same loop iteration. Now we use std::list instead of std::vector, allowing
us to iterate and delete with a single pass.
|
|
Otherwise the unreferenced declaration causes a fatal warning.
|
|
Quiet the temporary child_status_callback() output.
Add a bit of diagnostic info if apr_proc_wait() returns anything but
APR_CHILD_DONE.
|
|
At least on OS X 10.7, a call to apr_proc_wait(APR_NOWAIT) in fact seems to
block the caller. So instead of polling apr_proc_wait(), use APR callback
mechanism (apr_proc_other_child_register() et al.) and poll that using
apr_proc_other_child_refresh_all().
Evidently this polls the underlying system waitpid(), but the internal call
seems to better support nonblocking. On arrival in the
child_status_callback(APR_OC_REASON_DEATH) call, though, apr_proc_wait()
produces ECHILD: the child process in question has already been reaped.
The OS-encoded wait() status does get passed to the callback, but then we have
to use OS-dependent macros to tease apart voluntary termination vs. killed by
signal... a bit of a hole in APR's abstraction layer.
Wrap ensure_equals() calls with a macro to explain which comparison failed.
|
|
Fix EOL issues: "\r\n" vs. "\n".
On Windows, requesting a read in nonblocking mode can produce EAGAIN instead
of EWOULDBLOCK.
|