1 #include "e.h"
2
3 #define MAX_OUTPUT_CHARACTERS 5000
4
5 /* TODO:
6 * - Clear e_exec_instances on shutdown
7 * - Clear e_exec_start_pending on shutdown
8 * - Create border add handler
9 * - Launch .desktop in terminal if .desktop requires it
10 */
11
12 typedef struct _E_Exec_Launch E_Exec_Launch;
13 typedef struct _E_Exec_Search E_Exec_Search;
14 typedef struct _E_Exec_Watch E_Exec_Watch;
15
16 struct _E_Exec_Launch
17 {
18 E_Zone *zone;
19 const char *launch_method;
20 };
21
22 struct _E_Exec_Search
23 {
24 E_Exec_Instance *inst;
25 Efreet_Desktop *desktop;
26 int startup_id;
27 pid_t pid;
28 };
29
30 struct _E_Exec_Watch
31 {
32 void (*func)(void *data, E_Exec_Instance *inst, E_Exec_Watch_Type type);
33 const void *data;
34 Eina_Bool delete_me E_BITFIELD;
35 };
36
37 struct _E_Config_Dialog_Data
38 {
39 Efreet_Desktop *desktop;
40 char *exec;
41
42 Ecore_Exe_Event_Del event;
43 Ecore_Exe_Event_Data *error;
44 Ecore_Exe_Event_Data *read;
45
46 char *label, *exit, *signal;
47 };
48
49 /* local subsystem functions */
50 static E_Exec_Instance *_e_exec_cb_exec(void *data, Efreet_Desktop *desktop, char *exec, int remaining);
51 static Eina_Bool _e_exec_cb_expire_timer(void *data);
52 static Eina_Bool _e_exec_cb_exit(void *data, int type, void *event);
53 static Eina_Bool _e_exec_cb_desktop_update(void *data, int type, void *event);
54 static void _e_exec_cb_exec_new_free(void *data, void *event);
55 static void _e_exec_cb_exec_new_client_free(void *data, void *ev);
56 static void _e_exec_cb_exec_del_free(void *data, void *event);
57 static void _e_exe_instance_watchers_call(E_Exec_Instance *inst, E_Exec_Watch_Type type);
58 static Eina_Bool _e_exec_startup_id_pid_find(const Eina_Hash *hash EINA_UNUSED, const void *key EINA_UNUSED, void *value, void *data);
59
60 static void _e_exec_error_dialog(Efreet_Desktop *desktop, const char *exec, Ecore_Exe_Event_Del *event, Ecore_Exe_Event_Data *error, Ecore_Exe_Event_Data *read);
61 static void _fill_data(E_Config_Dialog_Data *cfdata);
62 static void *_create_data(E_Config_Dialog *cfd);
63 static void _free_data(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata);
64 static Evas_Object *_basic_create_widgets(E_Config_Dialog *cfd, Evas *evas, E_Config_Dialog_Data *cfdata);
65 static Evas_Object *_advanced_create_widgets(E_Config_Dialog *cfd, Evas *evas, E_Config_Dialog_Data *cfdata);
66 static Evas_Object *_dialog_scrolltext_create(Evas *evas, char *title, Ecore_Exe_Event_Data_Line *lines);
67 static void _dialog_save_cb(void *data, void *data2);
68 static Eina_Bool _e_exec_instance_free(E_Exec_Instance *inst);
69
70 /* local subsystem globals */
71 static Eina_List *e_exec_start_pending = NULL;
72 static Eina_Hash *e_exec_instances = NULL;
73 static int startup_id = 0;
74
75 static Ecore_Event_Handler *_e_exec_exit_handler = NULL;
76 static Ecore_Event_Handler *_e_exec_desktop_update_handler = NULL;
77
78 static E_Exec_Instance *(*_e_exec_executor_func)(void *data, E_Zone * zone, Efreet_Desktop * desktop, const char *exec, Eina_List *files, const char *launch_method) = NULL;
79 static void *_e_exec_executor_data = NULL;
80
81 E_API int E_EVENT_EXEC_NEW = -1;
82 E_API int E_EVENT_EXEC_NEW_CLIENT = -1;
83 E_API int E_EVENT_EXEC_DEL = -1;
84
85 /* externally accessible functions */
86 EINTERN int
e_exec_init(void)87 e_exec_init(void)
88 {
89 e_exec_instances = eina_hash_string_superfast_new(NULL);
90
91 _e_exec_exit_handler =
92 ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _e_exec_cb_exit, NULL);
93 _e_exec_desktop_update_handler =
94 ecore_event_handler_add(EFREET_EVENT_DESKTOP_CACHE_UPDATE, _e_exec_cb_desktop_update, NULL);
95
96 E_EVENT_EXEC_NEW = ecore_event_type_new();
97 E_EVENT_EXEC_NEW_CLIENT = ecore_event_type_new();
98 E_EVENT_EXEC_DEL = ecore_event_type_new();
99 return 1;
100 }
101
102 EINTERN int
e_exec_shutdown(void)103 e_exec_shutdown(void)
104 {
105 if (_e_exec_exit_handler) ecore_event_handler_del(_e_exec_exit_handler);
106 if (_e_exec_desktop_update_handler)
107 ecore_event_handler_del(_e_exec_desktop_update_handler);
108 eina_hash_free(e_exec_instances);
109 eina_list_free(e_exec_start_pending);
110 return 1;
111 }
112
113 E_API void
e_exec_executor_set(E_Exec_Instance * (* func)(void * data,E_Zone * zone,Efreet_Desktop * desktop,const char * exec,Eina_List * files,const char * launch_method),const void * data)114 e_exec_executor_set(E_Exec_Instance *(*func)(void *data, E_Zone * zone, Efreet_Desktop * desktop, const char *exec, Eina_List *files, const char *launch_method), const void *data)
115 {
116 _e_exec_executor_func = func;
117 _e_exec_executor_data = (void *)data;
118 }
119
120 E_API E_Exec_Instance *
e_exec(E_Zone * zone,Efreet_Desktop * desktop,const char * exec,Eina_List * files,const char * launch_method)121 e_exec(E_Zone *zone, Efreet_Desktop *desktop, const char *exec,
122 Eina_List *files, const char *launch_method)
123 {
124 E_Exec_Launch *launch;
125 E_Exec_Instance *inst = NULL;
126
127 if ((!desktop) && (!exec)) return NULL;
128
129 if (_e_exec_executor_func)
130 return _e_exec_executor_func(_e_exec_executor_data, zone,
131 desktop, exec, files, launch_method);
132
133 if (desktop)
134 {
135 const char *single;
136
137 single = eina_hash_find(desktop->x, "X-Enlightenment-Single-Instance");
138 if ((single) ||
139 (e_config->exe_always_single_instance))
140 {
141 Eina_Bool dosingle = EINA_FALSE;
142
143 // first take system config for always single instance if set
144 if (e_config->exe_always_single_instance) dosingle = EINA_TRUE;
145
146 // and now let desktop file override it
147 if (single)
148 {
149 if ((!strcasecmp(single, "true")) ||
150 (!strcasecmp(single, "yes")) ||
151 (!strcasecmp(single, "1")))
152 dosingle = EINA_TRUE;
153 else if ((!strcasecmp(single, "false")) ||
154 (!strcasecmp(single, "no")) ||
155 (!strcasecmp(single, "0")))
156 dosingle = EINA_FALSE;
157 }
158
159 if (dosingle)
160 {
161 const Eina_List *l;
162 E_Client *ec;
163
164 EINA_LIST_FOREACH(e_comp->clients, l, ec)
165 {
166 if (ec && (ec->desktop == desktop))
167 {
168 if (!ec->focused)
169 e_client_activate(ec, EINA_TRUE);
170 else
171 evas_object_raise(ec->frame);
172 return NULL;
173 }
174 }
175 }
176 }
177 }
178
179 launch = E_NEW(E_Exec_Launch, 1);
180 if (!launch) return NULL;
181 if (zone)
182 {
183 launch->zone = zone;
184 e_object_ref(E_OBJECT(launch->zone));
185 }
186 if (launch_method)
187 launch->launch_method = eina_stringshare_add(launch_method);
188
189 if (desktop)
190 {
191 if (exec)
192 inst = _e_exec_cb_exec(launch, NULL, strdup(exec), 0);
193 else
194 {
195 if (desktop->exec)
196 inst = efreet_desktop_command_get(desktop, files,
197 (Efreet_Desktop_Command_Cb)_e_exec_cb_exec, launch);
198 else
199 inst = _e_exec_cb_exec(launch, desktop, NULL, 0);
200 }
201 }
202 else
203 inst = _e_exec_cb_exec(launch, NULL, strdup(exec), 0);
204 if ((zone) && (inst))
205 {
206 inst->screen = zone->num;
207 inst->desk_x = zone->desk_x_current;
208 inst->desk_y = zone->desk_y_current;
209 }
210 return inst;
211 }
212
213 E_API Eina_Bool
e_exec_phony_del(E_Exec_Instance * inst)214 e_exec_phony_del(E_Exec_Instance *inst)
215 {
216 if (!inst) return EINA_TRUE;
217 EINA_SAFETY_ON_TRUE_RETURN_VAL(!inst->phony, EINA_FALSE);
218 inst->ref--;
219 _e_exe_instance_watchers_call(inst, E_EXEC_WATCH_STOPPED);
220 return _e_exec_instance_free(inst);
221 }
222
223 E_API E_Exec_Instance *
e_exec_phony(E_Client * ec)224 e_exec_phony(E_Client *ec)
225 {
226 E_Exec_Instance *inst;
227 Eina_List *l, *lnew;
228
229 if (ec->desktop)
230 {
231 /* try grouping with previous phony exec */
232 l = eina_hash_find(e_exec_instances, ec->desktop->orig_path ?: ec->desktop->name);
233 EINA_LIST_FOREACH(l, lnew, inst)
234 if (inst && inst->phony)
235 {
236 e_exec_instance_client_add(inst, ec);
237 return inst;
238 }
239 }
240
241 inst = E_NEW(E_Exec_Instance, 1);
242 inst->ref = 1;
243 inst->phony = 1;
244 inst->desktop = ec->desktop;
245 inst->startup_id = ec->netwm.startup_id;
246 if (ec->desktop)
247 {
248 efreet_desktop_ref(ec->desktop);
249 inst->key = eina_stringshare_add(ec->desktop->orig_path ?: ec->desktop->name);
250 }
251 else if (ec->icccm.command.argc)
252 {
253 Eina_Strbuf *buf;
254 int x;
255
256 buf = eina_strbuf_new();
257 for (x = 0; x < ec->icccm.command.argc; x++)
258 {
259 eina_strbuf_append(buf, ec->icccm.command.argv[x]);
260 if (x + 1 < ec->icccm.command.argc)
261 eina_strbuf_append_char(buf, ' ');
262 }
263 inst->key = eina_stringshare_add(eina_strbuf_string_get(buf));
264 eina_strbuf_free(buf);
265 }
266 else
267 {
268 free(inst);
269 return NULL;
270 }
271 inst->used = 1;
272 if (ec->zone) inst->screen = ec->zone->num;
273 if (ec->desk)
274 {
275 inst->desk_x = ec->desk->x;
276 inst->desk_y = ec->desk->y;
277 }
278 l = eina_hash_find(e_exec_instances, inst->key);
279 lnew = eina_list_append(l, inst);
280 if (l) eina_hash_modify(e_exec_instances, inst->key, lnew);
281 else eina_hash_add(e_exec_instances, inst->key, lnew);
282 inst->ref++;
283 ecore_event_add(E_EVENT_EXEC_NEW, inst, _e_exec_cb_exec_new_free, inst);
284 e_exec_instance_client_add(inst, ec);
285 return inst;
286 }
287
288 E_API E_Exec_Instance *
e_exec_startup_id_pid_instance_find(int id,pid_t pid)289 e_exec_startup_id_pid_instance_find(int id, pid_t pid)
290 {
291 E_Exec_Search search;
292
293 search.inst = NULL;
294 search.desktop = NULL;
295 search.startup_id = id;
296 search.pid = pid;
297 eina_hash_foreach(e_exec_instances, _e_exec_startup_id_pid_find, &search);
298 return search.inst;
299 }
300
301 E_API Efreet_Desktop *
e_exec_startup_id_pid_find(int id,pid_t pid)302 e_exec_startup_id_pid_find(int id, pid_t pid)
303 {
304 E_Exec_Instance *inst;
305
306 inst = e_exec_startup_id_pid_instance_find(id, pid);
307 if (!inst) return NULL;
308 return inst->desktop;
309 }
310
311 E_API E_Exec_Instance *
e_exec_startup_desktop_instance_find(Efreet_Desktop * desktop)312 e_exec_startup_desktop_instance_find(Efreet_Desktop *desktop)
313 {
314 E_Exec_Search search;
315
316 search.inst = NULL;
317 search.desktop = desktop;
318 search.startup_id = 0;
319 search.pid = 0;
320 eina_hash_foreach(e_exec_instances, _e_exec_startup_id_pid_find, &search);
321 return search.inst;
322 }
323
324 static void
_e_exe_instance_watchers_call(E_Exec_Instance * inst,E_Exec_Watch_Type type)325 _e_exe_instance_watchers_call(E_Exec_Instance *inst, E_Exec_Watch_Type type)
326 {
327 E_Exec_Watch *iw;
328 Eina_List *l, *ln;
329
330 inst->ref++;
331 EINA_LIST_FOREACH(inst->watchers, l, iw)
332 {
333 if (iw->func && (!iw->delete_me)) iw->func((void *)(iw->data), inst, type);
334 }
335 inst->ref--;
336 if (inst->ref == 0)
337 {
338 EINA_LIST_FOREACH_SAFE(inst->watchers, l, ln, iw)
339 {
340 if (iw->delete_me)
341 {
342 inst->watchers = eina_list_remove_list(inst->watchers, l);
343 free(iw);
344 }
345 }
346 }
347 }
348
349 E_API void
e_exec_instance_found(E_Exec_Instance * inst)350 e_exec_instance_found(E_Exec_Instance *inst)
351 {
352 E_FREE_FUNC(inst->expire_timer, ecore_timer_del);
353 _e_exe_instance_watchers_call(inst, E_EXEC_WATCH_STARTED);
354 }
355
356 E_API void
e_exec_instance_client_add(E_Exec_Instance * inst,E_Client * ec)357 e_exec_instance_client_add(E_Exec_Instance *inst, E_Client *ec)
358 {
359 inst->clients = eina_list_append(inst->clients, ec);
360 REFD(ec, 2);
361 e_object_ref(E_OBJECT(ec));
362 ec->exe_inst = inst;
363 inst->ref++;
364 ecore_event_add(E_EVENT_EXEC_NEW_CLIENT, inst, _e_exec_cb_exec_new_client_free, ec);
365 }
366
367 E_API void
e_exec_instance_watcher_add(E_Exec_Instance * inst,void (* func)(void * data,E_Exec_Instance * inst,E_Exec_Watch_Type type),const void * data)368 e_exec_instance_watcher_add(E_Exec_Instance *inst, void (*func)(void *data, E_Exec_Instance *inst, E_Exec_Watch_Type type), const void *data)
369 {
370 E_Exec_Watch *iw;
371
372 iw = E_NEW(E_Exec_Watch, 1);
373 if (!iw) return;
374 iw->func = func;
375 iw->data = data;
376 inst->watchers = eina_list_append(inst->watchers, iw);
377 }
378
379 E_API void
e_exec_instance_watcher_del(E_Exec_Instance * inst,void (* func)(void * data,E_Exec_Instance * inst,E_Exec_Watch_Type type),const void * data)380 e_exec_instance_watcher_del(E_Exec_Instance *inst, void (*func)(void *data, E_Exec_Instance *inst, E_Exec_Watch_Type type), const void *data)
381 {
382 E_Exec_Watch *iw;
383 Eina_List *l, *ln;
384
385 EINA_LIST_FOREACH_SAFE(inst->watchers, l, ln, iw)
386 {
387 if ((iw->func == func) && (iw->data == data))
388 {
389 if (inst->ref == 0)
390 {
391 inst->watchers = eina_list_remove_list(inst->watchers, l);
392 free(iw);
393 return;
394 }
395 else
396 {
397 iw->delete_me = EINA_TRUE;
398 return;
399 }
400 }
401 }
402 }
403
404 /* local subsystem functions */
405 static E_Exec_Instance *
_e_exec_cb_exec(void * data,Efreet_Desktop * desktop,char * exec,int remaining)406 _e_exec_cb_exec(void *data, Efreet_Desktop *desktop, char *exec, int remaining)
407 {
408 E_Exec_Instance *inst = NULL;
409 E_Exec_Launch *launch;
410 Eina_List *l, *lnew;
411 Ecore_Exe *exe = NULL;
412 char buf[4096];
413
414 launch = data;
415 inst = E_NEW(E_Exec_Instance, 1);
416 if (!inst) return NULL;
417 inst->ref = 1;
418
419 if (startup_id == 0)
420 {
421 startup_id = e_exehist_startup_id_get();
422 if (startup_id < 0) startup_id = 0;
423 }
424 if (++startup_id < 1) startup_id = 1;
425 snprintf(buf, sizeof(buf), "E_START|%i", startup_id);
426 e_util_env_set("DESKTOP_STARTUP_ID", buf);
427
428 // don't set vsync for clients - maybe inherited from compositor. fixme:
429 // need a way to still inherit from parent env of wm.
430 e_util_env_set("__GL_SYNC_TO_VBLANK", NULL);
431
432 //// FIXME: seem to be some issues with the pipe and filling up ram - need to
433 //// check. for now disable.
434 // exe = ecore_exe_pipe_run(exec,
435 // ECORE_EXE_PIPE_AUTO | ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_ERROR |
436 // ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR_LINE_BUFFERED,
437 // inst);
438 if ((desktop) && (desktop->path) && (desktop->path[0]))
439 {
440 if (!getcwd(buf, sizeof(buf)))
441 {
442 free(inst);
443 e_util_dialog_show
444 (_("Run Error"),
445 _("Enlightenment was unable to get current directory"));
446 return NULL;
447 }
448 if (chdir(desktop->path))
449 {
450 free(inst);
451 e_util_dialog_show
452 (_("Run Error"),
453 _("Enlightenment was unable to change to directory:<ps/>"
454 "<ps/>"
455 "%s"),
456 desktop->path);
457 return NULL;
458 }
459 exe = e_util_exe_safe_run(exec, inst);
460 if (chdir(buf))
461 {
462 e_util_dialog_show
463 (_("Run Error"),
464 _("Enlightenment was unable to restore to directory:<ps/>"
465 "<ps/>"
466 "%s"),
467 buf);
468 free(inst);
469 return NULL;
470 }
471 }
472 else if (exec)
473 {
474 if ((desktop) && (desktop->terminal))
475 {
476 Efreet_Desktop *tdesktop;
477
478 tdesktop = e_util_terminal_desktop_get();
479 if (tdesktop)
480 {
481 if (tdesktop->exec)
482 {
483 Eina_Strbuf *sb;
484
485 sb = eina_strbuf_new();
486 if (sb)
487 {
488 eina_strbuf_append(sb, tdesktop->exec);
489 eina_strbuf_append(sb, " -e ");
490 eina_strbuf_append_escaped(sb, exec);
491 exe = e_util_exe_safe_run
492 (eina_strbuf_string_get(sb), inst);
493 eina_strbuf_free(sb);
494 }
495 }
496 else
497 exe = e_util_exe_safe_run(exec, inst);
498 efreet_desktop_free(tdesktop);
499 }
500 else
501 exe = e_util_exe_safe_run(exec, inst);
502 }
503 else if (desktop && desktop->url)
504 {
505 char *sb;
506 size_t size = 65536, len;
507
508 sb = malloc(size);
509 snprintf(sb, size, "%s/enlightenment_open ", e_prefix_bin_get());
510 len = strlen(sb);
511 sb = e_util_string_append_quoted(sb, &size, &len, desktop->url);
512 exe = e_util_exe_safe_run(sb, inst);
513 free(sb);
514 }
515 else
516 exe = e_util_exe_safe_run(exec, inst);
517 }
518
519 if (!exe)
520 {
521 free(inst);
522 e_util_dialog_show(_("Run Error"),
523 _("Enlightenment was unable to fork a child process:<ps/>"
524 "<ps/>"
525 "%s"),
526 exec);
527 return NULL;
528 }
529 /* reset env vars */
530 if ((launch->launch_method) && (!desktop))
531 e_exehist_add(launch->launch_method, exec);
532 /* 20 lines at start and end, 20x100 limit on bytes at each end. */
533 //// FIXME: seem to be some issues with the pipe and filling up ram - need to
534 //// check. for now disable.
535 // ecore_exe_auto_limits_set(exe, 2000, 2000, 20, 20);
536 ecore_exe_tag_set(exe, "E/exec");
537
538 if (desktop)
539 {
540 inst->desktop = desktop;
541 efreet_desktop_ref(desktop);
542 inst->key = eina_stringshare_add(desktop->orig_path ?: desktop->name);
543 }
544 else
545 inst->key = eina_stringshare_add(exec);
546 inst->exe = exe;
547 inst->startup_id = startup_id;
548 inst->launch_time = ecore_time_get();
549 inst->expire_timer = ecore_timer_loop_add(e_config->exec.expire_timeout,
550 _e_exec_cb_expire_timer, inst);
551 l = eina_hash_find(e_exec_instances, inst->key);
552 lnew = eina_list_append(l, inst);
553 if (l) eina_hash_modify(e_exec_instances, inst->key, lnew);
554 else eina_hash_add(e_exec_instances, inst->key, lnew);
555 if (inst->desktop && inst->desktop->exec)
556 {
557 e_exec_start_pending = eina_list_append(e_exec_start_pending,
558 inst->desktop);
559 e_exehist_add(launch->launch_method, inst->desktop->exec);
560 }
561
562 if (!remaining)
563 {
564 if (launch->launch_method) eina_stringshare_del(launch->launch_method);
565 if (launch->zone) e_object_unref(E_OBJECT(launch->zone));
566 free(launch);
567 }
568 free(exec);
569 inst->ref++;
570 ecore_event_add(E_EVENT_EXEC_NEW, inst, _e_exec_cb_exec_new_free, inst);
571 return inst;
572 }
573
574 static Eina_Bool
_e_exec_cb_expire_timer(void * data)575 _e_exec_cb_expire_timer(void *data)
576 {
577 E_Exec_Instance *inst;
578
579 inst = data;
580 if (inst->desktop)
581 e_exec_start_pending = eina_list_remove(e_exec_start_pending,
582 inst->desktop);
583 inst->expire_timer = NULL;
584 _e_exe_instance_watchers_call(inst, E_EXEC_WATCH_TIMEOUT);
585 return ECORE_CALLBACK_CANCEL;
586 }
587
588 static Eina_Bool
_e_exec_instance_free(E_Exec_Instance * inst)589 _e_exec_instance_free(E_Exec_Instance *inst)
590 {
591 Eina_List *instances;
592
593 if (inst->ref) return EINA_FALSE;
594 E_FREE_LIST(inst->watchers, free);
595 if (inst->key)
596 {
597 instances = eina_hash_find(e_exec_instances, inst->key);
598 if (instances)
599 {
600 instances = eina_list_remove(instances, inst);
601 if (instances)
602 eina_hash_modify(e_exec_instances, inst->key, instances);
603 else
604 eina_hash_del_by_key(e_exec_instances, inst->key);
605 }
606 eina_stringshare_replace(&inst->key, NULL);
607 }
608 if (!inst->deleted)
609 {
610 inst->deleted = 1;
611 inst->ref++;
612 E_LIST_FOREACH(inst->clients, e_object_ref);
613 ecore_event_add(E_EVENT_EXEC_DEL, inst, _e_exec_cb_exec_del_free, inst);
614 return EINA_FALSE;
615 }
616
617 return EINA_TRUE;
618 }
619
620 /*
621 static Eina_Bool
622 _e_exec_cb_instance_finish(void *data)
623 {
624 _e_exec_instance_free(data);
625 return ECORE_CALLBACK_CANCEL;
626 }
627 */
628
629 static void
_e_exec_cb_exec_new_client_free(void * data,void * ev)630 _e_exec_cb_exec_new_client_free(void *data, void *ev)
631 {
632 E_Exec_Instance *inst = ev;
633 E_Client *ec = data;
634
635 inst->ref--;
636 _e_exec_instance_free(inst);
637 UNREFD(ec, 1);
638 e_object_unref(E_OBJECT(ec));
639 }
640
641 static void
_e_exec_cb_exec_new_free(void * data,void * ev EINA_UNUSED)642 _e_exec_cb_exec_new_free(void *data, void *ev EINA_UNUSED)
643 {
644 E_Exec_Instance *inst = data;
645
646 inst->ref--;
647 _e_exec_instance_free(inst);
648 }
649
650 static void
_e_exec_cb_exec_del_free(void * data,void * ev EINA_UNUSED)651 _e_exec_cb_exec_del_free(void *data, void *ev EINA_UNUSED)
652 {
653 E_Exec_Instance *inst = data;
654 E_Client *ec;
655
656 inst->ref--;
657
658 if (inst->desktop)
659 e_exec_start_pending = eina_list_remove(e_exec_start_pending,
660 inst->desktop);
661 if (inst->expire_timer) ecore_timer_del(inst->expire_timer);
662
663 EINA_LIST_FREE(inst->clients, ec)
664 {
665 ec->exe_inst = NULL;
666 e_object_unref(E_OBJECT(ec));
667 }
668
669 if (inst->desktop) efreet_desktop_free(inst->desktop);
670 if (!inst->phony)
671 {
672 if (inst->exe) ecore_exe_data_set(inst->exe, NULL);
673 }
674 free(inst);
675 }
676
677 static Eina_Bool
_e_exec_cb_exit(void * data EINA_UNUSED,int type EINA_UNUSED,void * event)678 _e_exec_cb_exit(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
679 {
680 Ecore_Exe_Event_Del *ev;
681 E_Exec_Instance *inst;
682
683 ev = event;
684 if (!ev->exe)
685 {
686 inst = e_exec_startup_id_pid_instance_find(-1, ev->pid);
687 if (!inst) return ECORE_CALLBACK_PASS_ON;
688 ev->exe = inst->exe;
689 }
690 // if (ecore_exe_tag_get(ev->exe)) printf(" tag %s\n", ecore_exe_tag_get(ev->exe));
691 if (!(ecore_exe_tag_get(ev->exe) &&
692 (!strcmp(ecore_exe_tag_get(ev->exe), "E/exec"))))
693 return ECORE_CALLBACK_PASS_ON;
694 inst = ecore_exe_data_get(ev->exe);
695 if (!inst) return ECORE_CALLBACK_PASS_ON;
696 if (inst->phony) return ECORE_CALLBACK_RENEW;
697
698 /* /bin/sh uses this if cmd not found */
699 if ((ev->exited) &&
700 ((ev->exit_code == 127) || (ev->exit_code == 255)))
701 {
702 if (e_config->exec.show_run_dialog)
703 {
704 E_Dialog *dia;
705
706 dia = e_dialog_new(NULL,
707 "E", "_e_exec_run_error_dialog");
708 if (dia)
709 {
710 char buf[4096];
711
712 e_dialog_title_set(dia, _("Application run error"));
713 snprintf(buf, sizeof(buf),
714 _("Enlightenment was unable to run the application:<ps/>"
715 "<ps/>"
716 "%s<ps/>"
717 "<ps/>"
718 "The application failed to start."),
719 ecore_exe_cmd_get(ev->exe));
720 e_dialog_text_set(dia, buf);
721 e_dialog_button_add(dia, _("OK"), NULL, NULL, NULL);
722 e_dialog_button_focus_num(dia, 1);
723 elm_win_center(dia->win, 1, 1);
724 e_dialog_show(dia);
725 }
726 }
727 }
728 /* Let's hope that everything returns this properly. */
729 else if (!((ev->exited) && (ev->exit_code == EXIT_SUCCESS)))
730 {
731 if (e_config->exec.show_exit_dialog)
732 {
733 /* filter out common exits via signals - int/term/quit. not really
734 * worth popping up a dialog for */
735 if (!((ev->signalled) &&
736 ((ev->exit_signal == SIGINT) ||
737 (ev->exit_signal == SIGQUIT) ||
738 (ev->exit_signal == SIGTERM)))
739 )
740 {
741 /* Show the error dialog with details from the exe. */
742 _e_exec_error_dialog(inst->desktop, ecore_exe_cmd_get(ev->exe), ev,
743 ecore_exe_event_data_get(ev->exe, ECORE_EXE_PIPE_ERROR),
744 ecore_exe_event_data_get(ev->exe, ECORE_EXE_PIPE_READ));
745 }
746 }
747 }
748
749 /* scripts that fork off children with & break child tracking... but this hack
750 * also breaks apps that handle single-instance themselves */
751 /*
752 if ((ecore_time_get() - inst->launch_time) < 2.0)
753 {
754 inst->exe = NULL;
755 if (inst->expire_timer) ecore_timer_del(inst->expire_timer);
756 inst->expire_timer = ecore_timer_loop_add(e_config->exec.expire_timeout, _e_exec_cb_instance_finish, inst);
757 }
758 else
759 */
760 inst->ref--;
761 inst->exe = NULL;
762 _e_exe_instance_watchers_call(inst, E_EXEC_WATCH_STOPPED);
763 _e_exec_instance_free(inst);
764
765 return ECORE_CALLBACK_PASS_ON;
766 }
767
768 static Eina_Bool
_e_exec_cb_desktop_update(void * data EINA_UNUSED,int type EINA_UNUSED,void * event EINA_UNUSED)769 _e_exec_cb_desktop_update(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
770 {
771 const Eina_Hash *execs = e_exec_instances_get();
772 Eina_Iterator *it;
773 const Eina_List *l, *ll;
774 E_Exec_Instance *exe;
775
776 it = eina_hash_iterator_data_new(execs);
777 EINA_ITERATOR_FOREACH(it, l)
778 {
779 EINA_LIST_FOREACH(l, ll, exe)
780 {
781 if (exe->desktop)
782 {
783 efreet_desktop_free(exe->desktop);
784 exe->desktop = NULL;
785 if (exe->key)
786 {
787 exe->desktop = efreet_desktop_get(exe->key);
788 if (!exe->desktop)
789 exe->desktop = efreet_util_desktop_name_find(exe->key);
790 }
791 }
792 }
793 }
794 eina_iterator_free(it);
795 return ECORE_CALLBACK_PASS_ON;
796 }
797
798 static Eina_Bool
_e_exec_startup_id_pid_find(const Eina_Hash * hash EINA_UNUSED,const void * key EINA_UNUSED,void * value,void * data)799 _e_exec_startup_id_pid_find(const Eina_Hash *hash EINA_UNUSED, const void *key EINA_UNUSED, void *value, void *data)
800 {
801 E_Exec_Search *search;
802 E_Exec_Instance *inst;
803 Eina_List *l;
804
805 search = data;
806 EINA_LIST_FOREACH(value, l, inst)
807 {
808 pid_t exe_pid;
809
810 exe_pid = 0;
811 if (inst->exe)
812 {
813 exe_pid = ecore_exe_pid_get(inst->exe);
814 if (exe_pid <= 0) inst->exe = NULL;
815 }
816 if (((search->desktop) &&
817 (search->desktop == inst->desktop)) ||
818
819 ((search->startup_id > 0) &&
820 (search->startup_id == inst->startup_id)) ||
821
822 ((inst->exe) && (search->pid > 1) && (!inst->phony) &&
823 (search->pid == exe_pid)))
824 {
825 search->inst = inst;
826 return EINA_FALSE;
827 }
828 }
829 return EINA_TRUE;
830 }
831
832 static void
_e_exec_error_dialog(Efreet_Desktop * desktop,const char * exec,Ecore_Exe_Event_Del * exe_event,Ecore_Exe_Event_Data * exe_error,Ecore_Exe_Event_Data * exe_read)833 _e_exec_error_dialog(Efreet_Desktop *desktop, const char *exec, Ecore_Exe_Event_Del *exe_event,
834 Ecore_Exe_Event_Data *exe_error, Ecore_Exe_Event_Data *exe_read)
835 {
836 E_Config_Dialog_View *v;
837 E_Config_Dialog_Data *cfdata;
838
839 v = E_NEW(E_Config_Dialog_View, 1);
840 if (!v) return;
841 cfdata = E_NEW(E_Config_Dialog_Data, 1);
842 if (!cfdata)
843 {
844 E_FREE(v);
845 return;
846 }
847 cfdata->desktop = desktop;
848 if (cfdata->desktop) efreet_desktop_ref(cfdata->desktop);
849 if (exec) cfdata->exec = strdup(exec);
850 cfdata->error = exe_error;
851 cfdata->read = exe_read;
852 cfdata->event = *exe_event;
853
854 v->create_cfdata = _create_data;
855 v->free_cfdata = _free_data;
856 v->basic.create_widgets = _basic_create_widgets;
857 v->advanced.create_widgets = _advanced_create_widgets;
858
859 /* Create The Dialog */
860 e_config_dialog_new(NULL, _("Application Execution Error"),
861 "E", "_e_exec_error_exit_dialog",
862 NULL, 0, v, cfdata);
863 }
864
865 static void
_fill_data(E_Config_Dialog_Data * cfdata)866 _fill_data(E_Config_Dialog_Data *cfdata)
867 {
868 char buf[4096];
869
870 if (!cfdata->label)
871 {
872 if (cfdata->desktop)
873 snprintf(buf, sizeof(buf), _("%s stopped running unexpectedly."), cfdata->desktop->name);
874 else
875 snprintf(buf, sizeof(buf), _("%s stopped running unexpectedly."), cfdata->exec);
876 cfdata->label = strdup(buf);
877 }
878 if ((cfdata->event.exited) && (!cfdata->exit))
879 {
880 snprintf(buf, sizeof(buf),
881 _("An exit code of %i was returned from %s."),
882 cfdata->event.exit_code, cfdata->exec);
883 cfdata->exit = strdup(buf);
884 }
885 if ((cfdata->event.signalled) && (!cfdata->signal))
886 {
887 if (cfdata->event.exit_signal == SIGINT)
888 snprintf(buf, sizeof(buf),
889 _("%s was interrupted by an Interrupt Signal."),
890 cfdata->exec);
891 else if (cfdata->event.exit_signal == SIGQUIT)
892 snprintf(buf, sizeof(buf), _("%s was interrupted by a Quit Signal."),
893 cfdata->exec);
894 else if (cfdata->event.exit_signal == SIGABRT)
895 snprintf(buf, sizeof(buf),
896 _("%s was interrupted by an Abort Signal."), cfdata->exec);
897 else if (cfdata->event.exit_signal == SIGFPE)
898 snprintf(buf, sizeof(buf),
899 _("%s was interrupted by a Floating Point Error."),
900 cfdata->exec);
901 else if (cfdata->event.exit_signal == SIGKILL)
902 snprintf(buf, sizeof(buf),
903 _("%s was interrupted by an Uninterruptable Kill Signal."),
904 cfdata->exec);
905 else if (cfdata->event.exit_signal == SIGSEGV)
906 snprintf(buf, sizeof(buf),
907 _("%s was interrupted by a Segmentation Fault."),
908 cfdata->exec);
909 else if (cfdata->event.exit_signal == SIGPIPE)
910 snprintf(buf, sizeof(buf),
911 _("%s was interrupted by a Broken Pipe."), cfdata->exec);
912 else if (cfdata->event.exit_signal == SIGTERM)
913 snprintf(buf, sizeof(buf),
914 _("%s was interrupted by a Termination Signal."),
915 cfdata->exec);
916 else if (cfdata->event.exit_signal == SIGBUS)
917 snprintf(buf, sizeof(buf),
918 _("%s was interrupted by a Bus Error."), cfdata->exec);
919 else
920 snprintf(buf, sizeof(buf),
921 _("%s was interrupted by the signal number %i."),
922 cfdata->exec, cfdata->event.exit_signal);
923 cfdata->signal = strdup(buf);
924 /* FIXME: Add sigchld_info stuff
925 * cfdata->event.data
926 * siginfo_t
927 * {
928 * int si_signo; Signal number
929 * int si_errno; An errno value
930 * int si_code; Signal code
931 * pid_t si_pid; Sending process ID
932 * uid_t si_uid; Real user ID of sending process
933 * int si_status; Exit value or signal
934 * clock_t si_utime; User time consumed
935 * clock_t si_stime; System time consumed
936 * sigval_t si_value; Signal value
937 * int si_int; POSIX.1b signal
938 * void * si_ptr; POSIX.1b signal
939 * void * si_addr; Memory location which caused fault
940 * int si_band; Band event
941 * int si_fd; File descriptor
942 * }
943 */
944 }
945 }
946
947 static void *
_create_data(E_Config_Dialog * cfd)948 _create_data(E_Config_Dialog *cfd)
949 {
950 E_Config_Dialog_Data *cfdata;
951
952 cfdata = cfd->data;
953 _fill_data(cfdata);
954 return cfdata;
955 }
956
957 static void
_free_data(E_Config_Dialog * cfd EINA_UNUSED,E_Config_Dialog_Data * cfdata)958 _free_data(E_Config_Dialog *cfd EINA_UNUSED, E_Config_Dialog_Data *cfdata)
959 {
960 if (cfdata->error) ecore_exe_event_data_free(cfdata->error);
961 if (cfdata->read) ecore_exe_event_data_free(cfdata->read);
962
963 if (cfdata->desktop) efreet_desktop_free(cfdata->desktop);
964
965 E_FREE(cfdata->exec);
966 E_FREE(cfdata->signal);
967 E_FREE(cfdata->exit);
968 E_FREE(cfdata->label);
969 E_FREE(cfdata);
970 }
971
972 static Evas_Object *
_dialog_scrolltext_create(Evas * evas,char * title,Ecore_Exe_Event_Data_Line * lines)973 _dialog_scrolltext_create(Evas *evas, char *title, Ecore_Exe_Event_Data_Line *lines)
974 {
975 Evas_Object *obj, *os;
976 char *text;
977 char *trunc_note = _("***The remaining output has been truncated. Save the output to view.***\n");
978 int tlen, max_lines, i;
979
980 os = e_widget_framelist_add(evas, _(title), 0);
981
982 obj = e_widget_textblock_add(evas);
983
984 tlen = 0;
985 for (i = 0; lines[i].line; i++)
986 {
987 tlen += lines[i].size + 1;
988 /* When the program output is extraordinarily long, it can cause
989 * significant delays during text rendering. Limit to a fixed
990 * number of characters. */
991 if (tlen > MAX_OUTPUT_CHARACTERS)
992 {
993 tlen -= lines[i].size + 1;
994 tlen += strlen(trunc_note);
995 break;
996 }
997 }
998 max_lines = i;
999 text = alloca(tlen + 1);
1000
1001 text[0] = 0;
1002 for (i = 0; i < max_lines; i++)
1003 {
1004 strcat(text, lines[i].line);
1005 strcat(text, "\n");
1006 }
1007
1008 /* Append the warning about truncated output. */
1009 if (lines[max_lines].line) strcat(text, trunc_note);
1010
1011 e_widget_textblock_plain_set(obj, text);
1012 e_widget_size_min_set(obj, 240, 120);
1013
1014 e_widget_framelist_object_append(os, obj);
1015
1016 return os;
1017 }
1018
1019 static Evas_Object *
_basic_create_widgets(E_Config_Dialog * cfd EINA_UNUSED,Evas * evas,E_Config_Dialog_Data * cfdata)1020 _basic_create_widgets(E_Config_Dialog *cfd EINA_UNUSED, Evas *evas, E_Config_Dialog_Data *cfdata)
1021 {
1022 char buf[4096];
1023 int error_length = 0;
1024 Evas_Object *o, *ob, *os;
1025
1026 _fill_data(cfdata);
1027
1028 o = e_widget_list_add(evas, 0, 0);
1029
1030 ob = e_widget_label_add(evas, cfdata->label);
1031 e_widget_list_object_append(o, ob, 1, 1, 0.5);
1032
1033 if (cfdata->error) error_length = cfdata->error->size;
1034 if (error_length)
1035 {
1036 os = _dialog_scrolltext_create(evas, _("Error Logs"),
1037 cfdata->error->lines);
1038 e_widget_list_object_append(o, os, 1, 1, 0.5);
1039 }
1040 else
1041 {
1042 ob = e_widget_label_add(evas, _("There was no error message."));
1043 e_widget_list_object_append(o, ob, 1, 1, 0.5);
1044 }
1045
1046 ob = e_widget_button_add(evas, _("Save This Message"), "system-run",
1047 _dialog_save_cb, NULL, cfdata);
1048 e_widget_list_object_append(o, ob, 0, 0, 0.5);
1049
1050 if (cfdata->desktop)
1051 snprintf(buf, sizeof(buf), _("This error log will be saved as %s/%s.log"),
1052 e_user_homedir_get(), cfdata->desktop->name);
1053 else
1054 snprintf(buf, sizeof(buf), _("This error log will be saved as %s/%s.log"),
1055 e_user_homedir_get(), "Error");
1056 ob = e_widget_label_add(evas, buf);
1057 e_widget_list_object_append(o, ob, 1, 1, 0.5);
1058
1059 return o;
1060 }
1061
1062 static Evas_Object *
_advanced_create_widgets(E_Config_Dialog * cfd EINA_UNUSED,Evas * evas,E_Config_Dialog_Data * cfdata)1063 _advanced_create_widgets(E_Config_Dialog *cfd EINA_UNUSED, Evas *evas, E_Config_Dialog_Data *cfdata)
1064 {
1065 char buf[4096];
1066 int read_length = 0;
1067 int error_length = 0;
1068 Evas_Object *o, *of, *ob, *ot;
1069
1070 _fill_data(cfdata);
1071
1072 o = e_widget_list_add(evas, 0, 0);
1073 ot = e_widget_table_add(e_win_evas_win_get(evas), 0);
1074
1075 ob = e_widget_label_add(evas, cfdata->label);
1076 e_widget_list_object_append(o, ob, 1, 1, 0.5);
1077
1078 if (cfdata->exit)
1079 {
1080 of = e_widget_framelist_add(evas, _("Error Information"), 0);
1081 ob = e_widget_label_add(evas, _(cfdata->exit));
1082 e_widget_framelist_object_append(of, ob);
1083 e_widget_list_object_append(o, of, 1, 1, 0.5);
1084 }
1085
1086 if (cfdata->signal)
1087 {
1088 of = e_widget_framelist_add(evas, _("Error Signal Information"), 0);
1089 ob = e_widget_label_add(evas, _(cfdata->signal));
1090 e_widget_framelist_object_append(of, ob);
1091 e_widget_list_object_append(o, of, 1, 1, 0.5);
1092 }
1093
1094 if (cfdata->read) read_length = cfdata->read->size;
1095
1096 if (read_length)
1097 {
1098 of = _dialog_scrolltext_create(evas, _("Output Data"),
1099 cfdata->read->lines);
1100 /* FIXME: Add stdout "start". */
1101 /* FIXME: Add stdout "end". */
1102 }
1103 else
1104 {
1105 of = e_widget_framelist_add(evas, _("Output Data"), 0);
1106 ob = e_widget_label_add(evas, _("There was no output."));
1107 e_widget_framelist_object_append(of, ob);
1108 }
1109 e_widget_table_object_append(ot, of, 0, 0, 1, 1, 1, 1, 1, 1);
1110
1111 if (cfdata->error) error_length = cfdata->error->size;
1112 if (error_length)
1113 {
1114 of = _dialog_scrolltext_create(evas, _("Error Logs"),
1115 cfdata->error->lines);
1116 /* FIXME: Add stderr "start". */
1117 /* FIXME: Add stderr "end". */
1118 }
1119 else
1120 {
1121 of = e_widget_framelist_add(evas, _("Error Logs"), 0);
1122 ob = e_widget_label_add(evas, _("There was no error message."));
1123 e_widget_framelist_object_append(of, ob);
1124 }
1125 e_widget_table_object_append(ot, of, 1, 0, 1, 1, 1, 1, 1, 1);
1126
1127 e_widget_list_object_append(o, ot, 1, 1, 0.5);
1128
1129 ob = e_widget_button_add(evas, _("Save This Message"), "system-run",
1130 _dialog_save_cb, NULL, cfdata);
1131 e_widget_list_object_append(o, ob, 0, 0, 0.5);
1132
1133 if (cfdata->desktop)
1134 snprintf(buf, sizeof(buf), _("This error log will be saved as %s/%s.log"),
1135 e_user_homedir_get(), cfdata->desktop->name);
1136 else
1137 snprintf(buf, sizeof(buf), _("This error log will be saved as %s/%s.log"),
1138 e_user_homedir_get(), "Error");
1139 ob = e_widget_label_add(evas, buf);
1140 e_widget_list_object_append(o, ob, 1, 1, 0.5);
1141
1142 return o;
1143 }
1144
1145 static void
_dialog_save_cb(void * data EINA_UNUSED,void * data2)1146 _dialog_save_cb(void *data EINA_UNUSED, void *data2)
1147 {
1148 E_Config_Dialog_Data *cfdata;
1149 FILE *f;
1150 char *text;
1151 char buf[1024];
1152 char buffer[4096];
1153 int read_length = 0;
1154 int i, tlen;
1155
1156 cfdata = data2;
1157
1158 if (cfdata->desktop)
1159 snprintf(buf, sizeof(buf), "%s/%s.log", e_user_homedir_get(),
1160 e_util_filename_escape(cfdata->desktop->name));
1161 else
1162 snprintf(buf, sizeof(buf), "%s/%s.log", e_user_homedir_get(),
1163 "Error");
1164 f = fopen(buf, "w");
1165 if (!f) return;
1166
1167 if (cfdata->exit)
1168 {
1169 snprintf(buffer, sizeof(buffer), "Error Information:\n\t%s\n\n",
1170 cfdata->exit);
1171 fwrite(buffer, sizeof(char), strlen(buffer), f);
1172 }
1173 if (cfdata->signal)
1174 {
1175 snprintf(buffer, sizeof(buffer), "Error Signal Information:\n\t%s\n\n",
1176 cfdata->signal);
1177 fwrite(buffer, sizeof(char), strlen(buffer), f);
1178 }
1179
1180 if (cfdata->read) read_length = cfdata->read->size;
1181
1182 if (read_length)
1183 {
1184 tlen = 0;
1185 for (i = 0; cfdata->read->lines[i].line; i++)
1186 tlen += cfdata->read->lines[i].size + 2;
1187 text = alloca(tlen + 1);
1188 text[0] = 0;
1189 for (i = 0; cfdata->read->lines[i].line; i++)
1190 {
1191 strcat(text, "\t");
1192 strcat(text, cfdata->read->lines[i].line);
1193 strcat(text, "\n");
1194 }
1195 snprintf(buffer, sizeof(buffer), "Output Data:\n%s\n\n", text);
1196 fwrite(buffer, sizeof(char), strlen(buffer), f);
1197 }
1198 else
1199 {
1200 snprintf(buffer, sizeof(buffer), "Output Data:\n\tThere was no output\n\n");
1201 fwrite(buffer, sizeof(char), strlen(buffer), f);
1202 }
1203
1204 /* Reusing this var */
1205 read_length = 0;
1206 if (cfdata->error) read_length = cfdata->error->size;
1207
1208 if (read_length)
1209 {
1210 tlen = 0;
1211 for (i = 0; cfdata->error->lines[i].line; i++)
1212 tlen += cfdata->error->lines[i].size + 1;
1213 text = alloca(tlen + 1);
1214 text[0] = 0;
1215 for (i = 0; cfdata->error->lines[i].line; i++)
1216 {
1217 strcat(text, "\t");
1218 strcat(text, cfdata->error->lines[i].line);
1219 strcat(text, "\n");
1220 }
1221 snprintf(buffer, sizeof(buffer), "Error Logs:\n%s\n", text);
1222 fwrite(buffer, sizeof(char), strlen(buffer), f);
1223 }
1224 else
1225 {
1226 snprintf(buffer, sizeof(buffer), "Error Logs:\n\tThere was no error message\n");
1227 fwrite(buffer, sizeof(char), strlen(buffer), f);
1228 }
1229
1230 fclose(f);
1231 }
1232
1233 E_API const Eina_List *
e_exec_desktop_instances_find(const Efreet_Desktop * desktop)1234 e_exec_desktop_instances_find(const Efreet_Desktop *desktop)
1235 {
1236 EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, NULL);
1237 return eina_hash_find(e_exec_instances, desktop->orig_path ?: desktop->name);
1238 }
1239
1240 E_API const Eina_Hash *
e_exec_instances_get(void)1241 e_exec_instances_get(void)
1242 {
1243 return e_exec_instances;
1244 }
1245