How to undo effects of freopen when launching a shell?

447 Views Asked by At

A piece of code ties stdout to a file in C using freopen. After this piece of code, a script is executed which launches a shell. The problem is now that all stdout output is going into that file, so any commands run in that shell are being placed in that file.

I've arrived at this script that launches the shell:

/bin/sh 1>&2 2>&1

Although it prints to the screen, I am not sure how I can verify that it is actually printing to stdout vs stderr. Can someone verify that I've gotten the IO redirection right?

EDIT: I do not have access to the program that calls freopen, just the script to run.

1

There are 1 best solutions below

5
On BEST ANSWER

Your shell fragment:

  • Makes standard output (1) go to the place where standard error (2) is currently going; and
  • Makes standard error (2) go to the place where standard output (1) is currently going.

The second operation is not needed.

Original answer

More seriously, if the piece of code is invoked with its standard error going to a file, then the shell's output is going to go to the same file. If you really want it to go to the same place as the original standard output, you need to preserve the original standard output before redirecting with freopen(). You'd do that with:

int fd1 = dup(FILENO_STDOUT);

...now do freopen...


dup2(fd1, FILENO_STDOUT);
close(fd1);

...now launch shell...without I/O redirection

Revised answer

Given the scenario:

  • A login shell is used to run a program Program-A (for which you don't have the source, and which uses freopen() on standard output)
  • Program-A runs Program-B (which is a shell script that ends up launching an interactive shell, using the /bin/sh 1>&2 2>&1 notation)

And assuming you want the standard output of the interactive shell to go to the original standard output, then you can do this with the following code:

launch.sh

Runs Program-A with extra I/O redirections and environment variables set:

set -x
fd -n 32
exec 3>&1
echo "$0: $@"
fd -n 32
FD_COPY_STDIN=3 exec ./Program-A "$@"

The fd program prints o for a file descriptor that's open and a - for a file descriptor that's closed. The -n 32 specifies 'print status for first 32 file descriptors' (by default, it prints for all possible file descriptors, but hundreds of lines consisting of 64 dashes is very boring to read).

Program-A

Shell script simulating your non-modifiable program. It runs Program-B after changing standard output to go to a file (standard.output).

set -x
echo "$0: $@"
# Simulate freopen("./standard.output", "w", stdout);
exec 1> ./standard.output
echo "$0: $@"
fd -n 32
exec ./Program-B "$@"

(You could, if you preferred, remove the last exec and then echo something after Program-B exits.)

Program-B

This is your revised script — the program run by Program-A:

echo "$0: $@"
fd -n 32

exec 1>&${FD_COPY_STDOUT:-3}
exec 3>&- 2>&1
echo "$0: $@"
fd -n 32
exec ${SHELL} -i

(Again, it would be possible to delete the final exec and do something else afterwards in the script.)

The -i option invokes an interactive shell. The 2>&1 redirects standard error to the same place that standard output is going. The interactive shell prompts on standard error (as I found out again the hard way — though I'd come across the behaviour before).

Example runs

The isodate command is equivalent to date +'%Y-%m-%d %H:%M:%S'. My standard prompt on machine osiris is Osiris JL:.

Without standard error redirected to a file:

Osiris JL: ls
launch.sh  Program-A  Program-B
Osiris JL: bash launch.sh $(isodate)
+ fd -n 32
ooo-----------------------------
+ exec
+ echo 'launch.sh: 2014-05-20' 17:03:59
launch.sh: 2014-05-20 17:03:59
+ fd -n 32
oooo----------------------------
+ FD_COPY_STDIN=3
+ exec ./Program-A 2014-05-20 17:03:59
+ echo '/home/jleffler/soq/New/Program-A: 2014-05-20' 17:03:59
/home/jleffler/soq/New/Program-A: 2014-05-20 17:03:59
+ exec
+ echo '/home/jleffler/soq/New/Program-A: 2014-05-20' 17:03:59
+ fd -n 32
+ exec ./Program-B 2014-05-20 17:03:59
/home/jleffler/soq/New/Program-B: 2014-05-20 17:03:59
ooo-----------------------------
jleffler@osiris:~/soq/New$ ls -l
total 16
-rwxr----- 1 jleffler eng 100 May 20 17:02 launch.sh
-rwxr----- 1 jleffler eng 158 May 20 17:02 Program-A
-rwxr----- 1 jleffler eng 121 May 20 17:02 Program-B
-rw-r----- 1 jleffler eng 208 May 20 17:03 standard.output
jleffler@osiris:~/soq/New$ exit
exit
Osiris JL:

With standard error redirected to a file:

Osiris JL: bash launch.sh $(isodate) 2>standard.error
ooo-----------------------------
launch.sh: 2014-05-20 17:05:11
oooo----------------------------
/home/jleffler/soq/New/Program-A: 2014-05-20 17:05:11
/home/jleffler/soq/New/Program-B: 2014-05-20 17:05:11
ooo-----------------------------
jleffler@osiris:~/soq/New$ ls -l
total 20
-rwxr----- 1 jleffler eng 100 May 20 17:02 launch.sh
-rwxr----- 1 jleffler eng 158 May 20 17:02 Program-A
-rwxr----- 1 jleffler eng 121 May 20 17:02 Program-B
-rw-r----- 1 jleffler eng 343 May 20 17:05 standard.error
-rw-r----- 1 jleffler eng 208 May 20 17:05 standard.output
jleffler@osiris:~/soq/New$ exit
exit
Osiris JL: cat standard.error
+ fd -n 32
+ exec
+ echo 'launch.sh: 2014-05-20' 17:05:11
+ fd -n 32
+ FD_COPY_STDIN=3
+ exec ./Program-A 2014-05-20 17:05:11
+ echo '/home/jleffler/soq/New/Program-A: 2014-05-20' 17:05:11
+ exec
+ echo '/home/jleffler/soq/New/Program-A: 2014-05-20' 17:05:11
+ fd -n 32
+ exec ./Program-B 2014-05-20 17:05:11
Osiris JL: cat standard.output
/home/jleffler/soq/New/Program-A: 2014-05-20 17:05:11
oooo----------------------------
/home/jleffler/soq/New/Program-B: 2014-05-20 17:05:11
oooo----------------------------
Osiris JL: