-
Notifications
You must be signed in to change notification settings - Fork 2k
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
sys/shell: Exit the shell on ctrl-D #10788
Conversation
use case? |
Examples, test scripts. In my case I was trying to integrate more tests in to Right now the shell is an infinite loop (except in native). I believe there should be a way to get out. |
Why? |
I don't know about this. I sometimes use the fact in my testing, that the node is still accessible with
There are also OrderedDict. |
If you have a recent version of python everything should be OK. In 3.7 it is even documented that they preserver insertion order.
Yes, but the test here was only using a dict to store pairs and not doing indexing of any kind. In that case a list of 2-tuples is the best choice. |
I don't see how this helps me with the shell being closed and me not being able to access the node anymore... |
(unless I reset) |
Sorry, I messed up the comments. |
This still leaves the node in an "unexpected" state. If I reconnect with a terminal I expect the node still to do whatever it was doing before (unless I toggle the reset pin), not being able to interact with it anymore, because the shell isn't there anymore. You still don't answered the question why the shell needs to be exited. |
It will. You can try it out. If CTRL-C exits the Exiting the shell allows one to write a test like the one in #9733 where the first part is interactive, then the shell exits and a non interactive part is run. Of course, the non interactive part can be first, but then the test starts immediately after the node boots, and we know this brings synchronization issues. Also, if the shell can exit, but one does not want it to, it is easy to fix (wrap it in a while loop). The other case (it does not exit, but one wants it to) is not. Observe that the shell can be exited now, so this is not adding the possibility of exiting, just another way to do it: Lines 229 to 232 in 6ed11de
Lines 290 to 292 in 6ed11de
Lastly: why the shell needs not to exit? I honestly don't understand why we are bikeshedding a <1 line change. Look at the asymmetry between the reliance we have on the shell (especially for tests) and the quality of the module and associated tools (like pyterm.) I'm doing a series of PRs trying to improve the situation (#10630, #10635, #9733) and I have other PRs on the works and I keep running into the same endless bikeshedding. Look at this change: it add 74 lines of code: one is the change itself, the remaining 73 are tests: tests that test all, and that run without interaction. I cannot help but wonder why the long discussion, when stuff with much less testing and more poorly described gets in way more easily. |
This is not bikeshedding. You stated "In my case I was trying to integrate more tests in to tests/shell and needed to exit the shell." and I asked to clarify why you need to exit the shell. Even a one line change needs to be justified, especially in something that was in the same shape for almost a decade. I'm not opposed to the change, just wondering why on earth you need to exit the shell in order to add more tests, and if there's not a simpler solution. |
I am against this change, there are sometimes a lot of noise on the UART when reseting, connecting the wires etc. Normally there is little risk of the shell interpreting the noise as a real command, but when the shell closes after a single (or two separate) specific byte it gets problematic. |
I use an 'exit' command to close the shell. ^D is nice but I typed it too often without thinking and then my shell was gone. But it's true that wrapping it in a while loop solves this for applications where you don't want the shell to ever go away (for instance I think everything currently in examples would qualify). This would mimic how getty works on linux. You can always exit the shell, but by default it is configured to respawn immediately. I've needed to close the shell too. You have to if you want to use the uart for something else. I telnet in over the wireless interface and get a shell there, then run a command to kill the other shell running on the uart and redirect the uart through the telnet terminal. In this way I use a riot board as a wireless ftdi cable. Afterwards I respawn the shell on the uart. This is one use case for closing the shell but certainly there are others. |
sys/shell/shell.c
Outdated
@@ -227,7 +227,7 @@ static int readline(char *buf, size_t size) | |||
} | |||
|
|||
int c = getchar(); | |||
if (c < 0) { | |||
if (c < 0 || c == '\x03' || c == '\x04') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's right to exit on ^C. We should cancel the current line and print a new prompt. This is consistent with how bash and friends behave. Like this: benemorius/RIOT@a946c44
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion. I have implemented it, I think it is more useful.
My interpretation of the comments is that what's contested here is exiting with control-D. Cancelling with control-C should not be as controversial, right? I will split this PR. |
c434864
to
23eb783
Compare
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions. |
23eb783
to
f56eb5b
Compare
Right now the only way to exit the shell is if stdin is closed. This works on native, but on an embedded platform stdin is the uart and thus is never closed. This patch causes the shell loop to exit on EOT (ASCII 0x04 / ctrl-D), also called "End-of-Transmission".
Test that the shell exits on ctrl-D and that it exits only once.
There's a couple of uses for this functionality which I had not thought of before:
|
After re-reading my own comments I should clarify that I'm strongly in favor of exiting on ^D. We just need to wrap existing consumers of the shell with a while loop to avoid functional changes in existing code which doesn't wish to have a shell capable of exiting. |
Does wrapping the shell in a while loop solve everyone's concerns with exiting on ^D? |
So this is how Lines 293 to 310 in 7118545
Currenlty, ^D is just ignored ( |
Ok so when I say to wrap existing consumers of the shell with a while loop I don't mean adding a loop to The idea is that we can make the shell optionally exitable but then modify all the existing callers of It's equivalent to the way that in linux |
Ok, got it, but to quote yourself:
would mean to avoid functional changes we need to touch every call of |
Yes, correct. I mean grep says it's not that many changes and all of them are simply adding a while(1) loop which should be trivial to review. We do need an exitable shell. End users can make an exitable shell non-exitable but they cannot make a non-exitable shell exitable.
At a glance, I have no objection to this. |
Nevertheless, it's a moving target, as new tests or examples might introduce additional calls. In my experience those are the real trouble, as behavioral changes like this might take a while to settle for devs used to the current behavior. |
This change is in preparation to [PR 10788]. PR 10788 will make the shell exitable which may lead to unexpected behavior in comparison to previous usage of the shell. To prevent this, this PR introduces two "new" functions to the shell's API: `shell_run_once()` and `shell_run_forever()`. `shell_run_once()` basically has the same behavior as `shell_run()` in current master: Start a shell and continue reading lines until EOF is reached. `shell_run_forever()` wraps around `shell_run_once()` and restarts the shell if it exits. `shell_run()` is re-introduced as a back-porting alias for `shell_run_forever()`. As a consequence all current calls to `shell_run()` won't exit even with [PR 10788] merged (which would add EOT as additional exit condition for `shell_run_once()`). [PR 10788]: RIOT-OS#10788
See #12272 for a demonstrator of what I mean. |
One thing I must point out is that ctrl-D handling in native and on serial based targets is different (and that should be fixed some day). Native uses the terminal in cooked mode, so it will never see the ctrl-d, instead it sees an EOF. Embedded targets use what would be the equivalent of raw mode, thus seen ctrl-d, ctrl-c, etc as what they are (though depending on the terminal program you are using you may have to use Ctrl-V to prevent it from being interpreted by your local machine). IMO this discrepancy should be fixed, and native should be setting up the terminal to behave as close to the UART as possible (raw mode, no buffering, no echo) using All this being said, it may make sense to differentiate between the stream being closed and a ctrl-d (maybe do a |
you can do this, but why invent new "sentences" when you can express that same logic using the vocabulary already present in C ( |
This change is in preparation to [PR 10788]. PR 10788 will make the shell exitable which may lead to unexpected behavior in comparison to previous usage of the shell. To prevent this, this PR introduces two "new" functions to the shell's API: `shell_run_once()` and `shell_run_forever()`. `shell_run_once()` basically has the same behavior as `shell_run()` in current master: Start a shell and continue reading lines until EOF is reached. `shell_run_forever()` wraps around `shell_run_once()` and restarts the shell if it exits. `shell_run()` is re-introduced as a back-porting alias for `shell_run_forever()`. As a consequence all current calls to `shell_run()` won't exit even with [PR 10788] merged (which would add EOT as additional exit condition for `shell_run_once()`). [PR 10788]: RIOT-OS#10788
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions. |
Alternative approach provided in #13675. |
@miri64 this is unrelated. |
Ok |
Now that the examples uses |
@fjmolinas Yes, right now |
With the new shell it would look like #14345 |
Then let's close this? |
Contribution description
Note the control-C part of this PR was split to #11004 .
Right now the only way to exit the shell is if stdin is closed. This works on native, but on an embedded platform stdin is the uart and thus is never closed.
This patch causes the shell loop to exit on EOT (ASCII 0x04 / ctrl-D a.k.a. "End-of-Transmission".)
Testing procedure
Run the test in
tests/shell
withnative
and with a board.To test it by hand with a board, use a terminal program that passes ctrl-D to the tty. In native, use Ctrl-V ctrl-D to insert a literal EOT.
Related PRs
Depends on #11003 , #11004.