-
Notifications
You must be signed in to change notification settings - Fork 249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Question] Is it possible to be notified when a command is executed #56
Comments
Short answer - no. Long answer: When you run a program on the slave side from a terminal emulator, it is usually run "within" a shell. The shell is therefore the first program at the slave side and becomes the session leader in its own process group. When you start your Still it is possible to get a hold of the program at the slave side by OS dependent process control tools. The module does this in |
@jerch Thanks for the detailed answer :) I kind of threw it out here knowing it would be hard. I decided to work around it since it's just an internal getting started with git/gerrit app. |
I'm going to look at doing this in the future at a higher level: xtermjs/xterm.js#576 microsoft/vscode#20676 |
With https://github.com/mheily/libkqueue it would be possible to use BSD style kqueue proc events on linux and windows to achieve this. See http://doc.geoffgarside.co.uk/kqueue/proc.html for an example. |
@Tyriar Inspired by python's psutil package I started something similar here https://github.com/jerch/node-psutil. It gonna have support for a process Problem is - only way to check reliably for a 'terminate' event of a process under unix is to poll the process regularly (done by a |
This is by no means bullet proof, but I wrote a module called autoterm that handles this by waiting for a specific prompt string to show up in the stream. For simple cases it works beautifully. |
@jerch I think $PS1 injection does a great job solving this issue on Unix-like systems. The main issue imo is how to reliably do it on Windows, I was thinking a user configurable regex (with pre-defined ones for default prompts on cmd, WSL, PS) that extracted the cwd. That approach isn't super reliable though. @jrop thanks for chiming in 😃. It looks like you're only listening for a single |
@Tyriar no, it listens for data events until the prompt is matched. Also the shell and the prompt are configurable, so it works for customized prompts as well. |
@jrop ah ok, that's pretty much shell integration as described in xtermjs/xterm.js#576 then isn't it? Just workout picking up and modifying the user's |
@Tyriar I have a customized prompt and this method still works because I can pass in a customized regex. |
@Tyriar I will have to look into shell Integration, not sure to be honest |
@jrop my 2 ways to support this were:
|
@Tyriar Alright, was typing from a phone previously, so hopefully I can be more clear now. I just looked into shell integration and my solution is more general because it does not rely on custom escape sequences. However, this does not stop injecting sequences into For the record, the case I was targeting was that we need to automate some deployment scripts, which need a TTY allocated over SSH. Of course, ssh supports the |
@jrop thanks for the clarification 😃 |
@Tyriar Yeah the prompt injection can help to get close, would this be possible under windows with the |
@jerch whatever the solution I'd want it to work on cmd, powershell, wsl bash, cygwin, etc. Let me know if you come up with anything more reliable/performant than regex matching every line. |
@Tyriar I made some progress with the psutil module. Under linux I am able to spot sub process creation and termination if the process runs long enough (at least 50 ms) with this poller: var psutil = require('./index');
var p = psutil.Process(14299); // shell pid to spot
var children = [];
var poller = setInterval(function () {
if (!p.isRunning())
clearInterval(poller);
var _children = p.children();
for (var i=0; i<_children.length; ++i) {
if (children.indexOf(_children[i]) == -1) {
var sub = psutil.Process(_children[i]);
console.log('### process start: ', sub.pid);
sub.on('terminate', function(process) {
console.log('### process end: ', process.pid);
});
}
}
children = _children;
}, 50); It can be moved to libuv/C++ to to reduce the CPU load and narrow the timeframe, still its another poller and cant spot short running subprocesses, bummer. From my investigations it is not possible to leave the polling field for process interaction in a platform independent way since most platforms just dont give APIs for pure event based process handling (only shining exception is kqueue under BSDs with the ability to register callbacks for process creation and termination). Neither linux nor solaris can do this. For windows it is partly doable for process termination at least by a blocking subthread. Well I am not sure yet about this approach. Any other conceptual ideas? |
Yeah I'd want to stay away from a polling solution, this is what I do not in VS Code's terminal to track when the process title changes, it falls apart on short lived processes though https://github.com/Microsoft/vscode/blob/e4bccec8ee656480e2d6fced77fbff683fed079e/src/vs/workbench/parts/terminal/electron-browser/terminalProcess.js#L132 |
@Tyriar Now it gets hacky - I found a way under linux to intercept process creation by overloading the A similar approach by hooking into |
@Tyriar Please have a look at https://github.com/jerch/subprocess-watcher for a proof of concept under linux. It is nearly 100% event based (just a single cleanup poller due to messed up event order, ehem). It uses |
@jerch pretty cool, would this work under macOS too? |
Not tested yet, but I think the fork /exec* overwrites will work there too. The inotify stuff should be doable with kqueue. But first I have to see what is possible with kqueue itself, maybe the overwrites are not needed there at all. kqueue can catch process creation for sure, still it might not work reliably due to the sandbox idea under macOS. Also I dont know if I can get the command line and arguments with kqueue alone. If I manage to get all the infos it should be possible to do something like this: var monitor = new Monitor(<parent_pid>); // pid of the shell
monitor.on('child', function(child){
if (child.cmdline == 'git status') {
console.log('git status started...');
child.on('terminate', function() {
console.log('git status finished...');
});
}
}); |
Ok tested on OSX - the fork/exec* thing works, for process termination I found a simpler way by using a library destructor. The latter works also under Linux (prolly all POSIX systems) which makes the fifos obsolete and simplifies the logic a lot. Basically now I can route everything over just one fifo and create events in node as soon as something was written to it. BTW kqueue is not sufficient under OSX, it lacks the |
The library destructor way has its own problems - while it can spot all graceful process exits, a process abort triggered by the kernel cant be spotted this way. As long as the process aborts by faulty condition from within I can catch it by overwriting all You can find the test code for linux under this branch https://github.com/jerch/subprocess-watcher/tree/desctructor_exit (works similar under OSX, code not yet pushed) |
Slowly getting there - after some refactoring there is now an example with pty: https://github.com/jerch/subprocess-watcher/blob/master/examples/pty.js. It is still linux only, next is to port it to OSX (see osx branch). |
Basic OSX port is done. Exits are spotted with kqueue (by a small c helper), the single fifo per process idea does not work at all under OSX (maybe due to some security constraint). |
Under Windows the trickest part is spotting the creation of a new subprocess. I tried it first with WMI, but that is not reliable enough (basically just a poller itself and misses very short running processes). Therefore I went with hooking into |
Basic Windows ports is done. Tested with Win10 64bit, works also with 32 bit apps there (wow64). Does not work with WSL apps yet, I think this needs a linux drop in (no clue yet about the subsystem mechanics). Btw under Windows I can always grab the exitcode, a pity POSIX limits this to the parent process. |
Basic porting to the main platforms is done. Atm the module supports linux, osx and windows. Gonna do a refactoring of the module API before getting lost in platform specific details... Btw I am somewhat surprised how easy it is to "undermine" OS parts and wonder if this is used by malicious software. Exp. the process creation hooks feel kinda viral (they are viral here in the meaning of getting applied over and over to children). Well, the hooks I applied do nothing fishy but I can imagine to do very evil things from here on. Suspicious at least... |
Sounds like this is the sort of thing that would get flagged by Windows Defender and other anti-virus software for being naughty. |
Only few tests here https://www.virustotal.com/en/ flag the windows binaries. On my computer Windows Defender did not flag any of it. |
Basic API consolidation is done, module is alpha now. It comes with two small examples (a tree view and a pty invocation). The native parts are almost settled and work quite reliable. There are still some platform quirks that need to be addressed in the JS part, esp. handling of the first process still differs. Also there is no testing code yet... |
Geez, x86 32/64bit multiarch is quite painful with ld (linux) - with ld you can annotate this by a Second big bummer - GYP doc sucks big time and I was not able to retrieve command output of a previous target (delayed command evaluation). Seems this is not supported by GYP at all. Therefore I had to create another helper module 'node-libexpansion' that builds a small helper binary to catch the Third bummer - since GYP doc sucks big time I have no clue how to set a custom variable from the commandline during module build to let people set multiarch support on or off. Foresight bummer - the problems with GYP will get even more complicated as soon as other multiarch platforms should be supported (How about ARM under linux and BSDs in general?) I start to wonder why people invented/use GYP at all while there are other better more complete build systems like autotools or cmake. Damn node-gyp... Ok enough whining - x86 32/64bit multiarch works now for all supported platforms (linux, osx, windows), though under linux x64 32bit-buildtools must be installed for now. Thats a big drawback since it is uncommon to have both buildsets installed. Not sure yet how to address this. TL;DR: GYP fails big time to address arch specific matters. Multiarch still works with quirky workarounds and is likely to fail as soon as other ld based platforms with multiarch support come in. |
Solved the 32/64 multiarch buildset problem under linux with a test, that tries to compile IA32 under AMD64 in the |
@jerch just making sure you know, I'm hesitant in shipping something like this in VS Code as it seems quite hacky and would likely fail in many environments as a result. |
No worries, I am with you. I figured that as soon as I realized that it is only possible by touching OS core features. Under Windows it is very intrusive and since it has to place a longjump into |
I was appreciating how much effort you put into this research project, increasingly fascinating stuff. You deleted my example code though 😢 |
Simple thing: the pattern can be misused for malicious things on all major operating systems. Who needs Spectre if one can hook into systems this easy. Its good that its gone, it should never have been made. |
I would like to be able to know when a
command
has been executed. Consider doingIs it possible to be notified when the above command has been executed? I have an Electron app where
commands
are executed through the UI and need to take actions when thecommand
is finished e.g routingThe text was updated successfully, but these errors were encountered: