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