Simple Linux Shell. stop command - c

I am trying to implement a simple shell in linux and one of the features it should have is to enable to user to press <ctrl+D> and make it stop whatever it is doing - Basicly exactly what <ctrl+C> does in Bash.
Now I was wondering on the easiest way to do this, and I was thinking of some kind of key listener which would make the current task stop. Now the only way I could think of doing this would be to have a separate thread which would force stop and return the main thread to the base state, where it can take new input.
A separate thread would be the only way to "constantly" listen for the correct keypress.
I was hoping for some thoughts on this and possibly a better way to go about it.
[Edit]
I am trying to make a simple shell which can execute external programs, print/change directory, print/set path, print command history, invoke commands from history and print/set/remove command aliases. The CTRL-D is meant to be the equivalent of the CTRL-C in Bash, which allows the user to immediately exit a currently running program, not to exit the shell itself. [/Edit]

Why don't you just handle Ctrl-C?
Here is just one of many SO disussions on trapping the signal: Catch Ctrl-C in C
Ctrl-D generally represents EOF on standard input. You shouldn't mess with it.

If you want the Control-D character to generate an interrupt for you, then:
You need to map the EOF character to something other than Control-D.
You need to map the interrupt character to Control-D.
You do this in POSIX with the <termios.h> header and the functions:
tcgetattr()
tcsetattr()
You'd retrieve the current attributes in a struct termios using tcgetattr(). You'd make a copy of the structure, and modify (for sake of argument) the copy, changing the elements of the c_cc array indexed by VINTR and VEOF (plus any other changes you want to make), and then setting the new attributes using tcsetattr(). You'd also arrange to ensure that you restore the original terminal settings (by another call to tcsetattr() using the original set of attributes retrieved with tcgetattr()) before your shell exits. This might be done by a handler registered with atexit(), or by other mechanisms. You should endeavour to reset the terminal attributes under all circumstances. You can't do anything about a SIGKILL killing you.
While you're testing this, make a little script for yourself:
echo stty $(stty -g) > sane
chmod u+x sane
That records the current (presumably sane) terminal settings in a form that is designed for stty to read reliably. If (when) you have problems with your shell, you can use Control-JsaneControl-J to run the script and reset your terminal back to the known sane settings. This is also useful if you're developing programs that use the curses library.

Unless my comment on the other answer is incorrect, I think what you should do is:
if (!fgets(input, sizeof(input), stdin) == NULL)
{
... do cleanup here ...
exit(0);
}
or something equivalent to that.

Related

How and when are interrupt key combination,such as CTRL-C, translated to signals?

Using a Linux OS, I need to transparently pass all keystrokes from remote connections to local hardware connections. This is fairly straight forward for everything but signals. I know I can register handlers for each signal that capture and pass the keystroke, but this seems like a hack that might not work for edge cases (what if someone changes the interrupt key combo mapping, would I be double passing the keystrokes, etc.).
In order to come up with a better solution, I really need to know more about how the key combos, IE: CTRL-C, become signals, IE: SIGINT.
How does the keycombo become a Signal (in the kernel, client application, runtime)?
Is the keystroke still passed? If CTRL-C has a null handler, does STDIN still get CTRL-C?
What is the best way to not interpret any keystroke as a signal combination? Can this be done in TTY or Environment settings?
The key combo is turned into a signal by the terminal's line discipline, a layer in the TTY subsystem. Here's the description from Wikipedia:
For example, the standard line discipline processes the data it receives from the hardware driver and from applications writing to the device according to the requirements of a terminal on a Unix-like system. On input, it handles special characters such as the interrupt character (typically Control-C) and the erase and kill characters (typically backspace or delete, and Control-U, respectively) and, on output, it replaces all the LF characters with a CR/LF sequence.
The keypress is not passed, it's suppressed when the signal is raised. If the process has no SIGINT handler, the kernel will terminate it.
You can obviously override this: that's how tools like ssh work. You can easily do this by setting the terminal to "raw" mode, e.g. with the shell command stty raw. If you then run cat -vE you can hit any keys you want and see them printed in caret notation. Make sure to plan on a way to close the terminal because you can obviously not Ctrl-C/Z/D out of it anymore.
How does the keycombo become a Signal (in the kernel, client application, runtime)?
This translation is performed as part of the kernel's TTY line discipline. There are a number of other translations performed as part of this process, like end-of-file (^D), stop signalling (^Z), character echo, basic line editing (backspace, ^U, etc), and DEL/BS and CR/CRLF translation.
Is the keystroke still passed? If CTRL-C has a null handler, does STDIN still get CTRL-C?
No. Control characters which are handled by the line discipline are not passed to the foreground process.
What is the best way to not interpret any keystroke as a signal combination? Can this be done in TTY or Environment settings?
Use tcgetattr() and tcsetattr() to change the terminal attributes. The c_cc element of struct termios contains settings for control characters; you can disable some or all elements by setting them to _POSIX_VDISABLE.
(You can also use cfmakeraw() -- documented at the same page as above -- to clear most elements of a struct termios, giving you a mostly "raw" terminal. However, this may disable more terminal behaviors than you expect.)

How to exit console screen in C language

I'm trying to exit the console screen i.e close the screen what command can i use to achieve this.
void main()
{
int n;
printf("Please enter a number less than 5");
scanf("%d", &n);
if(n <= 5)
printf("good");
else
{
printf("You entered a number above so the program will exit");
//here i need to call a function or use a command that will close
// the console screen;
}
}
Any help will be appreciated thanks
The C11 standard n1570 does not know about the "console screen" (and I guess you speak of the terminal emulator running your program). Notice that C11 does not mention "screens" or "keyboards" (only standard streams, and very often stdout is not a "console") and many computers (e.g. most Internet servers or supercomputers, or even your mobile phone...) don't have both. Also, your program could be run (even on Windows) with redirections or in a pipeline and then it has no console (so your question don't make any sense in such a common case).
So in general, there is no way to do what you want (since it does not make any sense), in a standard way.
Perhaps your operating system provide some (OS specific) way to achieve that. So investigate the OS API relevant to your system (e.g. WinAPI on Windows, or Linux syscalls -listed in syscalls(2)).
Perhaps you want to use some terminal related library like ncurses.
If your terminal follows the ANSI escape code conventions, you might follow them.
Otherwise, consider making your program having some GUI. For that, you practically need some widget toolkit (such as Qt, GTK, etc..)
You might also consider some inter-process communication with your desktop environment. How to do that (or even its possibility) is very operating-system and desktop specific and might be related to session management.
BTW, remember that stdout is often buffered (and perhaps line-buffered). You'll better end your printf control strings with \n and/or call fflush.
In a windowing operating system or execution environment the console window will close immediately the process terminates, so it is not clear what you are asking here since in your example the program terminates regardless of what input is entered.
If you are running the code from an IDE, often the IDE will create a console process and launch your code within that. In that case the console is not "owned" by your application, but is executed as a child process; in which case the window will remain open until the parent process launched by the IDE is closed. Similarly if you launch your program from a command shell. It is probably unreasonable behaviour for a process to attempt to close its parent even if it is possible.
It is possible to "hide" the console window while the process continues to run, which may be what you are asking; the means of doing that is platform specific, and you have not specified; for Windows such a question would be a duplicate of Win32 programming hiding console window. However it is quite possible that these methods will not work if the process is not launched directly but from some other console process.
in Windows you may simply write code on Notepad, then compile and run it through the Command prompt (cmd.exe). If you have GCC installed as compiler (with all the needed packages), then compile your main.c file as:
gcc main.c -o main.exe
If all went fine, as you run "main", there will be all of your output that you can close or step over for more editing. Bye
PS EDIT -- I see your point: when you launch your .exe from itself, the window closes without giving satisfaction of the messages. You may add a workaround like this before the last curly bracket:
printf("Press any key\n");
scanf("%d");
}
So the output window will still wait for one more input before closing.
You may check for additional information for eg here: https://www3.ntu.edu.sg/home/ehchua/programming/cpp/gcc_make.html
Bye

Implementing commands history triggered by arrow up/down keys in C

