1 /*
2  * Copyright (C) 2002-2012 Edscott Wilson Garcia
3  * EMail: edscott@users.sf.net
4  *
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program;
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23 
24 #include "rfm.h"
25 #include "rfm_modules.h"
26 #include "primary-run.i"
27 
28 
29 static const gchar *terminals_v[] = {
30 	"roxterm",
31 	"sakura",
32 	"gnome-terminal",
33 	"Eterm",
34 	"konsole",
35 	"Terminal",
36 	"aterm",
37 	"xterm",
38 	"kterm",
39 	"wterm",
40 	"multi-aterm",
41 	"evilvte",
42 	"mlterm",
43 	"xvt",
44 	"rxvt",
45 	"urxvt",
46 	"mrxvt",
47 	"tilda",
48 	NULL
49 };
50 
51 static const gchar *editors_v[] = {
52 	"gvim -f",
53 	"mousepad",
54 	"gedit",
55 	"kate",
56 	"xemacs",
57 	"nano",
58 	"vi",
59 	NULL
60 };
61 
rfm_get_terminals(void)62 const gchar **rfm_get_terminals(void) {return terminals_v;}
rfm_get_editors(void)63 const gchar **rfm_get_editors(void) {return editors_v;}
64 
65 static GSList *children_list=NULL;
66 
67 static pthread_mutex_t children_list_mutex = PTHREAD_MUTEX_INITIALIZER;
68 
rfm_remove_child(pid_t child)69 void rfm_remove_child(pid_t child){
70     if (!children_list) return;
71     pthread_mutex_lock(&(children_list_mutex));
72     children_list = g_slist_remove(children_list, GINT_TO_POINTER(child));
73     pthread_mutex_unlock(&(children_list_mutex));
74     return;
75 }
76 
rfm_killall_children(void)77 void rfm_killall_children(void){
78     TRACE("rfm_killall_children(): signalling controller children...\n");
79     pthread_mutex_lock(&(children_list_mutex));
80 
81     GSList *list = children_list;
82     for (;list && list->data; list = list->next){
83 	pid_t child = GPOINTER_TO_INT(list->data);
84 	TRACE( "ZZZZZZZ--- killing %d\n", child);
85 	kill(child, SIGTERM);
86 
87     }
88     g_slist_free(children_list);
89     children_list = NULL;
90     pthread_mutex_unlock(&(children_list_mutex));
91 
92 }
93 
rfm_add_child(pid_t child)94 void rfm_add_child(pid_t child){
95     pthread_mutex_lock(&(children_list_mutex));
96     NOOP(stderr, "adding %d to children_list\n", child);
97     children_list = g_slist_prepend(children_list, GINT_TO_POINTER(child));
98     pthread_mutex_unlock(&(children_list_mutex));
99 
100     return;
101 }
102 
103 gchar *
rfm_get_text_editor_envar(const gchar * value)104 rfm_get_text_editor_envar(const gchar *value){
105     if(!value) return NULL;
106 
107     gchar *editor=g_path_get_basename(value);
108     // if nano or vi, then use terminal emulator
109     if (editor &&
110 	    (strncmp(editor, "vi",strlen("vi"))==0
111 	     ||
112 	     strncmp(editor, "nano",strlen("nano"))==0)){
113 	const gchar *t=getenv("TERMINAL_CMD");
114 	gchar *term = g_find_program_in_path(t);
115 	if (term) g_free(term);
116 	else {
117 	    t=NULL;
118 	    gint i;
119 	    for (i=0; terminals_v[i]; i++){
120 		// sakura is broken...
121 		if (strstr(terminals_v[i], "sakura")) continue;
122 		term = g_find_program_in_path(terminals_v[i]);
123 		if (term){
124 		    t=terminals_v[i];
125 		    g_free(term);
126 		    break;
127 		}
128 	    }
129 	}
130 	if (t && strlen(t)) {
131 	    gchar *b=g_strdup_printf("%s %s %s",
132 		    t, rfm_term_exec_option(t), editor);
133 	    g_free(editor);
134 	    editor = b;
135 	}
136     } else {
137 	g_free(editor);
138 	editor = g_strdup(value);
139     }
140     return (editor);
141 }
142 
143 ///////
144 static pthread_mutex_t *
get_command_history_mutex(void)145 get_command_history_mutex(void){
146     static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
147     return &mutex;
148 }
149 //////////////// Module wraparounds ////////////////////////////
150 //
151 // Use rfm_thread_run_argv or rfm_thread_run?
152 // rfm_thread_run_argv will execute directly, not via a shell, and will
153 // not be saved in the lpterm history
154 
155 // rfm_thread_run_argv:
156 // This modality will execute the command without shell
157 
158 
159 // rfm_thread_run:
160 // This modality will execute the command via "sh -c" and allow pipes and
161 // redirection. and will be saved in the lpterm history file
162 
163 pid_t
rfm_thread_run_argv(widgets_t * widgets_p,gchar ** argv,gboolean interm)164 rfm_thread_run_argv (
165 	widgets_t * widgets_p,
166 	gchar ** argv,
167 	gboolean interm
168 	){
169     const void *vector[]={widgets_p, argv, GINT_TO_POINTER(interm), NULL,  NULL, NULL, NULL, NULL};
170     return GPOINTER_TO_INT(rfm_vector_run(RFM_MODULE_DIR, "run", GINT_TO_POINTER(7),
171 		    vector, "m_thread_run_argv"));
172 }
173 
174 pid_t
rfm_thread_run_argv_full(widgets_t * widgets_p,gchar ** argv,gboolean interm,gint * stdin_fd,void (* stdout_f)(void * stdout_data,void * stream,int childFD),void (* stderr_f)(void * stdout_data,void * stream,int childFD),void (* tubo_done_f)(void * data))175 rfm_thread_run_argv_full (
176 	widgets_t * widgets_p,
177 	gchar ** argv,
178 	gboolean interm,
179 	gint *stdin_fd,
180 	void (*stdout_f) (void *stdout_data,
181                       void *stream,
182                       int childFD),
183 	void (*stderr_f) (void *stdout_data,
184                       void *stream,
185                       int childFD),
186 	void (*tubo_done_f) (void *data)
187 	){
188     if (!argv || !argv[0]) return 0;
189     const void *vector[]={widgets_p, argv, GINT_TO_POINTER(interm), stdin_fd,  stdout_f, stderr_f, tubo_done_f};
190     return GPOINTER_TO_INT(rfm_vector_run(RFM_MODULE_DIR, "run", GINT_TO_POINTER(7),
191 		    vector, "m_thread_run_argv"));
192 
193  }
194 
195 pid_t
rfm_thread_run_argv_with_stdin(widgets_t * widgets_p,gchar ** argv,gboolean interm,gint * stdin_fd)196 rfm_thread_run_argv_with_stdin (
197 	widgets_t * widgets_p,
198 	gchar ** argv,
199 	gboolean interm,
200 	gint *stdin_fd
201     	){
202     const void *vector[]={widgets_p, argv, GINT_TO_POINTER(interm), stdin_fd,  NULL, NULL, NULL};
203     return GPOINTER_TO_INT(rfm_vector_run(RFM_MODULE_DIR, "run", GINT_TO_POINTER(7),
204 		    vector, "m_thread_run_argv"));
205 
206 
207 }
208 
209 pid_t
rfm_thread_run_argv_with_stdout(widgets_t * widgets_p,gchar ** argv,gboolean interm,void (* stdout_f)(void * stdout_data,void * stream,int childFD))210 rfm_thread_run_argv_with_stdout (
211 	widgets_t * widgets_p,
212 	gchar ** argv,
213 	gboolean interm,
214 	void (*stdout_f) (void *stdout_data,
215                       void *stream,
216                       int childFD)
217 	){
218     const void *vector[]={widgets_p, argv, GINT_TO_POINTER(interm), NULL,  stdout_f, NULL, NULL};
219     return GPOINTER_TO_INT(rfm_vector_run(RFM_MODULE_DIR, "run", GINT_TO_POINTER(7),
220 		    vector, "m_thread_run_argv"));
221 
222 
223 }
224 
225 pid_t
rfm_thread_run_argv_with_stderr(widgets_t * widgets_p,gchar ** argv,gboolean interm,void (* stderr_f)(void * stderr_data,void * stream,int childFD))226 rfm_thread_run_argv_with_stderr (
227 	widgets_t * widgets_p,
228 	gchar ** argv,
229 	gboolean interm,
230 	void (*stderr_f) (void *stderr_data,
231                       void *stream,
232                       int childFD)
233 	){
234     const void *vector[]={widgets_p, argv, GINT_TO_POINTER(interm), NULL,  NULL, stderr_f, NULL};
235     return GPOINTER_TO_INT(rfm_vector_run(RFM_MODULE_DIR, "run", GINT_TO_POINTER(7),
236 		    vector, "m_thread_run_argv"));
237 
238 }
239 ////////////////////////////////////////////////////////////////////////////
240 
241 void
rfm_recover_flags(gchar * in_cmd,gboolean * interm,gboolean * hold)242 rfm_recover_flags (gchar * in_cmd, gboolean * interm, gboolean * hold) {
243     DBHashTable *runflags;
244     GString *gs;
245     int *flags;
246     gchar *g = g_build_filename ( RUN_FLAG_FILE, NULL);
247     TRACE("opening %s...\n",g);
248     if((runflags = dbh_new (g, NULL, DBH_READ_ONLY|DBH_PARALLEL_SAFE)) == NULL) {
249         TRACE ("Cannot open %s\n", g);
250         *interm = 0;
251         *hold = 0;
252         return;
253     }
254     TRACE("opened %s.\n",g);
255     dbh_set_parallel_lock_timeout(runflags, 3);
256     gs = g_string_new (in_cmd);
257     sprintf ((char *)DBH_KEY (runflags), "%10u", g_string_hash (gs));
258     g_string_free (gs, TRUE);
259     flags = (int *)runflags->data;
260     dbh_load (runflags);
261     *interm = flags[0];
262     *hold = flags[1];
263     dbh_close (runflags);
264 
265     NOOP ("flags recovered from dbh file for %s, interm=%d hold=%d\n", in_cmd, *interm, *hold);
266 }
267 
268 
269 // this function is run in a gthread at startup.
270 // mutex is to protect from main thread running the
271 // save history function. Also protects history file
272 // from concurrent write access.
273 gpointer
rfm_load_sh_command_history(gpointer data)274 rfm_load_sh_command_history (gpointer data) {
275     // This thread is not barrier subject
276     view_t *view_p = (view_t *) data;
277     gchar *history = g_build_filename (LP_TERMINAL_HISTORY, NULL);
278     pthread_mutex_t *command_history_mutex=get_command_history_mutex();
279     pthread_mutex_lock(command_history_mutex);
280     g_list_free (view_p->sh_command);
281     GList *p;
282     for(p = view_p->sh_command; p; p = p->next) {
283         g_free (p->data);
284     }
285     view_p->sh_command = NULL;
286     view_p->sh_command = g_list_append (view_p->sh_command, g_strdup (""));
287     view_p->sh_command_counter = 0;
288 
289     FILE *sh_history = fopen (history, "r");
290     if(sh_history) {
291         // we should put a file lock here...
292         NOOP ("rfm_load_sh_command_history(): readlock for %s\n", history);
293         gchar line[2048];
294         memset (line, 0, 2048);
295         while(fgets (line, 2047, sh_history) && !feof (sh_history)) {
296             NOOP ("HISTORY: line %s\n", line);
297             if(strchr (line, '\n'))
298                 *strchr (line, '\n') = 0;
299             if(strlen (line) == 0)
300                 continue;
301             // skip invalid commands (except cd):
302             if(!MIME_is_valid_command (line)) {
303                 if(strcmp (line, "cd") != 0 && strncmp (line, "cd ", strlen ("cd ")) != 0) {
304                     NOOP ("HISTORY: invalid history command in %s: %s\n", history, line);
305                     continue;
306                 }
307             }
308 	    gchar *newline=compact_line(line);
309 	    GList *element=find_in_history_list(view_p->sh_command, newline);
310 
311 	    if (element) {
312 		// remove old element
313 		gchar *data=element->data;
314 		view_p->sh_command = g_list_remove(view_p->sh_command, data);
315 		g_free(data);
316 	    }
317 	    // put at top of the pile
318 	    view_p->sh_command =
319 		    g_list_insert_before (view_p->sh_command, g_list_last (view_p->sh_command), newline);
320         }
321 
322         NOOP ("rfm_load_sh_command_history(): readunlock for %s\n", history);
323         fclose (sh_history);
324         view_p->sh_command_counter = g_list_length (view_p->sh_command) - 1;
325     }
326     g_free (history);
327 
328     pthread_mutex_unlock(command_history_mutex);
329     return NULL;
330 }
331 
332 void
rfm_save_sh_command_history(view_t * view_p,const gchar * command)333 rfm_save_sh_command_history (view_t * view_p, const gchar * command) {
334     if (!view_p){
335 	return;
336     }
337     GList *p;
338 
339     pthread_mutex_t *command_history_mutex=get_command_history_mutex();
340     pthread_mutex_lock(command_history_mutex);
341     p = g_list_previous (g_list_last (view_p->sh_command));
342     if(!command || !strlen (command)){
343 	pthread_mutex_unlock(command_history_mutex);
344         return;
345     }
346     gchar *command_p = g_strdup (command);
347     g_strstrip (command_p);
348     // if repeat of last command, skip it.
349     if(!p || strcmp (command, (char *)p->data)) {
350         view_p->sh_command = g_list_insert_before (view_p->sh_command, g_list_last (view_p->sh_command), command_p);
351         //view_p->sh_command = g_list_append(view_p->sh_command, command_p);
352 
353         // don't save to file if invalid command
354         if(!MIME_is_valid_command (command_p)) {
355             if(strcmp (command_p, "cd") != 0 && strncmp (command_p, "cd ", strlen ("cd ")) != 0) {
356                 DBG ("not saving %s\n", command_p);
357                 view_p->sh_command_counter = g_list_length (view_p->sh_command) - 1;
358 		pthread_mutex_unlock(command_history_mutex);
359                 return;
360             }
361         }
362         // here we will rewrite the history, removing any item
363         // which duplicate the command about to be saved.
364         // This effectively moves the command to the bottom
365         // of the list.
366         //
367 
368         gchar *history = g_build_filename (LP_TERMINAL_HISTORY, NULL);
369 
370         // read it first to synchronize with other rodent instances
371         GList *disk_history = NULL;
372 
373         FILE *sh_history = fopen (history, "r");
374 
375         if(sh_history) {
376 
377             char line[2048];
378             memset (line, 0, 2048);
379             while(fgets (line, 2047, sh_history) && !feof (sh_history)) {
380                 if(strchr (line, '\n')) {
381                     *strchr (line, '\n') = 0;
382                 }
383                 if(strcmp (line, command_p) != 0) {
384                     disk_history = g_list_append (disk_history, g_strdup (line));
385                 }
386             }
387 
388             fclose (sh_history);
389         }
390         disk_history = g_list_append (disk_history, g_strdup (command_p));
391 
392         sh_history = fopen (history, "w");
393         if(sh_history) {
394             // we should put a file lock here...
395             GList *p;
396             for(p = g_list_first (disk_history); p && p->data; p = p->next) {
397                 fprintf (sh_history, "%s\n", (char *)p->data);
398                 g_free (p->data);
399             }
400             fclose (sh_history);
401         }
402         g_list_free (disk_history);
403         g_free (history);
404     }
405     view_p->sh_command_counter = g_list_length (view_p->sh_command) - 1;
406     NOOP ("rfm_save_sh_command_history(); command_counter=%d\n",
407 	    view_p->sh_command_counter);
408 
409     pthread_mutex_unlock(command_history_mutex);
410     return;
411 }
412 
413 
414 const gchar *
rfm_term_exec_option(const gchar * terminal)415 rfm_term_exec_option(const gchar *terminal) {
416     const gchar *exec_option = "-e";
417     gchar *t = g_path_get_basename (terminal);
418     if(strcmp (t, "gnome-terminal") == 0 || strcmp (t, "Terminal") == 0)
419             exec_option = "-x";
420     g_free(t);
421     return exec_option;
422 }
423 
424 const gchar *
rfm_what_term(void)425 rfm_what_term (void) {
426     const gchar *term=getenv ("TERMINAL_CMD");
427     gchar *t=NULL;
428     if(term && strlen (term)) {
429 	if (strchr(term, ' ')){
430 	    gchar **g = g_strsplit(term, " ", -1);
431 	    t = g_find_program_in_path (g[0]);
432 	    g_strfreev(g);
433 	} else {
434 	    t = g_find_program_in_path (term);
435 	}
436     }
437     if(!t) {
438 	    const gchar **p=terminals_v;
439 	    for (;p && *p; p++){
440 		t = g_find_program_in_path (*p);
441 		if (t) {
442 		    term=*p;
443 		    break;
444 		}
445 	    }
446     }
447     if (t) {
448 	g_free(t);
449 	return term;
450     }
451     DBG ("TERMINAL_CMD=%s: %s\n", getenv ("TERMINAL_CMD"), strerror (ENOENT));
452 
453     return NULL;
454 }
455 
456