-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpipe-spy.c
175 lines (147 loc) · 6.78 KB
/
pipe-spy.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/***********************************************************************\
Program to spy on a stdin/stdout pipe communication between two
processes, the caller and the target.
thoni56/Thomas Nilefalk - January 2020
Make the caller exec this program instead of the real target and
ensure that the TARGET variable points to the target using a full
path.
Then, when called, this program, the parent, will set up three
child processes, one spy for each of the downstream and upstream
communication flows and a third which will exec the real target.
The downstream and upstream childs will hook in to the stdin and
stdout of the parent and thus read from the output pipe of the
caller and write to the input pipe it has set up.
The parent process will setup pipes so that the spy childs can
hook their pipes to the exec'ed target process.
While propagating the communication the children will also copy
that to a logfile.
\**********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <stdbool.h>
#define READ_END 0
#define WRITE_END 1
FILE *logFile;
/* You need to define TARGET as a compile-time constant using -DTARGET=... */
//#define TARGET "path/to/some/executable"
int main(int argc, char **argv) {
bool trace = false; /* To trace, you must change this using
the debugger. We can't add an
option for this since we don't
control argc/argv, the caller does.
And we don't want to have to change
it... */
char logFileName[100];
char *fileName;
#ifdef TARGET
char *target = TARGET;
#else
char *target = getenv("TARGET");
#endif
fileName = getenv("LOGFILE");
if (fileName != NULL)
strcpy(logFileName, fileName);
else
sprintf(logFileName, "/tmp/pipespy%d.log", getpid());
logFile = fopen(logFileName, "w");
if (target == NULL) {
fprintf(logFile, "*** ERROR: NO pipe-spy TARGET DEFINED, neither compile-time or environment ***");
exit(-1);
}
/* Log arguments to the command on the first line in the log */
for (int a=0; a<argc; a++)
fprintf(logFile, "%s ", argv[a]);
fprintf(logFile, "\n");
fflush(logFile);
/* Create the pipes for the sub-spies to the target */
int downstream_pipe[2];
int upstream_pipe[2];
if (pipe(downstream_pipe) < 0) {
perror("pipe(downstream_pipe)");
_exit(-1);
}
if (pipe(upstream_pipe) < 0) {
close(downstream_pipe[READ_END]);
close(downstream_pipe[WRITE_END]);
perror("pipe(upstream_pipe)");
_exit(-1);
}
/* Fork a sub-spy to listen, log and propagate stdin */
pid_t downstream_pid = fork();
if (downstream_pid == 0) {
char buffer[10000];
if (trace) { fprintf(logFile, "** downstream child to fdopen\n"); fflush(logFile); }
FILE *toTarget = fdopen(downstream_pipe[WRITE_END], "w");
if (trace) { fprintf(logFile, "** downstream child did fdopen\n"); fflush(logFile); }
/* Close the pipe ends we don't use */
close(downstream_pipe[READ_END]);
close(upstream_pipe[READ_END]);
close(upstream_pipe[WRITE_END]);
/* Read from stdin which is the same as the parent has */
if (trace) { fprintf(logFile, "** downstream child to fgets\n"); fflush(logFile); }
while (fgets(buffer, 10000, stdin) != NULL) {
if (trace) { fprintf(logFile, "** downstream child did fgets\n"); fflush(logFile); }
fprintf(logFile, "->:%s", buffer); fflush(logFile);
fputs(buffer, toTarget); fflush(toTarget);
}
fprintf(logFile, "** downstream child got NULL\n");
_exit(1);
}
if (trace) { fprintf(logFile, "** parent forked downstream child to %d\n", downstream_pid); fflush(logFile); }
/* Fork a sub-spy to listen, log and propagate stdout */
pid_t upstream_pid = fork();
if (upstream_pid == 0) {
char buffer[10000];
if (trace) { fprintf(logFile, "** upstream child to fdopen\n"); fflush(logFile); }
FILE *fromTarget = fdopen(upstream_pipe[READ_END], "r");
if (trace) { fprintf(logFile, "** upstream child did fdopen\n"); fflush(logFile); }
/* Close the pipe ends we don't use */
close(downstream_pipe[READ_END]);
close(downstream_pipe[WRITE_END]);
close(upstream_pipe[WRITE_END]);
if (trace) { fprintf(logFile, "** upstream child to fgets\n"); fflush(logFile); }
while (fgets(buffer, 10000, fromTarget) != NULL) {
if (trace) { fprintf(logFile, "** upstream child did fgets\n"); fflush(logFile); }
fprintf(logFile, "<-:%s", buffer); fflush(logFile);
/* Write to stdout which is the same as the parent has */
fputs(buffer, stdout); fflush(stdout);
}
fprintf(logFile, "** upstream child got NULL\n"); fflush(logFile);
_exit(1);
}
if (trace) { fprintf(logFile, "** parent forked upstream child to %d\n", upstream_pid); fflush(logFile); }
/* Fork & exec the target with stdin & stdout pipes connected to
the upstream and downstream pipes */
pid_t target_pid = fork();
if (target_pid == 0) {
/* In the target, so... */
/* ... connect the stdin to the downstream pipes read end... */
if (trace) { fprintf(logFile, "** target child to dup2 on read end\n"); fflush(logFile); }
if (dup2(downstream_pipe[READ_END], STDIN_FILENO) == -1) exit(errno);
if (trace) { fprintf(logFile, "** target child did dup2 on read end\n"); fflush(logFile); }
/* ... the stdout to the upstream pipes write end... */
if (trace) { fprintf(logFile, "** target child to dup2 in write end\n"); fflush(logFile); }
if (dup2(upstream_pipe[WRITE_END], STDOUT_FILENO) == -1) exit(errno);
if (trace) { fprintf(logFile, "** target child did dup2 in write end\n"); fflush(logFile); }
/* ... and exec ... */
if (trace) { fprintf(logFile, "** target child to execv\n"); fflush(logFile); }
execv(target, argv);
/* ... if we get here execv failed... */
perror(target);
_exit(1);
}
if (trace) { fprintf(logFile, "** parent forked target to %d\n", target_pid); fflush(logFile); }
/* Parent need to close all pipes... */
close(downstream_pipe[READ_END]);
close(downstream_pipe[WRITE_END]);
close(upstream_pipe[READ_END]);
close(upstream_pipe[WRITE_END]);
int status;
waitpid(downstream_pid, &status, 0);
waitpid(upstream_pid, &status, 0);
waitpid(target_pid, &status, 0);
}