I have implemented my own shell which executes all existing UNIX commands and several custom commands. What I need to do is, to access previous commands by using arrow up/down keys in the way exactly how UNIX terminal does.
I have found that I could use getch() method of <curses.h> library. Even if I couldn't figure out how to use that method to expect non-blocking keyboard input, the real problem with that is that I need to compile the program to be able use it as following: gcc myShell.c -lcurses
What I have to do is probably using a signal to listen background input without expecting Enter input. I have also tried to fork() to create another process which will be responsible of waiting arrow keys inputs and then used getch() method to be able to catch the key but it didn't work either.
Since I already use read() method to read the command line inserted by the user. This arrow key input should be completely independent from the existing input reading.
Some answers in Stackoverflow points the <conio.h> library which does not exist in UNIX, hence unfortunately platform dependent.
To summarize briefly, I need to read arrow up/down keys in the background in my own shell and I have to do that platform independent and without being have to type something other than gcc myShell.c for compilation. It also should be captured without Enter press.
If any details about my question is not clear enough, please let me know to explain with more details as I could.
Thanks in advance!
The problem here is with the terminal you're using, not your program. For example, Windows command prompt will buffer input before even sending it to your program, so there is no platform-independent way to force the terminal to give you that data. I suspect curses has a platform-dependent way to turn off that buffering, and thus can get the character.
See answers to this question for more details: How to avoid press enter with any getchar()

How to execute command from C program

