I'm trying to bind an interactive program to a key in bash using bind -x. This program sets the terminal in raw mode during its execution, and resets the previous settings at exit.
Some keys have different codes when the program is run via the bash binding. For example, the down arrow code is <Esc>OB instead of <Esc>[B. I'm trying to understand why and find a solution to have the "normal" codes.
I printed the flags obtained with tcgetattr, they are the same whether the program is run normally or via the binding.
It is because the terminal is set in appcursor mode.
To unset it, write "\x1B[?1l" to the tty.
Related
I am using the following code to read and output each keystroke without having to press enter each time.
system("\bin\stty raw");
Right after I finish reading, the program does another system call to reset the terminal behaviour.
system("\bin\stty cooked");
The thing is, the last line is not resetting the terminal behavior as it should. Everything gets messed up once this program terminates. It continues to read input and does not do anything once an enter or CTRL C or anything else is pressed.
How can I reset the terminal behavior to the one it initially was please?
Use popen() and pclose() to run "/bin/stty -g". Read the output from stty -g] and save it for later.
When you want to reset the terminal, use "/bin/stty the-string-from-stty-g".
The mechanics are fiddly but doable.
The whole point of the -g option to stty is to give a string that can be passed back to stty to reinstate the current settings. You can then run your stty raw, ensuring that you reset the terminal before you exit using the string from stty -g.
Note, too, that stty sane does a good job of resetting aberrant terminals to a known state. You may need to run: Control-Jstty saneControl-J at the terminal command line to make it work.
You can also do it without running external programs. You'll need to look at tcgetattr() and
tcsetattr() and related functions. Again, you read the current settings (tcgetattr() et al), change a copy of them and set those as the new values, and ensure that you reset the original settings on exit (maybe with atexit()?).
I know how to get the stdout into a file using dup/dup2 system calls, but how do I get the entire output that would be normally shown on my terminal(including the prompt that says my username along with the $ symbol and the current working directory) to a file?
Yes you can, but this may be difficult in many details (depending on your expert level). For the shell to behave normally (I would mean exactly as in a terminal), then it needs to interact with a terminal (special system object). So you need to create a program that behave like a terminal, this what pseudo-terminals devices (/dev) are intended for. Read documentation about this to implement it but roughly, your application should behave like the user so should be connected to the slave side of the pseudo-terminal, and the shell to the master side of the pseudo-terminal. Then you can easily log real inputs made by the user and catch outputs made by the shell.
Can't comment cause of low reputation.
I would say there is no way to do that inside a code in C. Instead, you could use bash for example to redirect everything to a file, and leave the code in C as it is.
In this way you have all the info you want to save: prompt, current directory, call to the program (including flags), and of course the output of the program.
Well, you can do:
-For bash prompt PS1: Echo expanded PS1 (in case you want it expanded, if not there is a simple way to do it just echong PS1)
- For executed command: https://unix.stackexchange.com/questions/169259/how-to-capture-command-line-input-into-logfile-and-execute-it-at-the-same-time
- Standard output and error output: Redirect stderr and stdout in a Bash script
And that's all you want to capture, I think.
Look up the script command in Unix systems. If you want to capture all keyboard and std in/out for a command, use the script executable. If you want to see how it's done, look up the source.
I'm trying to recode the UNIX command script (as it is on OSX). This is part of an exercise for school to help students learn UNIX APIs. We are only allowed to use system calls, more specifically, only those available on MAN(2) pages on Mac OSX (since that's our OS at school).
I have a 'first version' that kind of works. Running a program such as ls prints the right output to the screen and in an output file.
The problem scenario
I run bash from within the script-clone. First issue is I get the following error:
bash: no job control in this shell
I have tried forcing the bash process into foreground with setpgrp and setpgid but that din't change anything so I concluded that was not the problem.
I also tried to understand why the real script command uses cfmakeraw (at least on Linux), as seen here, but I don't get it. The MAN page is not very helpful.
The real script also dup2s STDIN on the slave, as seen here, but when I do that, it seems like input isn't read anymore.
However, the bash still runs, and I can execute commands inside of it.
But if I run vim inside it, and then hit Ctrl-Z to put vim to the background, the terminal is messed up (which does not happen when I'm in my regular terminal).
So I guess I must have done something wrong. I'd appreciate any advice/help.
Here's the source code:
https://github.com/conradkleinespel/unix-command-script/tree/2587b07e7a36dc74bf6dff0e82c9fdd33cb40411
You can compile by doing: make (it builds on OSX 10.9, hopefully on Linux as well)
And run by doing: ./ft_script
Don't know it it makes more sense to have all the source code in StackOverflow as it would crowd the page with it. If needed, I can replace the Git link with the source.
I don't use OS X, so I can't directly test your code, but I'm currently writing a toy terminal emulator and had similar troubles.
about "bash: no job control in this shell"
In order to perform job control, a shell needs to be a session leader and the controlling process of its terminal. By default, your program inherits the controlling terminal of your own shell which runs your script program and which is also a session leader. Here is how to make your new slave process a session leader after fork:
/* we don't need the inherited master fd */
close(master);
/* discard the previous controlling tty */
ioctl(0, TIOCNOTTY, 0);
/* replace existing stdin/out/err with the slave pts */
dup2(slave, 0);
dup2(slave, 1);
dup2(slave, 2);
/* discard the extra file descriptor for the slave pts */
close(slave);
/* make the pts our controlling terminal */
ioctl(0, TIOCSCTTY, 0);
/* make a new session */
setsid()
At this point, the forked process has stdin/out/err bound to the new pts, the pts became its controlling terminal, and the process is a session leader. The job control should now work.
about raw tty
When you run a program inside a normal terminal, it looks like this:
(term emulator, master side) <=> /dev/pts/42 <=> (program, slave side)
If you press ^Z, the terminal emulator will write the ascii character 0x1A to the pts. It is a control character, so it won't be sent to the program, but instead the kernel will issue SIGSTP to the program and suspend it. The process of transforming characters into something else is called "line cooking" and has various settings that can be adjusted for each tty.
Now let's look at the situation with script:
term emulator <=> /dev/pts/42 <=> script <=> /dev/pts/43 <=> program
With normal line settings, what happens when you press ^Z? It will be transformed into SIGSTP by /dev/pts/42 and script will be suspended. But that's not what we want, instead we'd like the 0x1A character produced by our ^Z to go as-is through /dev/pts/42, then be passed by script to /dev/pts/43 and only then be transformed into SIGSTP to suspend the program.
This is the reason why the pts between your terminal and script must be configured as "raw", so that all control characters reach the pts between script and the program, as if you were directly working with it.
Here I have one command which is like interactive mode:
obex_test -b $BD_ADDR $CH_NUM
This command is from a script but I want to run this command through a system call in a C program.
obex_test is nothing but obex file transfer library.
Here I want to receive a file from remote device to local device through bluetooth.
This is the manual page of obex_test
Please can anybody tell me how can I put my C program in interactive mode like this command, and I want to use this command also.
I used popen(command,"r") but its not useful; it does not take input from the user.
If I used "w" mode then I don't know what happens; I directly get a message like >Unknown Command. It's the error this command gives when we give different options. So it's taken something as a write mode.
You could have two pairs of pipes (created with the pipe(2) system call); one for data from your program to obex_test's stdin and one from obex_test's stdout to your program. Then you would fork and execve... Beware of deadlocks (your program blocked on writing to obex_test stdin when its output pipe is full and blocking it), you might need to call poll(2) or select(2)...
However, as it man pages explain, "obex_test is a test application for the libopenobex library". So why don't call directly functions inside this libopenobex library, which you would link to your program?
You can use the system command. Check the manual page for more details.
For e.g. system( "obex_test -b 172.16.7.1 1234" );
All,
I have a little utility written in C that requires the user to press a key to make a menu selection. The code is predictably simple:
system("stty raw");
save_ID = getchar();
system("stty cooked");
It grabs the keystroke and doesn't need to wait for ENTER. It works fine from the command line, however, when I try to bind the command to a hotkey in Gnome/metacity, it waits for the keystroke alright, but the keystroke seems to be processed not as as input to the command, but as if it were a separate command. Eg. if my keystroke is "1" and I'm in a terminal I see:
"zsh: command not found: 1"
I've tried it without the "system" lines, but nothing changed. Is there perhaps some special version of "getchar" that works with the GUI? Incidentally, a non-interactive version of the program works fine when bound to a hotkey, so the program is basically functional, it just can't be used interactively.
Thanks
I believe GTK may interest you, although I've never personally used it.
http://www.gtk.org/
.
You should also be able to run gnome-terminal. Using /opt/appFoo/appFoo as the command to run your app normally, you would run
gnome-terminal -e "/opt/appFoo/appFoo" &