1 /* filter.c : spawning a filter and using it to re-write input, output, history and
2  * (C) 2000-2007 Hans Lub
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License , or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; see the file COPYING.  If not, write to
16  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  *  You may contact the author by e-mail:  hanslub42@gmail.com
19  */
20 
21 
22 
23 /* A filter is an external program run by rlwrap in order to examine
24    and possibly re-write user input, command output, prompts, history
25    entries and completion requests.  There can be at most one filter,
26    but this can be a pipeline ('pipeline filter1 : filter2')
27 
28    The filter communicates with rlwrap by reading and writing messages
29    on two pipes.
30 
31    A message is a byte sequence as follows:
32 
33    Tag     1 byte (can be TAG_INPUT, TAG_OUTPUT, TAG_HISTORY,
34    TAG_COMPLETION, TAG_PROMPT, TAG_OUTPUT_OUT_OF_BAND, TAG_ERROR)
35    Length  4 bytes (length of text + closing newline)
36    Text    <Length> bytes
37    '\n'    (so that the filter can be line buffered without
38    hanging rlwrap)
39 
40 
41    Communication is synchronous: after sending a message (and only
42    then) rlwrap waits for an answer, which must have the same tag, but
43    may be preceded by one or more "out of band" messages.  If the
44    filter is slow or hangs, rlwrap does the same. A filter can (and
45    should) signal an error by using TAG_ERROR (which will terminate
46    rlwrap). Filter output on stderr is displayed normally, but will
47    mess up the display.
48 
49    Length may be 0. (Example: If we have a prompt-less command, rlwrap
50    will send an empty TAG_PROMPT message, and the filter can send a
51    fancy prompt back. The converse is also possible, of course)
52 
53    A few environment variables are used to inform the filter about
54    file descriptors etc.
55 
56 */
57 
58 
59 
60 #include "rlwrap.h"
61 
62 
63 
64 
65 static int filter_input_fd = -1;
66 static int filter_output_fd = -1;
67 pid_t filter_pid = 0;
68 static int expected_tag = -1;
69 
70 
71 
72 
73 static char*read_from_filter(int tag);
74 static void write_message(int fd, int tag, const char *string, const char *description);
75 static void write_to_filter(int tag, const char *string);
76 static char* tag2description(int tag);
77 static char *read_tagless(void);
78 void handle_out_of_band(int tag, char *message);
79 
80 
81 
mypipe(int filedes[2])82 static void mypipe(int filedes[2]) {
83   int retval;
84   retval = pipe(filedes);
85   if (retval < 0)
86     myerror(FATAL|USE_ERRNO, "Couldn't create pipe");
87 }
88 
89 
spawn_filter(const char * filter_command)90 void spawn_filter(const char *filter_command) {
91   int input_pipe_fds[2];
92   int output_pipe_fds[2];
93 
94   mypipe(input_pipe_fds);
95   filter_input_fd = input_pipe_fds[1]; /* rlwrap writes filter input to this */
96 
97   mypipe(output_pipe_fds);
98   filter_output_fd = output_pipe_fds[0]; /* rlwrap  reads filter output from here */
99   DPRINTF1(DEBUG_FILTERING, "preparing to spawn filter <%s>", filter_command);
100   assert(!command_pid || signal_handlers_were_installed);  /* if there is a command, then signal handlers are installed */
101 
102   fflush(NULL);
103   if ((filter_pid = fork()) < 0)
104     myerror(FATAL|USE_ERRNO, "Cannot spawn filter '%s'", filter_command);
105   else if (filter_pid == 0) { /* child */
106     int signals_to_allow[] = {SIGPIPE, SIGCHLD, SIGALRM, SIGUSR1, SIGUSR2, 0};
107     char **argv;
108 
109     i_am_filter = TRUE;
110     if (debug)
111        my_fopen(&debug_fp, DEBUG_FILENAME, "a+", "debug log");
112     unblock_signals(signals_to_allow);  /* when we run a pager from a filter we want to catch these */
113 
114 
115     DEBUG_RANDOM_SLEEP;
116     /* set environment for filter (it needs to know at least the file descriptors for its input and output) */
117     DPRINTF1(DEBUG_FILTERING, "getenv{RLWRAP_FILTERDIR} = <%s>", strifnull(getenv("RLWRAP_FILTERDIR")));
118 
119     if ((! getenv("RLWRAP_FILTERDIR")) || (! *getenv("RLWRAP_FILTERDIR")))
120       mysetenv("RLWRAP_FILTERDIR", add2strings(DATADIR,"/rlwrap/filters"));
121     mysetenv("PATH", add3strings(getenv("RLWRAP_FILTERDIR"),":",getenv("PATH")));
122     mysetenv("RLWRAP_VERSION", VERSION);
123     mysetenv("RLWRAP_COMMAND_PID",  as_string(command_pid));
124     mysetenv("RLWRAP_COMMAND_LINE", command_line);
125     if (impatient_prompt)
126       mysetenv("RLWRAP_IMPATIENT", "1");
127     mysetenv("RLWRAP_INPUT_PIPE_FD", as_string(input_pipe_fds[0]));
128     mysetenv("RLWRAP_OUTPUT_PIPE_FD", as_string(output_pipe_fds[1]));
129     mysetenv("RLWRAP_MASTER_PTY_FD", as_string(master_pty_fd));
130     mysetenv("RLWRAP_BREAK_CHARS", rl_basic_word_break_characters);
131     mysetenv("RLWRAP_DEBUG", as_string(debug));
132 
133 
134     close(filter_input_fd);
135     close(filter_output_fd);
136 
137     /* @@@TODO: split the command in words (possibly quoted when containing spaces). DONT use the shell (|, < and > are never used on filter command lines */
138     if (scan_metacharacters(filter_command, "'|\"><"))  { /* if filter_command contains shell metacharacters, let the shell unglue them */
139       char *exec_command = add3strings("exec", " ", filter_command);
140       argv = list4("sh", "-c", exec_command, NULL);
141       DPRINTF1(DEBUG_FILTERING, "exec_command = <%s>", exec_command);
142 
143     } else {                                              /* if not, split and feed to execvp directly (cheaper, better error message) */
144       argv = split_with(filter_command, " ");
145     }
146     assert(argv[0]);
147     if(execvp(argv[0], argv) < 0) {
148       char *sorry = add3strings("Cannot exec filter '", argv[0], add2strings("': ", strerror(errno)));
149       write_message(output_pipe_fds[1], TAG_ERROR, sorry, "to stdout"); /* this will kill rlwrap */
150       mymicrosleep(100 * 1000); /* 100 sec for rlwrap to go away should be enough */
151       exit (-1);
152     }
153     assert(!"not reached");
154 
155   } else { /* parent */
156     DEBUG_RANDOM_SLEEP;
157     mysignal(SIGPIPE, SIG_IGN, NULL); /* ignore SIGPIPE - we have othere ways to deal with filter death */
158     DPRINTF1(DEBUG_FILTERING, "spawned filter with pid %d", filter_pid);
159     close (input_pipe_fds[0]);
160     close (output_pipe_fds[1]);
161   }
162 }
163 
164 
kill_filter()165 void kill_filter()  {
166   int status;
167   assert (filter_pid && filter_input_fd);
168   close(filter_input_fd); /* filter will see EOF and should exit  */
169   myalarm(40); /* give filter 0.04seconds to go away */
170   if(!filter_is_dead &&                     /* filter's SIGCHLD hasn't been caught  */
171      waitpid(filter_pid, &status, WNOHANG) < 0 && /* interrupted  .. */
172      WTERMSIG(status) == SIGALRM) {         /*  .. by alarm (and not e.g. by SIGCHLD) */
173      myerror(WARNING|NOERRNO, "filter didn't die - killing it now");
174   }
175   if (filter_pid)
176     kill(filter_pid, SIGKILL); /* do this as a last resort */
177   myalarm(0);
178 }
179 
180 
181 
filters_last_words()182 char *filters_last_words() {
183   assert (filter_is_dead);
184   return read_from_filter(TAG_OUTPUT);
185 }
186 
filter_is_interested_in(int tag)187 int filter_is_interested_in(int tag) {
188   static char *interests = NULL;
189   assert(tag <= MAX_INTERESTING_TAG);
190   if (!interests) {
191     char message[MAX_INTERESTING_TAG + 2];
192     int i;
193     mymicrosleep(500); /* Kludge - shouldn't the filter talk first - so we know it's alive? */
194     for (i=0; i <= MAX_INTERESTING_TAG; i++)
195       message[i] = 'n';
196     message[i] = '\0';
197     interests = pass_through_filter(TAG_WHAT_ARE_YOUR_INTERESTS, message);
198     if(interests[TAG_SIGNAL] == 'y')
199       myerror(WARNING|NOERRNO, "this filter handles signals, which means that signals are blocked during filter processing\n"
200               "if the filter hangs, you won't be able to interrupt with e.g. CTRL-C (use kill -9 %d instead)  ", getpid());
201 
202   }
203   return (interests[tag] == 'y');
204 }
205 
206 static int user_frustration_signals[] = {SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGALRM};
207 
208 
pass_through_filter(int tag,const char * buffer)209 char *pass_through_filter(int tag, const char *buffer) {
210   char *filtered;
211 
212   assert(!out_of_band(tag));
213   if (filter_pid ==0 || (tag <  MAX_INTERESTING_TAG && !filter_is_interested_in(tag)))
214     return mysavestring(buffer);
215 
216   if (tag == TAG_WHAT_ARE_YOUR_INTERESTS ||    /* only evaluate next alternative if interests are known                                                       */
217       !filter_is_interested_in(TAG_SIGNAL))    /* signal handling filters will get an "unexpected tag" error when the signal arrives during filter processing */
218     unblock_signals(user_frustration_signals); /* allow users to use CTRL-C, but only after uninterruptible_msec                                              */
219 
220   DPRINTF3(DEBUG_FILTERING, "to filter (%s, %d bytes) %s", tag2description(tag), (int) strlen(buffer), M(buffer));
221   write_to_filter((expected_tag = tag), buffer);
222   filtered = read_from_filter(tag);
223   DPRINTF3(DEBUG_FILTERING, "from filter (%s, %d bytes) %s",  tag2description(tag), (int) strlen(filtered), M(filtered));
224 
225   block_all_signals();
226 
227   return filtered;
228 }
229 
230 
231 
232 
read_from_filter(int tag)233 static char *read_from_filter(int tag) {
234   uint8_t  tag8;
235   DEBUG_RANDOM_SLEEP;
236   assert (!out_of_band(tag));
237 
238 
239   while (read_patiently2(filter_output_fd, &tag8, sizeof(uint8_t), 1000, "from filter"), out_of_band(tag8))
240     handle_out_of_band(tag8, read_tagless());
241   if (tag8 != tag)
242     myerror(FATAL|NOERRNO, "Tag mismatch, expected %s from filter, but got %s", tag2description(tag), tag2description(tag8));
243 
244   return read_tagless();
245 }
246 
247 
read_tagless()248 static char *read_tagless() {
249   uint32_t length32;
250   char *buffer;
251 
252   read_patiently2(filter_output_fd, &length32, sizeof(uint32_t), 1000, "from filter");
253   buffer = mymalloc(length32);
254   read_patiently2(filter_output_fd, buffer, length32, 1000,"from filter");
255 
256   if (buffer[length32 -1 ] != '\n')
257     myerror(FATAL|USE_ERRNO, "filter output without closing newline");
258   buffer[length32 -1 ] = '\0';
259 
260   return buffer;
261 }
262 
starts_with(const char * str,const char * prefix)263 static bool starts_with(const char *str, const char *prefix) {
264   return mystrstr(str, prefix) == str;
265 }
266 
maybe_tweak_readline(const char * message)267 static void maybe_tweak_readline(const char*message) { /* a bit kludgey, but easy to extend  */
268   char **words;
269   if (message[0] != '@')
270     return;
271   words = split_with(message, "::"); /* words and its elements are allocated on the heap */
272   /* parameter checking should be done  in the {python,perl} modules - if not, the assertions below may fail */
273   assert(words[1] != NULL);
274   if (starts_with(message, "@rl_completer_word_break_characters::"))
275       rl_completer_word_break_characters = words[1];
276   if (starts_with(message, "@rl_completer_quote_characters::"))
277     rl_completer_quote_characters = words[1];
278   if (starts_with(message, "@rl_filename_completion_desired::"))
279     rl_filename_completion_desired = my_atoi(words[1]);
280   if (starts_with(message, "@rl_variable_bind::")) {  /* "@rl_variable_bind::rl_variable_name::value::\n" */
281     assert(words[2] != NULL);
282     rl_variable_bind(words[1], words[2]); /* no need for error handling: readline will complain if necessary */
283   }
284   /* feel free to extend this list (but make sure to modify the {perl,python} modules accordingly! */
285 }
286 
handle_out_of_band(int tag,char * message)287 void handle_out_of_band(int tag, char *message) {
288   int split_em_up = FALSE;
289 
290   DPRINTF3(DEBUG_FILTERING, "received out-of-band (%s, %d bytes) %s", tag2description(tag),
291            (int) strlen(message), M(message));
292   switch (tag) {
293   case TAG_ERROR:
294     if (expected_tag == TAG_COMPLETION) /* start new line when completing (looks better) */
295       fprintf(stderr, "\n"); /* @@@ error reporting (still) uses buffered I/O */
296     WONTRETURN(myerror(FATAL|NOERRNO, message));
297   case TAG_OUTPUT_OUT_OF_BAND:
298     my_putstr(message);
299     break;
300   case TAG_ADD_TO_COMPLETION_LIST:
301   case TAG_REMOVE_FROM_COMPLETION_LIST:
302     split_em_up = TRUE;
303     break;
304   case TAG_IGNORE:
305     maybe_tweak_readline(message);
306     break;
307   default:
308     WONTRETURN(myerror(FATAL|USE_ERRNO, "out-of-band message with unknown tag %d: <%20s>", tag, message));
309   }
310   if (split_em_up) {
311     char **words = split_with(message, " \n\t");
312     char **plist, *word;
313     for(plist = words;(word = *plist); plist++)
314       if (tag == TAG_ADD_TO_COMPLETION_LIST)
315         add_word_to_completions(word);
316       else
317         remove_word_from_completions(word);
318     free_splitlist(words);
319   }
320   free(message);
321 }
322 
323 
write_to_filter(int tag,const char * string)324 static void write_to_filter(int tag, const char *string) {
325   write_message(filter_input_fd, tag, string, "to filter");
326 }
327 
328 
write_message(int fd,int tag,const char * string,const char * description)329 static void write_message(int fd, int tag,  const char *string, const char *description) {
330   uint8_t  tag8     = tag;
331   uint32_t length32 = strlen(string) + 1;
332   write_patiently2(fd, &tag8, sizeof (uint8_t) , 1000, description);
333   write_patiently2(fd, &length32, sizeof(uint32_t), 1000, description);
334   write_patiently2(fd, string, length32 - 1 , 1000, description);
335   write_patiently2(fd, "\n", 1 , 1000, description);
336 }
337 
338 
339 
340 
tag2description(int tag)341 static char* tag2description(int tag) {
342   switch (tag) {
343   case TAG_INPUT:                      return "INPUT";
344   case TAG_OUTPUT:                     return "OUTPUT";
345   case TAG_HISTORY:                    return "HISTORY";
346   case TAG_COMPLETION:                 return "COMPLETION";
347   case TAG_PROMPT:                     return "PROMPT";
348   case TAG_HOTKEY:                     return "HOTKEY";
349   case TAG_SIGNAL:                     return "SIGNAL";
350   case TAG_WHAT_ARE_YOUR_INTERESTS:    return "WHAT_ARE_YOUR_INTERESTS";
351   case TAG_IGNORE:                     return "TAG_IGNORE";
352   case TAG_ADD_TO_COMPLETION_LIST:     return "ADD_TO_COMPLETION_LIST";
353   case TAG_REMOVE_FROM_COMPLETION_LIST:return "REMOVE_FROM_COMPLETION_LIST";
354   case TAG_OUTPUT_OUT_OF_BAND:         return "OUTPUT_OUT_OF_BAND";
355   case TAG_ERROR:                      return "ERROR";
356   default:                             return as_string(tag);
357   }
358 }
359 
360