What is the best way to execute command such as 'trap -p' etc directly from program written in ANSI C?
I tried:
system("bash");
system("trap -p");
But when I add system("bash") program dissappears. How to prevent it from dissapering or what is the better way to execute such commands?
EDIT:
Thank you all for helping me.
More details about what I intended to achieve:
I want to be able to:
-add new traps inside my program ( traps working only in my program )
-display currently set traps ( again, traps in my program )
Is that possible to achive in relatively easy way?
But when I add system("bash") program dissappears
Yes, bash is now running and your C program is waiting for it to terminate. It seems to have disappeared because you would be seeing a new shell running in your terminal. Try typing exit and your C program will continue. You can confirm this by adding a print statement after system("bash");.
You can get trap -p to produce output by specifying the -i option to bash, which makes it an interactive shell:
system("bash -i -c 'trap -p'");
From this it would seem that trap requires a tty, which non-interactive bash doesn't have.
Or you could put the trap command in a script and run it like this:
system("bash script.sh");
The contents of script.sh:
echo Before setting trap...
trap -p
trap somecmd SIGINT
echo After setting a trap...
trap -p
In the output you should see that initially there were no traps set (assuming that none were inherited from the shell that ran your C program), and then trap should show the newly created trap.
I am guessing you are on Linux or some other POSIX system
You should get a better picture of Linux programming by reading Advanced Linux Programming. It looks like you are misunderstanding processes and signals.
You cannot catch a signal inside the process running your C program from some shell (either your parent shell, or any child shell started with system(3). So the output of trap -p from any shell is not relevant to your program (but to the shell running it). Hence even using popen(3) like  FILE*fp = popen("trap -p", "r"); (or popen("bash -i -c 'trap -p'", "r")....) then reading from fp (and at last pclose-ing it) is useless.
If you want to handle signals inside your C program, read first carefully signal(7); then read POSIX signal.h documentation (notice sig_atomic_t); read also sigaction(2), fork(2), execve(2)
I want to be able to: add new traps inside my program
This has no meaning for C programs running on Linux or POSIX. A C program can handle (with great care and caution!) some signals, which are not traps.
[I want to:] display currently set traps
Again, "trap" has no sense inside a C or C++ program, but signals do. You don't really need to display the currently set signal handlers, because you have set them before. And sigaction(2) accepts a third oldact pointer to hold the previous signal action.
Processor traps (which are only handled by kernel code, not by application code) are remotely and indirectly related to signals. For example, a page fault (for implementation of virtual memory) is often handled by the kernel to fill the page cache with a page from disk (file or swap zone) but may translate to a SIGSEGV signal (for segmentation fault) sent to the process, which often terminates with a core dump.
If you install some signal handler in your C program, be sure to understand what are async-signal-safe functions (the only ones you are allowed to call from a signal handler; in particular calling fprintf or malloc -even indirectly- is forbidden, so is undefined behavior). A useful way of handling a signal is to declare some volatile sig_atomic_t variables and set them inside signal handlers (and test and reset them outside, e.g. in your event loop).
The shell trap builtin is used to manage some signals (and also exit and error conditions). To manage signals in C, use sigaction(2). To run something at exit(3) time, use atexit(3). To handle error conditions, be sure to test every individual syscalls(2) and most library functions (like scanf(3) or malloc(3) etc etc ..., see intro(3)), using errno(3)
Instead of running an interactive bash, it seems that you are looking for a way to run trap -p in a noninteractive Bash shell. Here's how you do that.
system("bash -c 'trap -p'");
However, your C-level signal handlers will not be visible in the trap -p output. Bash can only know about trap handlers which were defined in Bash; and the shell you are starting will not have any (unless they are inherited from the shell you used to start your C program).

Preventing MSYS 'bash' from killing processes that trap ^C

I have a console-mode Windows application (ported from Unix) that was originally designed to do a clean exit when it received ^C (Unix SIGINT). A clean exit in this case involves waiting, potentially quite a long time, for remote network connections to close down. (I know this is not the normal behavior of ^C but I am not in a position to change it.) The program is single-threaded.
I can trap ^C with either signal(SIGINT) (as under Unix) or with SetConsoleCtrlHandler. Either works correctly when the program is run under CMD.EXE. However, if I use the "bash" shell that comes with MSYS (I am using the MinGW environment to build the program, as this allows me to reuse the Unix makefiles) then the program is forcibly terminated some random, short time (less than 100 milliseconds) after the ^C. This is unacceptable, since as I mentioned, the program needs to wait for remote network connections to close down.
It is very likely that people will want to run this program under MSYS bash. Also, this effect breaks the test suite. I have not been able to find any way to work around the problem either from within the program (ideal) or by settings on the shell (acceptable). Can anyone recommend anything?
I had the exact same problem - I had written a program with a SIGINT/SIGTERM handler. That handler did clean-up work which sometimes took awhile. When I ran the program from within msys bash, ctrl-c would cause my SIGINT handler to fire, but it would not finish - the program was terminated ("from the outside", as it were) before it could complete its clean-up work.
Building on phs's answer, and this answer to a similar question: https://stackoverflow.com/a/23678996/2494650, I came up with the following solution. It's insanely simple, and it might have some side-effects that I've yet to discover, but it fixed the problem for me.
Create a ~/.bashrc file with the following line:
trap '' SIGINT
That's it. This traps the sigint signal and prevents msys bash from terminating your program "from the outside". However, it somehow still lets the SIGINT signal through to your program, allowing it to do its graceful cleanup/shutdown. I can't tell you exactly why it works this way, but it does - at least for me.
Good luck!
This could be due to the infamous mintty "Input/Output interaction with alien programs" problem (aka mintty issue #56). In this case it is manifesting as Ctrl-C abruptly killing the program rather than being passed down to the program as a signal to be caught and handled. Evidence for this theory is based on zwol's extensive explanation: "console-mode Windows application", "[application is] designed to do a clean exit when it received ^C", "[application] works correctly when the program is run under CMD.EXE" but "[when using the terminal] that comes with MSYS [...] program is forcibly terminated" (at the time of writing (2018) MSYS defaults to using mintty as its terminal).
Unfortunately mintty isn't a full Windows console replacement and various behaviours expected by "native" Windows programs are not implemented. However, you might have some joy wrapping such native programs in winpty when running them within mintty...
Other questions also describe this behaviour: see https://superuser.com/questions/606201/how-to-politely-kill-windows-process-from-cygwin and https://superuser.com/questions/1039098/how-to-make-mintty-close-gracefully-on-ctrl-c .
Arg - 5 minute edit on comment. Here's what I wanted to write:
As a workaround, instead of trying to trap the CTRL-C event which is also being propagated to the shell I'd propose turning off the ENABLED_PROCESSED_INPUT on stdin so that CTRL-C is reported as a keyboard input instead of as a signal:
DWORD mode;
HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
GetConsoleMode(hstdin, &mode);
SetConsoleMode(hstdin, mode & ~ENABLE_PROCESSED_INPUT); /* disable CTRL-C processing as a signal */
You could then process keyboard input in your main thread while the rest of the program does its thing in a separate thread and set an event to cleanup when CTRL-C is received.
When you run your program with MSYS bash, do you run the executable directly, or is there a wrapping (bash) shell script?
If so, it may be registering a custom Ctrl-C handler with the trap command (that does a sleep followed by a kill.) If such a thing exists, alter or remove it.
If there is no trap registered, or there is no wrapping script, consider making such a script and adding your own trap to override the default behavior. You can see an example of how to use it here or on bash's man page (in the SHELL BUILTINS section).
Ctrl-C is SIGINT? I thought Ctrl-Z was SIGINT, but Ctrl-C is SIGTERM. Check that.
Do you have a CYGWIN environment setting (in control panel/environment variables)? Try setting CYGWIN=notty and restart open a new MSYS bash shell - does the problem persist?

Resources