#include "e.h" /* local subsystem functions */ static Eina_Bool _e_sys_cb_timer(void *data); static Eina_Bool _e_sys_cb_exit(void *data, int type, void *event); static void _e_sys_cb_logout_logout(void *data, E_Dialog *dia); static void _e_sys_cb_logout_wait(void *data, E_Dialog *dia); static void _e_sys_cb_logout_abort(void *data, E_Dialog *dia); static Eina_Bool _e_sys_cb_logout_timer(void *data); static void _e_sys_logout_after(void); static void _e_sys_logout_begin(E_Sys_Action a_after); static void _e_sys_current_action(void); static void _e_sys_action_failed(void); static int _e_sys_action_do(E_Sys_Action a, char *param); static void _e_sys_dialog_cb_delete(E_Obj_Dialog *od); static Ecore_Event_Handler *_e_sys_exe_exit_handler = NULL; static Ecore_Exe *_e_sys_halt_check_exe = NULL; static Ecore_Exe *_e_sys_reboot_check_exe = NULL; static Ecore_Exe *_e_sys_suspend_check_exe = NULL; static Ecore_Exe *_e_sys_hibernate_check_exe = NULL; static int _e_sys_can_halt = 0; static int _e_sys_can_reboot = 0; static int _e_sys_can_suspend = 0; static int _e_sys_can_hibernate = 0; static E_Sys_Action _e_sys_action_current = E_SYS_NONE; static E_Sys_Action _e_sys_action_after = E_SYS_NONE; static Ecore_Exe *_e_sys_exe = NULL; static double _e_sys_begin_time = 0.0; static double _e_sys_logout_begin_time = 0.0; static Ecore_Timer *_e_sys_logout_timer = NULL; static E_Obj_Dialog *_e_sys_dialog = NULL; static E_Dialog *_e_sys_logout_confirm_dialog = NULL; static const int E_LOGOUT_AUTO_TIME = 60; static const int E_LOGOUT_WAIT_TIME = 15; /* externally accessible functions */ EINTERN int e_sys_init(void) { /* this is not optimal - but it does work cleanly */ _e_sys_exe_exit_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _e_sys_cb_exit, NULL); /* delay this for 1.0 seconds while the rest of e starts up */ ecore_timer_add(1.0, _e_sys_cb_timer, NULL); return 1; } EINTERN int e_sys_shutdown(void) { if (_e_sys_exe_exit_handler) ecore_event_handler_del(_e_sys_exe_exit_handler); _e_sys_exe_exit_handler = NULL; _e_sys_halt_check_exe = NULL; _e_sys_reboot_check_exe = NULL; _e_sys_suspend_check_exe = NULL; _e_sys_hibernate_check_exe = NULL; return 1; } EAPI int e_sys_action_possible_get(E_Sys_Action a) { switch (a) { case E_SYS_EXIT: case E_SYS_RESTART: case E_SYS_EXIT_NOW: return 1; case E_SYS_LOGOUT: return 1; case E_SYS_HALT: case E_SYS_HALT_NOW: return _e_sys_can_halt; case E_SYS_REBOOT: return _e_sys_can_reboot; case E_SYS_SUSPEND: return _e_sys_can_suspend; case E_SYS_HIBERNATE: return _e_sys_can_hibernate; default: return 0; } return 0; } EAPI int e_sys_action_do(E_Sys_Action a, char *param) { int ret = 0; if (_e_sys_action_current != E_SYS_NONE) { _e_sys_current_action(); return 0; } switch (a) { case E_SYS_EXIT: case E_SYS_RESTART: case E_SYS_EXIT_NOW: case E_SYS_LOGOUT: case E_SYS_SUSPEND: case E_SYS_HIBERNATE: case E_SYS_HALT_NOW: ret = _e_sys_action_do(a, param); break; case E_SYS_HALT: case E_SYS_REBOOT: if (!e_util_immortal_check()) _e_sys_logout_begin(a); return 1; break; default: break; } if (ret) _e_sys_action_current = a; else _e_sys_action_current = E_SYS_NONE; return ret; } static Eina_List *extra_actions = NULL; EAPI E_Sys_Con_Action * e_sys_con_extra_action_register(const char *label, const char *icon_group, const char *button_name, void (*func)(void *data), const void *data) { E_Sys_Con_Action *sca; sca = E_NEW(E_Sys_Con_Action, 1); if (label) sca->label = eina_stringshare_add(label); if (icon_group) sca->icon_group = eina_stringshare_add(icon_group); if (button_name) sca->button_name = eina_stringshare_add(button_name); sca->func = func; sca->data = data; extra_actions = eina_list_append(extra_actions, sca); return sca; } EAPI void e_sys_con_extra_action_unregister(E_Sys_Con_Action *sca) { extra_actions = eina_list_remove(extra_actions, sca); if (sca->label) eina_stringshare_del(sca->label); if (sca->icon_group) eina_stringshare_del(sca->icon_group); if (sca->button_name) eina_stringshare_del(sca->button_name); free(sca); } EAPI const Eina_List * e_sys_con_extra_action_list_get(void) { return extra_actions; } /* local subsystem functions */ static Eina_Bool _e_sys_cb_timer(void *data __UNUSED__) { /* exec out sys helper and ask it to test if we are allowed to do these * things */ char buf[4096]; e_init_status_set(_("Checking System Permissions")); snprintf(buf, sizeof(buf), "%s/enlightenment/utils/enlightenment_sys -t halt", e_prefix_lib_get()); _e_sys_halt_check_exe = ecore_exe_run(buf, NULL); snprintf(buf, sizeof(buf), "%s/enlightenment/utils/enlightenment_sys -t reboot", e_prefix_lib_get()); _e_sys_reboot_check_exe = ecore_exe_run(buf, NULL); snprintf(buf, sizeof(buf), "%s/enlightenment/utils/enlightenment_sys -t suspend", e_prefix_lib_get()); _e_sys_suspend_check_exe = ecore_exe_run(buf, NULL); snprintf(buf, sizeof(buf), "%s/enlightenment/utils/enlightenment_sys -t hibernate", e_prefix_lib_get()); _e_sys_hibernate_check_exe = ecore_exe_run(buf, NULL); return ECORE_CALLBACK_CANCEL; } static Eina_Bool _e_sys_cb_exit(void *data __UNUSED__, int type __UNUSED__, void *event) { Ecore_Exe_Event_Del *ev; ev = event; if ((_e_sys_exe) && (ev->exe == _e_sys_exe)) { if (ev->exit_code != 0) _e_sys_action_failed(); if (((_e_sys_action_current != E_SYS_HALT) && (_e_sys_action_current != E_SYS_HALT_NOW) && (_e_sys_action_current != E_SYS_REBOOT)) || (ev->exit_code != 0)) { if (_e_sys_dialog) { e_object_del(E_OBJECT(_e_sys_dialog)); _e_sys_dialog = NULL; } } _e_sys_action_current = E_SYS_NONE; _e_sys_exe = NULL; return ECORE_CALLBACK_RENEW; } if ((_e_sys_halt_check_exe) && (ev->exe == _e_sys_halt_check_exe)) { e_init_status_set(_("System Check Done")); /* exit_code: 0 == OK, 5 == suid root removed, 7 == group id error * 10 == permission denied, 20 == action undefined */ if (ev->exit_code == 0) { _e_sys_can_halt = 1; _e_sys_halt_check_exe = NULL; } } else if ((_e_sys_reboot_check_exe) && (ev->exe == _e_sys_reboot_check_exe)) { e_init_status_set(_("System Check Done")); if (ev->exit_code == 0) { _e_sys_can_reboot = 1; _e_sys_reboot_check_exe = NULL; } } else if ((_e_sys_suspend_check_exe) && (ev->exe == _e_sys_suspend_check_exe)) { e_init_status_set(_("System Check Done")); if (ev->exit_code == 0) { _e_sys_can_suspend = 1; _e_sys_suspend_check_exe = NULL; } } else if ((_e_sys_hibernate_check_exe) && (ev->exe == _e_sys_hibernate_check_exe)) { e_init_status_set(_("System Check Done")); if (ev->exit_code == 0) { _e_sys_can_hibernate = 1; _e_sys_hibernate_check_exe = NULL; } } return ECORE_CALLBACK_RENEW; } static void _e_sys_cb_logout_logout(void *data __UNUSED__, E_Dialog *dia) { if (_e_sys_logout_timer) { ecore_timer_del(_e_sys_logout_timer); _e_sys_logout_timer = NULL; } _e_sys_logout_begin_time = 0.0; _e_sys_logout_after(); e_object_del(E_OBJECT(dia)); _e_sys_logout_confirm_dialog = NULL; } static void _e_sys_cb_logout_wait(void *data __UNUSED__, E_Dialog *dia) { if (_e_sys_logout_timer) ecore_timer_del(_e_sys_logout_timer); _e_sys_logout_timer = ecore_timer_add(0.5, _e_sys_cb_logout_timer, NULL); _e_sys_logout_begin_time = ecore_time_get(); e_object_del(E_OBJECT(dia)); _e_sys_logout_confirm_dialog = NULL; } static void _e_sys_cb_logout_abort(void *data __UNUSED__, E_Dialog *dia) { if (_e_sys_logout_timer) { ecore_timer_del(_e_sys_logout_timer); _e_sys_logout_timer = NULL; } _e_sys_logout_begin_time = 0.0; e_object_del(E_OBJECT(dia)); _e_sys_logout_confirm_dialog = NULL; _e_sys_action_current = E_SYS_NONE; _e_sys_action_after = E_SYS_NONE; if (_e_sys_dialog) { e_object_del(E_OBJECT(_e_sys_dialog)); _e_sys_dialog = NULL; } } static void _e_sys_logout_confirm_dialog_update(int remaining) { char txt[PATH_MAX]; if (!_e_sys_logout_confirm_dialog) { fputs("ERROR: updating logout confirm dialog, but none exists!\n", stderr); return; } snprintf(txt, sizeof(txt), _("Logout is taking too long.
" "Some applications refuse to close.
" "Do you want to finish the logout
" "anyway without closing these
" "applications first?

