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