1 /*
2     Terminal Mixer - multi-point multi-user access to terminal applications
3     Copyright (C) 2007  Lluís Batlle i Rossell
4 
5     Please find the license in the provided COPYING file.
6 */
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <assert.h>
10 #include <errno.h>
11 #include <sys/select.h>
12 
13 #include "main.h"
14 #include "filter.h"
15 #include "handlers.h"
16 
17 /* it uses app_stdin, app_stdout, app_stderr.
18  * They have value -1 if not opened. */
19 
20 static struct FilterRules *fr;
21 
write_newline_cb(struct FilterRules * myfr,const struct FFilter * ff,char * obuf,int * olen,const char * ibuf,int pos)22 void write_newline_cb(struct FilterRules *myfr,
23         const struct FFilter *ff, char *obuf, int *olen,
24         const char *ibuf, int pos)
25 {
26     obuf[(*olen)++] = '\n';
27 }
28 
app_control_start()29 void app_control_start()
30 {
31     struct FFilter *ff;
32     fr = new_filter_rules();
33 
34     /* We don't pay attention to any telnet command */
35     ff = new_ftelnet();
36     add_ffilter(fr, ff);
37 
38     /* The raw terminal will send \r,
39      * telnet will send \r\0. If we filter all \0, we're fine.
40      * I used to change \r to \n here... It works better if I don't. */
41 
42     /* We should trim the telnet's no-operation characters '\0'. */
43     ff = new_fstring_len("\0", 1);
44     add_ffilter(fr, ff);
45 }
46 
app_control_shutdown()47 void app_control_shutdown()
48 {
49 }
50 
app_control_prepare_read_fdset(fd_set * read_set,int * maxfd)51 void app_control_prepare_read_fdset(fd_set *read_set, int *maxfd)
52 {
53     if (app_stdout != -1)
54     {
55         debugmsg("Considering app_stdout %i", app_stdout);
56         FD_SET(app_stdout, read_set);
57         *maxfd = max(*maxfd, app_stdout);
58     }
59 
60     if (app_stderr != -1 && app_stdout != app_stderr)
61     {
62         debugmsg("Considering app_stderr %i", app_stderr);
63         FD_SET(app_stderr, read_set);
64         *maxfd = max(*maxfd, app_stderr);
65     }
66 }
67 
68 /* Return -1, finished stdout/stderr. Else, return 0. */
app_control_process_read_fdset(fd_set * read_set)69 int app_control_process_read_fdset(fd_set *read_set)
70 {
71     if (app_stdout != -1 && FD_ISSET(app_stdout, read_set))
72     {
73         int res;
74         debugmsg("Read app_stdout %i", app_stdout);
75         res = read(app_stdout, stream_buffer, stream_buffer_size);
76         if (res == -1 && errno == EIO)
77         {
78             /* I've noticed that when the app dies, read() returns EIO */
79             app_stdout = -1;
80             app_stdin = -1;
81             return -1;
82         }
83         if (res == -1 )
84             error("Error reading from app.");
85         if (res == 0)
86         {
87             debugmsg("Closing app_stdout %i", app_stdout);
88             close(app_stdout);
89             if (!command_line.s_param.nohup)
90                 close(1);
91             app_stdout = -1;
92             if (app_stdout == app_stderr || app_stderr == -1)
93             {
94                 app_stderr = -1;
95                 return -1;
96             }
97         } else
98         {
99             hex_dump("from app", stream_buffer, res);
100             if (!command_line.s_param.nohup)
101                 write(1, stream_buffer, res);
102             if (command_line.s_param.serve_unix)
103                 s_unix_send_to_connected(stream_buffer, res);
104             if (command_line.s_param.serve_tcp)
105                 s_tcp_send_to_connected(stream_buffer, res);
106 #ifdef linux
107             if (command_line.s_param.serve_eth)
108                 s_eth_send_to_connected(stream_buffer, res);
109 #endif /* linux */
110         }
111     }
112     if (app_stderr != -1 && app_stdout != app_stderr &&
113             FD_ISSET(app_stderr, read_set))
114     {
115         int res;
116         debugmsg("Read app_stderr %i", app_stdout);
117         res = read(app_stderr, stream_buffer, stream_buffer_size);
118         if (res == 0)
119         {
120             debugmsg("Closing app_stderr %i", app_stderr);
121             close(app_stderr);
122             close(2); /* MOVE */
123             app_stderr = -1;
124             if (app_stdout == -1)
125                 return -1;
126         } else
127         {
128             if (!command_line.s_param.nohup)
129                 write(2, stream_buffer, res);
130 
131             if (command_line.s_param.serve_unix)
132                 s_unix_send_to_connected(stream_buffer, res);
133             if (command_line.s_param.serve_tcp)
134                 s_tcp_send_to_connected(stream_buffer, res);
135 #ifdef linux
136             if (command_line.s_param.serve_eth)
137                 s_eth_send_to_connected(stream_buffer, res);
138 #endif /* linux */
139         }
140     }
141     return 0;
142 }
143 
app_control_local_send_to_stdin(const char * buffer,size_t size)144 void app_control_local_send_to_stdin(const char *buffer, size_t size)
145 {
146     if (size == 0)
147     {
148         close(app_stdin);
149         if (app_stdout == app_stdin)
150           app_stdout = -1;
151         if (app_stderr == app_stdin)
152           app_stderr = -1;
153     }
154     else
155     {
156         hex_dump("from local to app", buffer, size);
157         write(app_stdin, buffer, size);
158     }
159 }
160 
write_data_to_app_stdin(const char * buffer,size_t size)161 static void write_data_to_app_stdin(const char *buffer, size_t size)
162 {
163     if (size > 0)
164     {
165         hex_dump("to app", buffer, size);
166         if( !command_line.s_param.nohup &&
167                 command_line.s_param.echo_in_local_terminal)
168             write(1, buffer, size);
169         write(app_stdin, buffer, size);
170     }
171 }
172 
app_control_remote_send_to_stdin(const char * buffer,size_t size)173 void app_control_remote_send_to_stdin(const char *buffer, size_t size)
174 {
175     hex_dump("from client prefilter", buffer, size);
176     if (command_line.s_param.client_can_write)
177     {
178         int osize;
179         if (size > 0)
180         {
181             filter_stream(fr, ostream_buffer, &osize,
182                     buffer, size);
183             if (osize > 0)
184                 write_data_to_app_stdin(ostream_buffer, osize);
185         } else if (size == 0)
186         {
187             filter_flush(fr, ostream_buffer, &osize);
188             if (osize > 0)
189                 write_data_to_app_stdin(ostream_buffer, osize);
190         }
191 
192         if (size == 0 && command_line.s_param.client_may_close_app_stdin)
193         {
194             close(app_stdin);
195             if (app_stdout == app_stdin)
196               app_stdout = -1;
197             if (app_stderr == app_stdin)
198               app_stderr = -1;
199         }
200     }
201 }
202 
app_control_avoid_sending(fd_set * read_set)203 void app_control_avoid_sending(fd_set *read_set)
204 {
205 }
206