" "Auto logout in %d seconds."), remaining); e_dialog_text_set(_e_sys_logout_confirm_dialog, txt); } static Eina_Bool _e_sys_cb_logout_timer(void *data __UNUSED__) { Eina_List *l; E_Border *bd; int pending = 0; EINA_LIST_FOREACH(e_border_client_list(), l, bd) { if (!bd->internal) pending++; } if (pending == 0) goto after; else if (_e_sys_logout_confirm_dialog) { int remaining = E_LOGOUT_AUTO_TIME - round(ecore_loop_time_get() - _e_sys_logout_begin_time); /* it has taken 60 (E_LOGOUT_AUTO_TIME) seconds of waiting the * confirm dialog and we still have apps that will not go * away. Do the action as user may be far away or forgot it. * * NOTE: this is the behavior for many operating systems and I * guess the reason is people that hit "shutdown" and * put their laptops in their backpacks in the hope * everything will be turned off properly. */ if (remaining > 0) { _e_sys_logout_confirm_dialog_update(remaining); return ECORE_CALLBACK_RENEW; } else { _e_sys_cb_logout_logout(NULL, _e_sys_logout_confirm_dialog); return ECORE_CALLBACK_CANCEL; } } else { /* it has taken 15 seconds of waiting and we still have apps that * will not go away */ double now = ecore_loop_time_get(); if ((now - _e_sys_logout_begin_time) > E_LOGOUT_WAIT_TIME) { E_Dialog *dia; dia = e_dialog_new(e_container_current_get(e_manager_current_get()), "E", "_sys_error_logout_slow"); if (dia) { _e_sys_logout_confirm_dialog = dia; e_dialog_title_set(dia, _("Logout problems")); e_dialog_icon_set(dia, "system-log-out", 64); e_dialog_button_add(dia, _("Logout now"), NULL, _e_sys_cb_logout_logout, NULL); e_dialog_button_add(dia, _("Wait longer"), NULL, _e_sys_cb_logout_wait, NULL); e_dialog_button_add(dia, _("Cancel Logout"), NULL, _e_sys_cb_logout_abort, NULL); e_dialog_button_focus_num(dia, 1); _e_sys_logout_confirm_dialog_update(E_LOGOUT_AUTO_TIME); e_win_centered_set(dia->win, 1); e_dialog_show(dia); _e_sys_logout_begin_time = now; } return ECORE_CALLBACK_RENEW; } } return ECORE_CALLBACK_RENEW; after: _e_sys_logout_after(); _e_sys_logout_timer = NULL; return ECORE_CALLBACK_CANCEL; } static void _e_sys_logout_after(void) { if (_e_sys_dialog) { e_object_del(E_OBJECT(_e_sys_dialog)); _e_sys_dialog = NULL; } _e_sys_action_current = _e_sys_action_after; _e_sys_action_do(_e_sys_action_after, NULL); _e_sys_action_after = E_SYS_NONE; } static void _e_sys_logout_begin(E_Sys_Action a_after) { Eina_List *l; E_Border *bd; E_Obj_Dialog *od; /* start logout - at end do the a_after action */ od = e_obj_dialog_new(e_container_current_get(e_manager_current_get()), _("Logout in progress"), "E", "_sys_logout"); e_obj_dialog_obj_theme_set(od, "base/theme/sys", "e/sys/logout"); e_obj_dialog_obj_part_text_set(od, "e.textblock.message", _("Logout in progress.
" "Please wait.")); e_obj_dialog_show(od); e_obj_dialog_icon_set(od, "system-log-out"); if (_e_sys_dialog) e_object_del(E_OBJECT(_e_sys_dialog)); _e_sys_dialog = od; _e_sys_action_after = a_after; EINA_LIST_FOREACH(e_border_client_list(), l, bd) { e_border_act_close_begin(bd); } /* and poll to see if all pending windows are gone yet every 0.5 sec */ _e_sys_logout_begin_time = ecore_time_get(); if (_e_sys_logout_timer) ecore_timer_del(_e_sys_logout_timer); _e_sys_logout_timer = ecore_timer_add(0.5, _e_sys_cb_logout_timer, NULL); } static void _e_sys_current_action(void) { /* display dialog that currently an action is in progress */ E_Dialog *dia; dia = e_dialog_new(e_container_current_get(e_manager_current_get()), "E", "_sys_error_action_busy"); if (!dia) return; e_dialog_title_set(dia, _("Enlightenment is busy with another request")); e_dialog_icon_set(dia, "enlightenment/sys", 64); switch (_e_sys_action_current) { case E_SYS_LOGOUT: e_dialog_text_set(dia, _("Logging out.
" "You cannot perform other system actions
" "once a logout has begun.")); break; case E_SYS_HALT: case E_SYS_HALT_NOW: e_dialog_text_set(dia, _("Powering off.
" "You cannot do any other system actions
" "once a shutdown has been started.")); break; case E_SYS_REBOOT: e_dialog_text_set(dia, _("Resetting.
" "You cannot do any other system actions
" "once a reboot has begun.")); break; case E_SYS_SUSPEND: e_dialog_text_set(dia, _("Suspending.
" "Until suspend is complete you cannot perform
" "any other system actions.")); break; case E_SYS_HIBERNATE: e_dialog_text_set(dia, _("Hibernating.
" "You cannot perform any other system actions
" "until this is complete.")); break; default: e_dialog_text_set(dia, _("EEK! This should not happen")); break; } e_dialog_button_add(dia, _("OK"), NULL, NULL, NULL); e_dialog_button_focus_num(dia, 0); e_win_centered_set(dia->win, 1); e_dialog_show(dia); } static void _e_sys_action_failed(void) { /* display dialog that the current action failed */ E_Dialog *dia; dia = e_dialog_new(e_container_current_get(e_manager_current_get()), "E", "_sys_error_action_failed"); if (!dia) return; e_dialog_title_set(dia, _("Enlightenment is busy with another request")); e_dialog_icon_set(dia, "enlightenment/sys", 64); switch (_e_sys_action_current) { case E_SYS_HALT: case E_SYS_HALT_NOW: e_dialog_text_set(dia, _("Power off failed.")); break; case E_SYS_REBOOT: e_dialog_text_set(dia, _("Reset failed.")); break; case E_SYS_SUSPEND: e_dialog_text_set(dia, _("Suspend failed.")); break; case E_SYS_HIBERNATE: e_dialog_text_set(dia, _("Hibernate failed.")); break; default: e_dialog_text_set(dia, _("EEK! This should not happen")); break; } e_dialog_button_add(dia, _("OK"), NULL, NULL, NULL); e_dialog_button_focus_num(dia, 0); e_win_centered_set(dia->win, 1); e_dialog_show(dia); } static int _e_sys_action_do(E_Sys_Action a, char *param __UNUSED__) { char buf[PATH_MAX]; E_Obj_Dialog *od; switch (a) { case E_SYS_EXIT: // XXX TODO: check for e_fm_op_registry entries and confirm if (!e_util_immortal_check()) ecore_main_loop_quit(); else return 0; break; case E_SYS_RESTART: // XXX TODO: check for e_fm_op_registry entries and confirm // FIXME: we dont share out immortal info to restarted e. :( // if (!e_util_immortal_check()) { restart = 1; ecore_main_loop_quit(); } // else // return 0; break; case E_SYS_EXIT_NOW: exit(0); break; case E_SYS_LOGOUT: // XXX TODO: check for e_fm_op_registry entries and confirm _e_sys_logout_begin(E_SYS_EXIT); break; case E_SYS_HALT: case E_SYS_HALT_NOW: /* shutdown -h now */ if (e_util_immortal_check()) return 0; snprintf(buf, sizeof(buf), "%s/enlightenment/utils/enlightenment_sys halt", e_prefix_lib_get()); if (_e_sys_exe) { if ((ecore_time_get() - _e_sys_begin_time) > 2.0) _e_sys_current_action(); return 0; } else { _e_sys_begin_time = ecore_time_get(); _e_sys_exe = ecore_exe_run(buf, NULL); od = e_obj_dialog_new(e_container_current_get(e_manager_current_get()), _("Power off"), "E", "_sys_halt"); e_obj_dialog_obj_theme_set(od, "base/theme/sys", "e/sys/halt"); e_obj_dialog_obj_part_text_set(od, "e.textblock.message", _("Power off.
" "Please wait.")); e_obj_dialog_show(od); e_obj_dialog_icon_set(od, "system-shutdown"); if (_e_sys_dialog) e_object_del(E_OBJECT(_e_sys_dialog)); e_obj_dialog_cb_delete_set(od, _e_sys_dialog_cb_delete); _e_sys_dialog = od; /* FIXME: display halt status */ } break; case E_SYS_REBOOT: /* shutdown -r now */ if (e_util_immortal_check()) return 0; snprintf(buf, sizeof(buf), "%s/enlightenment/utils/enlightenment_sys reboot", e_prefix_lib_get()); if (_e_sys_exe) { if ((ecore_time_get() - _e_sys_begin_time) > 2.0) _e_sys_current_action(); return 0; } else { _e_sys_begin_time = ecore_time_get(); _e_sys_exe = ecore_exe_run(buf, NULL); od = e_obj_dialog_new(e_container_current_get(e_manager_current_get()), _("Resetting"), "E", "_sys_reboot"); e_obj_dialog_obj_theme_set(od, "base/theme/sys", "e/sys/reboot"); e_obj_dialog_obj_part_text_set(od, "e.textblock.message", _("Resetting.
" "Please wait.")); e_obj_dialog_show(od); e_obj_dialog_icon_set(od, "system-restart"); if (_e_sys_dialog) e_object_del(E_OBJECT(_e_sys_dialog)); e_obj_dialog_cb_delete_set(od, _e_sys_dialog_cb_delete); _e_sys_dialog = od; /* FIXME: display reboot status */ } break; case E_SYS_SUSPEND: /* /etc/acpi/sleep.sh force */ snprintf(buf, sizeof(buf), "%s/enlightenment/utils/enlightenment_sys suspend", e_prefix_lib_get()); if (_e_sys_exe) { if ((ecore_time_get() - _e_sys_begin_time) > 2.0) _e_sys_current_action(); return 0; } else { if (e_config->desklock_on_suspend) e_desklock_show(EINA_TRUE); _e_sys_begin_time = ecore_time_get(); _e_sys_exe = ecore_exe_run(buf, NULL); od = e_obj_dialog_new(e_container_current_get(e_manager_current_get()), _("Suspending"), "E", "_sys_suspend"); e_obj_dialog_obj_theme_set(od, "base/theme/sys", "e/sys/suspend"); e_obj_dialog_obj_part_text_set(od, "e.textblock.message", _("Suspending.
" "Please wait.")); e_obj_dialog_show(od); e_obj_dialog_icon_set(od, "system-suspend"); if (_e_sys_dialog) e_object_del(E_OBJECT(_e_sys_dialog)); e_obj_dialog_cb_delete_set(od, _e_sys_dialog_cb_delete); _e_sys_dialog = od; /* FIXME: display suspend status */ } break; case E_SYS_HIBERNATE: /* /etc/acpi/hibernate.sh force */ snprintf(buf, sizeof(buf), "%s/enlightenment/utils/enlightenment_sys hibernate", e_prefix_lib_get()); if (_e_sys_exe) { if ((ecore_time_get() - _e_sys_begin_time) > 2.0) _e_sys_current_action(); return 0; } else { if (e_config->desklock_on_suspend) e_desklock_show(EINA_TRUE); _e_sys_begin_time = ecore_time_get(); _e_sys_exe = ecore_exe_run(buf, NULL); od = e_obj_dialog_new(e_container_current_get(e_manager_current_get()), _("Hibernating"), "E", "_sys_hibernate"); e_obj_dialog_obj_theme_set(od, "base/theme/sys", "e/sys/hibernate"); e_obj_dialog_obj_part_text_set(od, "e.textblock.message", _("Hibernating.
" "Please wait.")); e_obj_dialog_show(od); e_obj_dialog_icon_set(od, "system-suspend-hibernate"); if (_e_sys_dialog) e_object_del(E_OBJECT(_e_sys_dialog)); _e_sys_dialog = od; e_obj_dialog_cb_delete_set(od, _e_sys_dialog_cb_delete); /* FIXME: display hibernate status */ } break; default: return 0; } return 1; } static void _e_sys_dialog_cb_delete(E_Obj_Dialog *od __UNUSED__) { /* If we don't NULL out the _e_sys_dialog, then the * ECORE_EXE_EVENT_DEL callback will trigger and segv if the window * is deleted in some other way. */ _e_sys_dialog = NULL; }