How to redirect output of an already running process [duplicate] - linux

This question already has answers here:
Redirect STDERR / STDOUT of a process AFTER it's been started, using command line?
(8 answers)
Closed 6 years ago.
Normally I would start a command like
longcommand &;
I know you can redirect it by doing something like
longcommand > /dev/null;
for instance to get rid of the output or
longcommand 2>&1 > output.log
to capture output.
But I sometimes forget, and was wondering if there is a way to capture or redirect after the fact.
longcommand
ctrl-z
bg 2>&1 > /dev/null
or something like that so I can continue using the terminal without messages popping up on the terminal.

See Redirecting Output from a Running Process.
Firstly I run the command cat > foo1 in one session and test that data from stdin is copied to the file. Then in another session I redirect the output.
Firstly find the PID of the process:
$ ps aux | grep cat
rjc 6760 0.0 0.0 1580 376 pts/5 S+ 15:31 0:00 cat
Now check the file handles it has open:
$ ls -l /proc/6760/fd
total 3
lrwx—— 1 rjc rjc 64 Feb 27 15:32 0 -> /dev/pts/5
l-wx—— 1 rjc rjc 64 Feb 27 15:32 1 -> /tmp/foo1
lrwx—— 1 rjc rjc 64 Feb 27 15:32 2 -> /dev/pts/5
Now run GDB:
$ gdb -p 6760 /bin/cat
GNU gdb 6.4.90-debian
[license stuff snipped]
Attaching to program: /bin/cat, process 6760
[snip other stuff that's not interesting now]
(gdb) p close(1)
$1 = 0
(gdb) p creat("/tmp/foo3", 0600)
$2 = 1
(gdb) q
The program is running. Quit anyway (and detach it)? (y or n) y
Detaching from program: /bin/cat, process 6760
The p command in GDB will print the value of an expression, an expression can be a function to call, it can be a system call… So I execute a close() system call and pass file handle 1, then I execute a creat() system call to open a new file. The result of the creat() was 1 which means that it replaced the previous file handle. If I wanted to use the same file for stdout and stderr or if I wanted to replace a file handle with some other number then I would need to call the dup2() system call to achieve that result.
For this example I chose to use creat() instead of open() because there are fewer parameter. The C macros for the flags are not usable from GDB (it doesn’t use C headers) so I would have to read header files to discover this – it’s not that hard to do so but would take more time. Note that 0600 is the octal permission for the owner having read/write access and the group and others having no access. It would also work to use 0 for that parameter and run chmod on the file later on.
After that I verify the result:
ls -l /proc/6760/fd/
total 3
lrwx—— 1 rjc rjc 64 2008-02-27 15:32 0 -> /dev/pts/5
l-wx—— 1 rjc rjc 64 2008-02-27 15:32 1 -> /tmp/foo3 <====
lrwx—— 1 rjc rjc 64 2008-02-27 15:32 2 -> /dev/pts/5
Typing more data in to cat results in the file /tmp/foo3 being appended to.
If you want to close the original session you need to close all file handles for it, open a new device that can be the controlling tty, and then call setsid().

You can also do it using reredirect (https://github.com/jerome-pouiller/reredirect/).
The command bellow redirects the outputs (standard and error) of the process PID to FILE:
reredirect -m FILE PID
The README of reredirect also explains other interesting features: how to restore the original state of the process, how to redirect to another command or to redirect only stdout or stderr.
The tool also provides relink, a script allowing to redirect the outputs to the current terminal:
relink PID
relink PID | grep usefull_content
(reredirect seems to have same features than Dupx described in another answer but, it does not depend on Gdb).

Dupx
Dupx is a simple *nix utility to redirect standard output/input/error of an already running process.
Motivation
I've often found myself in a situation where a process I started on a remote system via SSH takes much longer than I had anticipated. I need to break the SSH connection, but if I do so, the process will die if it tries to write something on stdout/error of a broken pipe. I wish I could suspend the process with ^Z and then do a
bg %1 >/tmp/stdout 2>/tmp/stderr
Unfortunately this will not work (in shells I know).
http://www.isi.edu/~yuri/dupx/

Screen
If process is running in a screen session you can use screen's log command to log the output of that window to a file:
Switch to the script's window, C-a H to log.
Now you can :
$ tail -f screenlog.2 | grep whatever
From screen's man page:
log [on|off]
Start/stop writing output of the current window to a file "screenlog.n" in the window's default directory, where n is the number of the current window. This filename can be changed with the 'logfile' command. If no parameter is given, the state of logging is toggled. The session log is appended to the previous contents of the file if it already exists. The current contents and the contents of the scrollback history are not included in the session log. Default is 'off'.
I'm sure tmux has something similar as well.

I collected some information on the internet and prepared the script that requires no external tool: See my response here. Hope it's helpful.

Related

Script command: separate input and output

I'm trying to monitor command execution on a shell.
I need to separate the input command, for example:
input:
ls -l /
output:
total 76
lrwxrwxrwx 1 root root 7 Aug 11 10:25 bin -> usr/bin
drwxr-xr-x 3 root root 4096 Aug 11 11:18 boot
drwxr-xr-x 17 root root 3200 Oct 11 11:10 dev
...
Also, I want to do the same if I open another shell, for example, after connection through ssh to another server.
I've been using script command to do this and it works just fine!
It logs all command input and output even if the shell changes (through ssh, or entering a msfconsole, for example).
Nevertheless, I found two main issues:
For my project, I need to separate (using a decoder) each command from the rest, also it would be awesome to be able to separate command input and output, for example:
cmd1. pwd ---> /var/
cmd2. echo "hello world" ---> "hello world"
....
Sometimes the script command could generate an output with garbage due to shell special characters (for colors, for example) which I would like to filter out.
So I've been thinking about this and I guess I could create a simple script that read from the file written by "script" command and processed the data.
Nevertheless, I'm not sure about what could be the best approach to do this.
I'm evaluating different solutions and I would like to know different proposals from the community.
Maybe I'm losing something and you know a better tool rather than script command or have some idea I've not considered.
Best regards,
A useful util for distinguishing stdout from stderr is annotate-output, (install the "devscripts" package), which sends stderr and stdin both to stdout along with helpful little prefixes. For example, let's try counting characters of a file that exists, plus one that doesn't exist:
annotate-output wc -c /bin/bash /bin/nosuchshell
Output:
00:29:06 I: Started wc -c /bin/bash /bin/nosuchshell
00:29:06 E: wc: /bin/nosuchshell: No such file or directory
00:29:06 O: 1099016 /bin/bash
00:29:06 O: 1099016 total
00:29:06 I: Finished with exitcode 1
That output could be parsed separately using sed, awk, or even a tee and a few greps.

Is it possible to "push" messages to an active bash terminal?

For example to inform the user about status of some task or to report an event. This should happen not only at login but through the life of the session.
It is okay if the message display gets deferred till the user exits an application and comes back to shell.
A generic Linux solution is preferred but something that works only on Ubuntu is fine too.
First, some background.
Every terminal has an associated terminal device file which you can get using the tty command
% tty
/dev/pts/2
If a program writes into this file, it will appear on the terminal. Here's an example
% echo "Hi there" > /dev/pts/2
Hi there
Now, whether you can write into this file or not depends on the permissions of the file
% ls -l /dev/pts/2
crw------- 1 noufal tty 136, 2 Mar 4 10:32 /dev/pts/2
This means that I can write into it but no one else can (which is reasonable since I don't want others to write onto my terminal). Commands like wall mentioned by Prashanth write into the terminal files of all the users regardless of permission since it runs with group privileges of the tty user and all the terminal device files have the same group too. Notice the s in the execute permission of the group below.
% ls -l /usr/bin/wall
-rwxr-sr-x 1 root tty 27K Mar 7 2018 /usr/bin/wall
Individually, you can turn on or turn off broadcasts to your terminal using the `
% mesg
is n
% ls -l /dev/pts/2
crw------- 1 noufal tty 136, 2 Mar 4 10:35 /dev/pts/2
% mesg y
% ls -l /dev/pts/2
crw--w---- 1 noufal tty 136, 2 Mar 4 10:35 /dev/pts/2
Now, to answer your question, if I write into the tty file of a given shell, I can display stuff into that terminal regardless of what's running there. The caveat is that you will either need to be the same user as the one running the shell or your program will have to run as sgid tty.
Here is a simple commented shell function that will display a running clock on a given terminal.
function dclock () {
# Both the tput and date redirect their output to the given tty
# file so that the display occurs over there.
while true
do
tput cup 0 50 > $1 # Position the cursor at row 0 column 50
date > $1 # Print the date
sleep 1
done
}
You can open a different terminal (T), find the tty file using the tty command and then run dclock /dev/pts/whatever in your current terminal to see a clock appear in T.
The wall command can send messages to users.
I generally use it to notify users of scheduled Maintanence.
http://man7.org/linux/man-pages/man1/wall.1.html

Reading value of `stdout` and `stderr`

I am trying to read value of stdout and stderr using the following commands:
cat /dev/stderr
cat /dev/stdout
But, the command keeps running.
Use a FIFO Instead
Technically, /dev/stdout and /dev/stderr are really file descriptors, not FIFOs or named pipes. On my system, they're actually just symlinks to /dev/fd/1 and /dev/fd/2. Those descriptors are typically linked to your TTY or PTY. So, you can't really read from them the way you're trying to do.
What you probably want is the mkfifo utility. For example, to write to standard error, and then read it from another command or script:
# Create a named pipe.
$ mkfifo error
# See what a named pipe looks like in the filesystem.
$ ls -l error
prw-r--r-- 1 user staff 0 May 13 01:47 error|
# In a subshell: echo to stdout, duplicate stdout to stderr,
# write stderr to the error FIFO. Background to avoid blocking.
# Then read from the FIFO until empty, which ends both tasks.
$ ( echo foo >&2 2> error & ); cat error
foo
As a more verbose but less contorted example, consider this:
$ ruby -e 'STDERR.puts "Some error."' 2> error & cat error
[1] 32458
Some error.
[1]+ Done ruby -e 'STDERR.puts "Some error."' 2> error
In this example, Ruby uses standard error to write a string to the error FIFO we created earlier. The write happens in the background, but blocks until the FIFO is emptied by the cat command. Once the FIFO is emptied, the background job completes.
The FIFO is just a special type of file, so you can remove it when you're done with rm error.
I don't really know what you mean by reading the value of stderr or stdin, but I can tell you why that cat /dev/stderr would keep running: it's waiting for data to read from the fd.
On the systems I can test this on, both output fd's are connected to the terminal just like stdin is, and reading from them works just fine. On Linux, we can view this with:
$ ls -l /proc/self/fd
lrwx------ 1 nobody nogroup 64 May 13 21:47 0 -> /dev/pts/1
lrwx------ 1 nobody nogroup 64 May 13 21:47 1 -> /dev/pts/1
lrwx------ 1 nobody nogroup 64 May 13 21:47 2 -> /dev/pts/1
lr-x------ 1 nobody nogroup 64 May 13 21:47 3 -> /proc/44664/fd
The permission bits at the start of the line show that all of the fd's 0 to 2 are opened for both reading and writing.
Reading from them works in practice, too (input in italics):
$ cat /dev/stderr
foo
foo
$ read -u 2 ; echo "reply: $REPLY"
asdf
reply: asdf
Though really, it might be better to just open /dev/tty and read from there if we want to interact on the terminal even when there are redirections in place. (That's what ssh does to ask the password, for example.)

How to redirect input from another tty?

When i run cat - in say /dev/pts/2 and try to write to its input from another tty with echo foo > /dev/pts/2 or echo foo > /proc/(pid of cat)/fd/0 it just prints foo in pts/2, cat doesn't repeat it . why?
How to send input to cat from another tty so it also repeat it ?
Each and every terminal has a file for it, in /dev/pts/.
$ ps
to determine which terminal you are on. Example: I am on terminal 3
PID TTY TIME CMD
1477 pts/3 00:00:00 ps
26511 pts/3 00:00:01 bash
Than just redirect your output to that terminal.
cat foo > /dev/pts/3
Make a first in first out pipe on the second terminal, the one you'd like to display the text on
mkfifo --mode=600 /tmp/pipe
Redirect the command to that pipe on the first terminal
cat foo > /tmp/pipe
I think there is a fundamental misunderstanding here: you cannot inject content to another TTY's input stream (unless you own the master).
You can, however, call cat /dev/pts/0 to read from another TTY's input stream, but beware that you will be fighting whatever process is already there.

setuid on an executable doesn't seem to work

I wrote a small C utility called killSPR to kill the following processes on my RHEL box. The idea is for anyone who logs into this linux box to be able to use this utility to kill the below mentioned processes (which doesn't work - explained below).
cadmn#rhel /tmp > ps -eaf | grep -v grep | grep " SPR "
cadmn 5822 5821 99 17:19 ? 00:33:13 SPR 4 cadmn
cadmn 10466 10465 99 17:25 ? 00:26:34 SPR 4 cadmn
cadmn 13431 13430 99 17:32 ? 00:19:55 SPR 4 cadmn
cadmn 17320 17319 99 17:39 ? 00:13:04 SPR 4 cadmn
cadmn 20589 20588 99 16:50 ? 01:01:30 SPR 4 cadmn
cadmn 22084 22083 99 17:45 ? 00:06:34 SPR 4 cadmn
cadmn#rhel /tmp >
This utility is owned by the user cadmn (under which these processes run) and has the setuid flag set on it (shown below).
cadmn#rhel /tmp > ls -l killSPR
-rwsr-xr-x 1 cadmn cusers 9925 Dec 17 17:51 killSPR
cadmn#rhel /tmp >
The C code is given below:
/*
* Program Name: killSPR.c
* Description: A simple program that kills all SPR processes that
* run as user cadmn
*/
#include <stdio.h>
int main()
{
char *input;
printf("Before you proceed, find out under which ID I'm running. Hit enter when you are done...");
fgets(input, 2, stdin);
const char *killCmd = "kill -9 $(ps -eaf | grep -v grep | grep \" SPR \" | awk '{print $2}')";
system(killCmd);
return 0;
}
A user (pmn) different from cadmn tries to kill the above-mentioned processes with this utility and fails (shown below):
pmn#rhel /tmp > ./killSPR
Before you proceed, find out under which ID I'm running. Hit enter when you are done...
sh: line 0: kill: (5822) - Operation not permitted
sh: line 0: kill: (10466) - Operation not permitted
sh: line 0: kill: (13431) - Operation not permitted
sh: line 0: kill: (17320) - Operation not permitted
sh: line 0: kill: (20589) - Operation not permitted
sh: line 0: kill: (22084) - Operation not permitted
pmn#rhel /tmp >
While the user waits to hit enter above, the process killSPR is inspected and is seen to be running as the user cadmn (shown below) despite which killSPR is unable to terminate the processes.
cadmn#rhel /tmp > ps -eaf | grep -v grep | grep killSPR
cadmn 24851 22918 0 17:51 pts/36 00:00:00 ./killSPR
cadmn#rhel /tmp >
BTW, none of the main partitions have any nosuid on them
pmn#rhel /tmp > mount | grep nosuid
pmn#rhel /tmp >
The setuid flag on the executable doesn't seem to have the desired effect. What am I missing here? Have I misunderstood how setuid works?
First and foremost, setuid bit simply allows a script to set the uid. The script still needs to call setuid() or setreuid() to run in the the real uid or effective uid respectively. Without calling setuid() or setreuid(), the script will still run as the user who invoked the script.
Avoid system and exec as they drop privileges for security reason. You can use kill() to kill the processes.
Check These out.
http://linux.die.net/man/2/setuid
http://man7.org/linux/man-pages/man2/setreuid.2.html
http://man7.org/linux/man-pages/man2/kill.2.html
You should replace your system call with exec call. Manual for system say's it drops privileges when run from suid program.
The reason is explained in man system:
Do not use system() from a program with set-user-ID or set-group-ID
privileges, because strange values for some environment variables might
be used to subvert system integrity. Use the exec(3) family of func‐
tions instead, but not execlp(3) or execvp(3). system() will not, in
fact, work properly from programs with set-user-ID or set-group-ID
privileges on systems on which /bin/sh is bash version 2, since bash 2
drops privileges on startup. (Debian uses a modified bash which does
not do this when invoked as sh.)
If you replace system with exec you will need to be able to use shell syntax unless you call /bin/sh -c <shell command>, this is what is system actually doing.
Check out this link on making a shell script a daemon:
Best way to make a shell script daemon?
You might also want to google some 'linux script to service', I found a couple of links on this subject.
The idea is that you wrap a shell script that has some basic stuff in it that allows a user to control a program run as another user by calling a 'service' type script instead. For example, you could wrap up /usr/var/myservice/SPRkiller as a 'service' script that could then just be called as such from any user: service SPRkiller start, then SPRkiller would run, kill the appropriate services (assuming the SPR 'program' is run as a non-root user).
This is what it sounds like you are trying to achieve. Running a program (shell script/C program/whatever) carries the same user restrictions on it no matter what (except for escalation bugs/hacks).
On a side note, you seem to have a slight misunderstanding of user rights on Linux/Unix as well as what certain commands and functions do. If a user does not have permissions to do a certain action (like kill the process of another user), then calling setuid on the program you want to kill (or on kill itself) will have no effect because the user does not have permission to another users 'space' without super user rights. So even if you're in a shell script or a C program and called the same system command, you will get the same effect.
http://www.linux.com/learn/ is a great resource, and here's a link for file permissions
hope that helps

Resources