I have a Linux process running in the background. I want to take over its stdin/out/err over SSH and also be the terminal controller. The "original" file descriptors are pseudo terminals, too.
I have tried Reptyr and dupx. Reptyr fails around vfork, but dupx works very well. The GDB script it generated:
attach 123
set $fd=open("/dev/pts/14", 0)
set $xd=dup(0)
call dup2($fd, 0)
call close($fd)
call close($xd)
set $fd=open("/dev/pts/14", 1089)
set $xd=dup(1)
call dup2($fd, 1)
call close($fd)
call write($xd, "Remaining standard output of 123 is redirected to /dev/pts/14\n", 62)
call close($xd)
set $fd=open("/dev/pts/14", 1089)
set $xd=dup(2)
call dup2($fd, 2)
call close($fd)
call write($xd, "Remaining standard error of 123 is redircted to /dev/pts/14\n", 60)
call close($xd)
As soon as the dupx command finished, the shell is not returned and the target app receives my input (via pts/14) immediately.
Now I want to achieve the same result using my standalone binary application. I've ported the same syscalls (dup/dup2/close, etc) what being executed by the gdb by script driven by dupx:
int fd; int xd;
char* s = "Remaining standard output is redirected to new terminal\n";
fd = open(argv[1], O_RDONLY);
xd = dup( STDIN_FILENO);
dup2(fd, STDIN_FILENO );
close(fd);
close(xd);
fd = open(argv[1], O_WRONLY|O_CREAT|O_APPEND);
xd = dup( STDOUT_FILENO);
dup2(fd, STDOUT_FILENO);
close(fd);
write(xd, s, strlen(s));
close(xd);
fd = open(argv[1], O_WRONLY|O_CREAT|O_APPEND);
xd = dup( STDERR_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
write(xd, s, strlen(s));
close(xd);
Running the snipplet above is done by injecting a shared library into the remote process via sigstop/ptrace attach/dlopen/etc (using a tool similar to hotpatch). Lets consider this part of the problem to be safe and working reliable: after doing all this, the file descriptors of the target process are changed as I wanted. I can verify it by simply checking /proc/pidof target/fd.
However, the shell returns and it still receives all my input, not the target app.
I noticed if I simply attach/detach with gdb after this point (= fds changed by the injected C code) without actually changing anything, the desired behavior is accomplished (mean: the shell is not returned but the target app starts receiving my input). The command is:
gdb --pid=`pidof target` --batch --ex=quit
And now my question is: how? What happens in the background? How can I do the same without gdb? I've tried stracing gdb to get some hints, and also tried playing with the tty ioctl API's without any luck.
Please note, that obtaining the terminal controller status (if that is the key of this problem at all) by the fork/setsid way what Reptyr uses is not acceptable for me: I want to avoid forking.
Additionally, I cant control starting the target, so "why don't you run it in screen" is no answer here.
I've ssh access, thats where pts/14 was coming from. Shell and the
target app might be competing, but I've never experienced such
behaviour; dupx alwaysed did what I wanted in this scenario.
Well, sitting and wondering why the known problem by chance didn't show up in the past won't solve it, even if this point would be clarified. The way to go is to make it work by design rather than by accident. For this purpose it is necessary for your standalone binary application to not return to the shell (to avoid the concurrent reading of input) while the input is supposed to go to the target app.
See e. g. also Redirect input from one terminal to another, Why does tapping a TTY device only capture every other character?
Related
code sample from server:
dup2( client, STDOUT_FILENO ); /* duplicate socket on stdout */
dup2( client, STDERR_FILENO ); /* duplicate socket on stderr too */
char * msgP = NULL;
int len = 0;
while (len == 0) {
ioctl(client, FIONREAD, &len);
}
if (len > 0) {
msgP = malloc(len * sizeof(char));
len = read(client, msgP, len);
system(msgP);
fflush(stdout);
fflush(stderr);
}
When I send a command from the client I call the system function. This function is sufficient for many commands but not for all. I tried several different commands and I had problems with a few (ex: nano). The problem I'm facing is that after I call the system function I can not send any input any more for that command (if necessary).I can still send other commands.
My question is how can I solve this problem?
P.S. i did some test and cd command also dont work . who can explain me why?
Thanks for the help !
The test and cd commands are built into command-line shells: The shells do not execute them as external programs. They read those commands and process them by making changes inside the shell program itself.
When you execute a program with system or a routine from the exec family, it creates a separate process that runs the program. A separate process can read input, write output, change files, and communicate on the network, but it cannot change things inside the process that created it (except that it can send some information to that process, by providing a status code when it exits or by various means of interprocess communication). This is why cd cannot be executed with system: A separate process cannot change the working directory of another process. In order to execute a cd command, you must call chdir or fchdir to change the working directory for your own process.
There is a separate test command, but some shells choose to implement it internally instead of using the external program. Regarding nano, I do not know why it is not working for you. It works for me when I use system("nano") or system("nano xyz"). You would have to provide more information about the specific problem you are seeing with nano.
The way that ssh provides remote command execution is that it executes a shell process on the server. A shell is a program that reads commands from its input and executes them. Some of the commands, like cd, it executes internally. Other commands it executes by calling external programs. To provide a similar service, you could either write your own shell or execute one of the existing shells. On Unix systems, standard shells may be found in /bin with names ending in sh, such as /bin/bash and /bin/csh. (Not everything ending in sh is necessarily a shell, though.)
Even if you execute a shell, there are a number of details to doing it properly, including:
Ensuring that the standard input, standard output, and standard error streams of the shell are connected the way you want them to be.
Passing the desired environment and command-line arguments to the shell.
I've searched quite a lot, but I still don't have an answer for this. I've got a program that creates other processes by asking the user the desired command, then I use execlp to open this new process. I wanted to know if there's an easy way to the parent process find out if the command was executed, or if the received command doesn't exist.
I have the following code:
if (executarComando(comando) != OK)
fprintf(stderr,"Nao foi possivel executar esse comando. ");
where executarComando is:
int executarComando(char* cmd) {
if ( execlp("xterm", "xterm", "-hold", "-e", cmd, NULL) == ERROR) // error
return ERROR;
return OK;
}
Your problem is that your execlp always succeeds; it's running xterm, not the command you're passing to the shell xterm runs. You will need to add some kind of communication channel between your program and this shell so that you can communicate back success or failure. I would do something like replacing the command with
( command ) 99>&- ; echo $? >&99
Then, open a pipe before forking to call execlp, and in the child, use dup2 to create as file descriptor number 99 corresponding to the write end of the pipe. Now, you can read back the exit status of the command across the pipe.
Just hope xterm doesn't go closing all file descriptors on you; otherwise you're out of luck and you'll have to make a temporary fifo (via mkfifo) somewhere in the filesystem to achieve the same result.
Note that the number 99 was arbitrary; anything other than 0, 1, or 2 should work.
There's no trivial way; a convention often used is that the fork()ed child will report the error and exit(-1) (or exit(255)) in the specific case where the exec() fails, and most commands avoid using that for their own failure modes.
I'm trying to create a TAR archive from my program and then opening the archive for further processing. I have a 2 second delay between calling system() and open(). So far, it works fine, but I'm not sure why the 2 second delay is necessary or if it's the correct solution.
Without the delay, I get error code 2 (ENOENT "No such file or directory") from the open() call. My first thought was the filesystem wasn't updating itself fast enough and open() couldn't find the file. But what if the system is really busy? Do I need a longer delay? Should I loop until open() succeeds instead of the delay? Is the problem something completely different?
UPDATE
The root filesystem is EXT2. /tmp is mounted in RAM using TMPFS. I'm using tar to create an archive, not extract contents of one. Essentially, my program is supposed to create an archive of some log files and send them over the network (that's why I open the archive after creating it).
int return_value = system("/bin/tar -czf /tmp/logs.tar.gz /var/log/mylogs.log* &> /dev/null");
// error checks on return_value as described here: http://linux.die.net/man/2/wait
if(return_value != 0) {
return return_value;
}
//usleep(2000000);
return_value = open("/tmp/logs.tar.gz", O_RDONLY | O_LARGEFILE, 0);
// success or failure depending on whether there's a delay or not
You could even avoid running an external tar command by using libtar directly in your program.
ADDED
And you should show us your program. I'm pretty sure that if the call to system just extracted some file thru tar, it is available just after a successful system call, e.g. something like:
int err = system("/bin/tar xf /tmp/foo.tar bar");
int fd = -1;
if (err == 0)
fd = open("bar", O_RDONLY);
// fd is available
there is no reason to wait a few seconds in this code. You are probably doing more complex things, or you forgot to test the result of system
You think you are redirecting tar's output with "&>", but actually you are running it in the background, because system() happens to invoke a shell that doesn't support &> and so interprets it as "&" followed by ">". The delay causes your program to wait long enough that tar completes.
The fix is to modify your command to use syntax that your shell supports. Throwing the error output from tar is probably a mistake in any case.
Here's what I would try:
fork/exec tar yourself, and have your parent collect the tar-child. If system is introducing a race condition with the file system, taking control of the child process creating/reaping may help.
touch an empty file (fopen for writing and close) and then tar into into the new file.
Give tar the --verify option; the file has to exist in order to be verified :)
I am working on a linux daemon and having some issues with the stdin/stdout. Normally because of the nature of a daemon you do not have any stdin or stdout. However, I do have a function in my daemon that is called when the daemon runs for the first time to specify different parameters that are required for the daemon to run successfully. When this function is called the terminal becomes so sluggish that I have to launch a seperate shell and kill the daemon with top to get a responsive prompt back. Now I suspect that this has something to do with the forking process closing the stdin/stdout but I am not quite sure how I could work around this. If you guys could shed some light on the situation that would be most appreciated. Thanks.
Edit:
int main(argc, char *argv[]) {
/* setup signal handling */
/* check command line arguments */
pid_t pid, sid;
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
if(pid > 0){
exit(EXIT_SUCCESS);
}
sid = setsid();
if(sid < 0) {
exit(EXIT_FAILURE);
}
umask(027);
/* set syslogging */
/* do some logic to determine wether we are running the daemon for the first time and if we are call the one time function which uses fgets() to recieve some input */
while(1) {
/* do required work */
}
/* do some clean up procedures and exit */
return 0;
}
You guys mention using a config file. This is is exactly what I do to store the parameters recieved via input. However I still initially need to get these from the user via the stdin. The logic for determining whether we are running for the first time is based off of the existence of the config file.
Normally, the standard input of a daemon should be connected to /dev/null, so that if anything is read from standard input, you get an EOF immediately. Normally, standard output should be connected to a file - either a log file or /dev/null. The latter means all writes will succeed, but no information will be stored. Similarly, standard error should be connected to /dev/null or to a log file.
All programs, including daemons, are entitled to assume that stdin, stdout and stderr are appropriately opened file streams.
It is usually appropriate for a daemon to control where its input comes from and outputs go to. There is seldom occasion for input to come from other than /dev/null. If the code was written to survive without standard output or standard error (for example, it opens a standard log channel, or perhaps uses syslog(3)) then it may be appropriate to close stdout and stderr. Otherwise, it is probably appropriate to redirect them to /dev/null, while still logging messages to a log file. Alternatively, you can redirect both stdout and stderr to a log file - beware continuously growing log files.
Your sluggish-to-impossible response time might be because your program is not paying attention to EOF in a read loop somewhere. It might be prompting for user input on /dev/null, and reading a response from /dev/null, and not getting a 'y' or 'n' back, it tries again, which chews up your system horribly. Of course, the code is flawed in not handling EOF, and counting the number of times it gets an invalid response and stopping being silly after a reasonable number of attempts (16, 32, 64). The program should shut up shop sanely and safely if it expects a meaningful input and continues not to get it.
You guys mention using a config file. This is is exactly what I do to store the parameters recieved via input. However I still initially need to get these from the user via the stdin. The logic for determining whether we are running for the first time is based off of the existence of the config file.
Instead of reading stdin, have the user write the config file themselves; check for its existence before forking, and exit with an error if it doesn't. Include a sample config file with the daemon, and document its format in your daemon's manpage. You do have a manpage, yes? Your config file is textual, yes?
Also, your daemonization logic is missing a key step. After forking, but before calling setsid, you need to close fds 0, 1, and 2 and reopen them to /dev/null (do not attempt to do this with fclose and fopen). That should fix your sluggish terminal problem.
Your design is wrong. Daemon processes should not take input via stdin or deliver output to stdout/stderr. You'll close those descriptors as part of the daemonizing phase. Daemons should take configuration parameters from the command line, a config file, or both. If runtime-input is required you'll have to read a file, open a socket, etc., but the point of a daemon is that it should be able to run and do its thing without a user being present at the console.
If you want to run your program detached, use the shell: (setsid <command> &). Do not fork() inside your program, which will cause sysadmin nightmare.
Don't use syslog() nor redirect stdout or stderr.
Better yet, use a daemon manager such as daemon tools, runit, OpenRC and systemd, to daemonize your program for you.
Use a config file. Do not use STDIN or STDOUT with a daemon. Daemons are meant to run in the background with no user interaction.
If you insist on using stdin/keyboard input to fire up the daemon (e.g. to get some magic passphrase you wouldn't want to store in a file) then handle all I/O before the fork().
I have a project the requires the use of the exec family. My project consist of making an interactive shell. The shell will implement a few basic commands like cd, ls, echo, etc. I have been researching the use of exec, but have not found a useful site. Any suggested links would help.
int ret;
ret = execl ("/bin/ls", "ls", "-1", (char *)0);
How would i get the output of this operation to show on the screen?
doing
int fd = 1;
dup(fd);
close(fd);
gets the output to the screen.
The code you wrote works for me in a simple test program that does nothing else. Remember, when you call execl, the process retains all of the old file handles. So whatever stdout was when you call execl, it will be the same when the new binary is loaded. If you just want the output to go to the terminal, just make sure stdout goes to the terminal.
If you want to do I/O with another program, popen is good for this (as mgb mentioned). It will fork a new process, set up plumbing for you, call some variant of exec, and return a file handle you can use for communication.