From 2f6fcf42287f177587373c0704ee6d5679fca261 Mon Sep 17 00:00:00 2001 From: Cedric Bail Date: Mon, 18 Mar 2013 00:07:34 +0900 Subject: [PATCH] e: add automatic desktop lock/unlock when a bluetooth device disapear. This use a custom implementation of l2ping to ping known device and do proper action when they go away. I don't recommend to use the auto unlonck, but it is there if someone need it. --- configure.ac | 11 +- data/etc/sysactions.conf.in | 1 + src/bin/Makefile.am | 6 +- src/bin/e_sys_l2ping.c | 101 +++++++++++++++ src/bin/e_sys_main.c | 19 +++ src/modules/bluez4/e_mod_main.c | 209 ++++++++++++++++++++++++++++---- src/modules/bluez4/e_mod_main.h | 4 +- src/modules/bluez4/ebluez4.c | 8 -- 8 files changed, 320 insertions(+), 39 deletions(-) create mode 100644 src/bin/e_sys_l2ping.c diff --git a/configure.ac b/configure.ac index 82c0a5bef..b3dbcec65 100644 --- a/configure.ac +++ b/configure.ac @@ -197,9 +197,13 @@ CPPFLAGS="${PCPPFLAGS}" AC_SUBST(cf_cflags) AC_SUBST(cf_libs) -AC_CHECK_HEADERS([bluetooth/bluetooth.h], - [have_bluetooth_h="yes"], - [have_bluetooth_h="no"]) +PKG_CHECK_MODULES([BLUEZ], [bluez], + [have_bluetooth="yes"], + [have_bluetooth="no"]) +AM_CONDITIONAL([HAVE_BLUETOOTH], [test "x${have_bluetooth}"]) +if test "x${have_bluetooth}"; then + AC_DEFINE_UNQUOTED([HAVE_BLUETOOTH], [1], [Bluetooth is there]) +fi execinfo_libs="" AC_CHECK_HEADERS([execinfo.h], [have_execinfo="yes"], [have_execinfo="no"]) @@ -487,6 +491,7 @@ PKG_CHECK_MODULES(E_FM_OPEN, [ PKG_CHECK_MODULES(E_SYS, [ eina >= ${efl_version} + ecore >= ${efl_version} ]) PKG_CHECK_MODULES(E_INIT, [ diff --git a/data/etc/sysactions.conf.in b/data/etc/sysactions.conf.in index 6054a0423..b0dfab51c 100644 --- a/data/etc/sysactions.conf.in +++ b/data/etc/sysactions.conf.in @@ -51,6 +51,7 @@ action: /bin/mount /bin/mount action: /bin/umount /bin/umount action: /usr/bin/eject /usr/bin/eject action: gdb gdb +action: l2ping l2ping # on FreeBSD use this instead of the above. #action suspend /usr/sbin/zzz diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am index fd930ef66..dfdb96b3b 100644 --- a/src/bin/Makefile.am +++ b/src/bin/Makefile.am @@ -422,10 +422,10 @@ e_fm_op.c enlightenment_fm_op_LDADD = @E_FM_OP_LIBS@ -lm enlightenment_sys_SOURCES = \ -e_sys_main.c +e_sys_main.c e_sys_l2ping.c -enlightenment_sys_LDADD = @SUID_LDFLAGS@ @E_SYS_LIBS@ -enlightenment_sys_CFLAGS = @SUID_CFLAGS@ @E_SYS_CFLAGS@ +enlightenment_sys_LDADD = @SUID_LDFLAGS@ @E_SYS_LIBS@ @BLUEZ_LIBS@ +enlightenment_sys_CFLAGS = @SUID_CFLAGS@ @E_SYS_CFLAGS@ @BLUEZ_CFLAGS@ if HAVE_EEZE enlightenment_backlight_SOURCES = \ diff --git a/src/bin/e_sys_l2ping.c b/src/bin/e_sys_l2ping.c new file mode 100644 index 000000000..ac826ea72 --- /dev/null +++ b/src/bin/e_sys_l2ping.c @@ -0,0 +1,101 @@ +#include "config.h" + +#include + +#ifdef HAVE_BLUETOOTH +#include + +#include +#include +#endif + +double +e_sys_l2ping(const char *bluetooth_mac) +{ +#ifdef HAVE_BLUETOOTH + char send_buf[L2CAP_CMD_HDR_SIZE + 1]; + char recv_buf[L2CAP_CMD_HDR_SIZE + 1]; + char tmp[18]; + l2cap_cmd_hdr *send_cmd; + l2cap_cmd_hdr *recv_cmd; + struct sockaddr_l2 addr; + struct timeval tv; + socklen_t optlen; + fd_set rfds; + double start; + int fd; + + /* Create socket */ + fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP); + if (fd < 0) { + perror("Can't create socket"); + return -1; + } + + /* Bind to local address */ + memset(&addr, 0, sizeof(addr)); + addr.l2_family = AF_BLUETOOTH; + bacpy(&addr.l2_bdaddr, BDADDR_ANY); + + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) + { + perror("Can't bind socket"); + return -1; + } + + /* Connect to remote device */ + memset(&addr, 0, sizeof(addr)); + addr.l2_family = AF_BLUETOOTH; + str2ba(bluetooth_mac, &addr.l2_bdaddr); + + if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) + { + perror("Can't connect"); + return -1; + } + + start = ecore_time_get(); + + /* Get local address */ + memset(&addr, 0, sizeof(addr)); + optlen = sizeof(addr); + + if (getsockname(fd, (struct sockaddr *) &addr, &optlen) < 0) + { + perror("Can't get local address"); + return -1; + } + + ba2str(&addr.l2_bdaddr, tmp); + + send_cmd = (l2cap_cmd_hdr *) send_buf; + send_cmd->ident = 200; + send_cmd->len = htobs(1); + send_cmd->code = L2CAP_ECHO_REQ; + send_buf[L2CAP_CMD_HDR_SIZE] = 'A'; + + if (send(fd, send_buf, L2CAP_CMD_HDR_SIZE + 1, 0) <= 0) + { + perror("Send failed"); + return -1; + } + + if (recv(fd, recv_buf, L2CAP_CMD_HDR_SIZE + 1, 0) < 0) + { + perror("Recv failed"); + return -1; + } + + recv_cmd = (l2cap_cmd_hdr *) recv_buf; + recv_cmd->len = btohs(recv_cmd->len); + if (recv_cmd->ident != 200) + return -1; /* Wrong packet */ + + close(fd); + + return ecore_time_get() - start; +#else + fprintf(stderr, "e_sys_l2ping nop\n"); + return -1; +#endif +} diff --git a/src/bin/e_sys_main.c b/src/bin/e_sys_main.c index 747836dca..180570670 100644 --- a/src/bin/e_sys_main.c +++ b/src/bin/e_sys_main.c @@ -16,6 +16,8 @@ #endif #include +double e_sys_l2ping(const char *bluetooth_mac); + /* local subsystem functions */ #ifdef HAVE_EEZE_MOUNT static Eina_Bool mountopts_check(const char *opts); @@ -88,6 +90,11 @@ main(int argc, output = argv[3]; } + else if (!strcmp(argv[1], "l2ping")) + { + action = argv[1]; + output = argv[2]; + } #ifdef HAVE_EEZE_MOUNT else { @@ -173,6 +180,18 @@ main(int argc, exit(WEXITSTATUS(r)); } + else if (!test && !strcmp(action, "l2ping")) + { + char tmp[128]; + double latency; + + latency = e_sys_l2ping(output); + + eina_convert_dtoa(latency, tmp); + fprintf(stdout, tmp); + + return (latency < 0) ? 1 : 0; + } if ((!test) #ifdef HAVE_EEZE_MOUNT && (!mnt) diff --git a/src/modules/bluez4/e_mod_main.c b/src/modules/bluez4/e_mod_main.c index f294ae9e9..594a3a0ee 100644 --- a/src/modules/bluez4/e_mod_main.c +++ b/src/modules/bluez4/e_mod_main.c @@ -4,6 +4,14 @@ #include "ebluez4.h" /* Local Variables */ +static Ecore_Exe *autolock_exe = NULL; +static Ecore_Poller *autolock_poller = NULL; +static Ecore_Event_Handler *autolock_die = NULL; +static Ecore_Event_Handler *autolock_out = NULL; +static Ecore_Event_Handler *autolock_desklock = NULL; +static Eina_Bool autolock_initted = EINA_FALSE; +static Eina_Bool autolock_waiting = EINA_TRUE; + static Eina_List *instances = NULL; static E_Module *mod = NULL; static char tmpbuf[1024]; @@ -15,6 +23,42 @@ Config *ebluez4_config = NULL; EAPI E_Module_Api e_modapi = {E_MODULE_API_VERSION, "Bluez4"}; /* Local Functions */ +static Eina_Bool +_ebluez_l2ping_poller(void *data EINA_UNUSED) +{ + Eina_Strbuf *buf; + const char *tmp = NULL; + + autolock_poller = NULL; + + buf = eina_strbuf_new(); + if (e_desklock_state_get()) + { + if (!autolock_waiting) + tmp = ebluez4_config->unlock_dev_addr; + else + tmp = ebluez4_config->lock_dev_addr; + } + else + { + if (!autolock_waiting) + tmp = ebluez4_config->lock_dev_addr; + else + tmp = ebluez4_config->unlock_dev_addr; + } + + if (tmp) + { + eina_strbuf_append_printf(buf, "%s/enlightenment/utils/enlightenment_sys l2ping %s", + e_prefix_lib_get(), tmp); + autolock_exe = ecore_exe_run(eina_strbuf_string_get(buf), NULL); + } + + eina_strbuf_free(buf); + + return 0; +} + static void _ebluez4_search_dialog_del(Instance *inst) { @@ -262,9 +306,15 @@ _ebluez4_cb_lock(void *data, int tog; tog = e_menu_item_toggle_get(mi); - eina_stringshare_replace(&ebluez4_config->lock_dev_name, - tog ? dev->name : NULL); + eina_stringshare_replace(&ebluez4_config->lock_dev_addr, + tog ? dev->addr : NULL); e_config_save_queue(); + + if (autolock_exe) + ecore_exe_kill(autolock_exe); + autolock_exe = NULL; + if (!autolock_poller && (ebluez4_config->lock_dev_addr || ebluez4_config->unlock_dev_addr)) + autolock_poller = ecore_poller_add(ECORE_POLLER_CORE, 32, _ebluez_l2ping_poller, NULL); } static void @@ -276,9 +326,15 @@ _ebluez4_cb_unlock(void *data, int tog; tog = e_menu_item_toggle_get(mi); - eina_stringshare_replace(&ebluez4_config->unlock_dev_name, - tog ? dev->name : NULL); + eina_stringshare_replace(&ebluez4_config->unlock_dev_addr, + tog ? dev->addr : NULL); e_config_save_queue(); + + if (autolock_exe) + ecore_exe_kill(autolock_exe); + autolock_exe = NULL; + if (!autolock_poller && (ebluez4_config->lock_dev_addr || ebluez4_config->unlock_dev_addr)) + autolock_poller = ecore_poller_add(ECORE_POLLER_CORE, 32, _ebluez_l2ping_poller, NULL); } static void @@ -348,22 +404,27 @@ _ebluez4_add_devices(Instance *inst) e_menu_item_label_set(submi, "Forget"); e_menu_item_callback_set(submi, _ebluez4_cb_forget, dev); - /* Auto lock when away */ - submi = e_menu_item_new(subm); - e_menu_item_check_set(submi, 1); - e_menu_item_label_set(submi, "Lock on disconnect"); - e_menu_item_callback_set(submi, _ebluez4_cb_lock, dev); - chk = ebluez4_config->lock_dev_name && dev->name && - !strcmp(dev->name, ebluez4_config->lock_dev_name); - e_menu_item_toggle_set(submi, !!chk); +#ifdef HAVE_BLUETOOTH + if (autolock_initted) + { + /* Auto lock when away */ + submi = e_menu_item_new(subm); + e_menu_item_check_set(submi, 1); + e_menu_item_label_set(submi, "Lock on disconnect"); + e_menu_item_callback_set(submi, _ebluez4_cb_lock, dev); + chk = ebluez4_config->lock_dev_addr && dev->addr && + !strcmp(dev->addr, ebluez4_config->lock_dev_addr); + e_menu_item_toggle_set(submi, !!chk); - submi = e_menu_item_new(subm); - e_menu_item_check_set(submi, 1); - e_menu_item_label_set(submi, "Unlock on disconnect"); - e_menu_item_callback_set(submi, _ebluez4_cb_unlock, dev); - chk = ebluez4_config->unlock_dev_name && dev->name && - !strcmp(dev->name, ebluez4_config->unlock_dev_name); - e_menu_item_toggle_set(submi, !!chk); + submi = e_menu_item_new(subm); + e_menu_item_check_set(submi, 1); + e_menu_item_label_set(submi, "Unlock on disconnect"); + e_menu_item_callback_set(submi, _ebluez4_cb_unlock, dev); + chk = ebluez4_config->unlock_dev_addr && dev->addr && + !strcmp(dev->addr, ebluez4_config->unlock_dev_addr); + e_menu_item_toggle_set(submi, !!chk); + } +#endif } return ret; @@ -520,10 +581,93 @@ static const E_Gadcon_Client_Class _gc_class = E_GADCON_CLIENT_STYLE_PLAIN }; +static Eina_Bool +_ebluez_exe_die(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event_info) +{ + Ecore_Exe_Event_Del *ev = event_info; + + if (ev->exe != autolock_exe) + return ECORE_CALLBACK_PASS_ON; + + if (!autolock_initted) + { + if (ev->exit_code == 0) + { + autolock_initted = EINA_TRUE; + } + } + else + { + if (e_desklock_state_get()) // Locked state ? + { + if (!autolock_waiting) + { + // Not waiting yet for the auto unlock device to appear before unlock + if (ev->exit_code == 0 && ebluez4_config->unlock_dev_addr) + { + e_desklock_hide(); + } + } + else if (ev->exit_code == 1) + { + // The device just disapeared, now we can wait for it to disapear + autolock_waiting = EINA_FALSE; + } + } + else + { + if (!autolock_waiting) + { + // Not waiting yet for the auto lock device to disappear before locking + if (ev->exit_code == 1 && ebluez4_config->lock_dev_addr) + { + e_desklock_show(EINA_FALSE); + } + } + else if (ev->exit_code == 0) + { + // The device just appeared, now we can wait for it to disapear + autolock_waiting = EINA_FALSE; + } + } + } + + if (autolock_initted) + autolock_poller = ecore_poller_add(ECORE_POLLER_CORE, 32, _ebluez_l2ping_poller, NULL); + + autolock_exe = NULL; + + return ECORE_CALLBACK_PASS_ON; +} + +static Eina_Bool +_ebluez_exe_out(void *data, int ev_type, void *ev) +{ + /* FIXME: Need experiment, but we should be able to use latency to somehow estimate distance, right ? */ + return ECORE_CALLBACK_PASS_ON; +} + +static Eina_Bool +_ebluez_desklock(void *data, int ev_type, void *ev) +{ + if (autolock_exe) + ecore_exe_kill(autolock_exe); + autolock_exe = NULL; + + if (!autolock_poller && autolock_initted && (ebluez4_config->lock_dev_addr || ebluez4_config->unlock_dev_addr)) + autolock_poller = ecore_poller_add(ECORE_POLLER_CORE, 32, _ebluez_l2ping_poller, NULL); + + autolock_waiting = EINA_TRUE; + + return ECORE_CALLBACK_PASS_ON; +} + /* Module Functions */ EAPI void * e_modapi_init(E_Module *m) { + Eina_Strbuf *buf; + mod = m; conf_edd = E_CONFIG_DD_NEW("Config", Config); @@ -531,8 +675,8 @@ e_modapi_init(E_Module *m) #undef D #define T Config #define D conf_edd - E_CONFIG_VAL(D, T, lock_dev_name, STR); - E_CONFIG_VAL(D, T, unlock_dev_name, STR); + E_CONFIG_VAL(D, T, lock_dev_addr, STR); + E_CONFIG_VAL(D, T, unlock_dev_addr, STR); ebluez4_config = e_config_domain_load("module.ebluez4", conf_edd); if (!ebluez4_config) @@ -542,6 +686,16 @@ e_modapi_init(E_Module *m) e_gadcon_provider_register(&_gc_class); + autolock_die = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _ebluez_exe_die, NULL); + autolock_out = ecore_event_handler_add(ECORE_EXE_EVENT_DATA, _ebluez_exe_out, NULL); + autolock_desklock = ecore_event_handler_add(E_EVENT_DESKLOCK, _ebluez_desklock, NULL); + + buf = eina_strbuf_new(); + eina_strbuf_append_printf(buf, "%s/enlightenment/utils/enlightenment_sys -t l2ping", + e_prefix_lib_get()); + autolock_exe = ecore_exe_run(eina_strbuf_string_get(buf), NULL); + eina_strbuf_free(buf); + return m; } @@ -550,8 +704,17 @@ e_modapi_shutdown(E_Module *m) { E_CONFIG_DD_FREE(conf_edd); - eina_stringshare_del(ebluez4_config->lock_dev_name); - eina_stringshare_del(ebluez4_config->unlock_dev_name); + if (autolock_exe) ecore_exe_kill(autolock_exe); + autolock_exe = NULL; + if (autolock_poller) ecore_timer_del(autolock_poller); + autolock_poller = NULL; + + ecore_event_handler_del(autolock_die); + ecore_event_handler_del(autolock_out); + ecore_event_handler_del(autolock_desklock); + + eina_stringshare_del(ebluez4_config->lock_dev_addr); + eina_stringshare_del(ebluez4_config->unlock_dev_addr); free(ebluez4_config); ebluez4_config = NULL; diff --git a/src/modules/bluez4/e_mod_main.h b/src/modules/bluez4/e_mod_main.h index c8d713b60..dc48b3d2c 100644 --- a/src/modules/bluez4/e_mod_main.h +++ b/src/modules/bluez4/e_mod_main.h @@ -16,8 +16,8 @@ struct _Instance typedef struct _Config Config; struct _Config { - const char *lock_dev_name; - const char *unlock_dev_name; + const char *lock_dev_addr; + const char *unlock_dev_addr; }; extern Config *ebluez4_config; diff --git a/src/modules/bluez4/ebluez4.c b/src/modules/bluez4/ebluez4.c index 2347808d1..e10ccbe48 100644 --- a/src/modules/bluez4/ebluez4.c +++ b/src/modules/bluez4/ebluez4.c @@ -385,10 +385,6 @@ _on_removed(void *context, const EDBus_Message *msg) fdev = eina_list_search_unsorted(ctxt->found_devices, _dev_addr_cmp, dev->addr); - if (dev->name && ebluez4_config->lock_dev_name && - !strcmp(dev->name, ebluez4_config->lock_dev_name)) - e_desklock_show(EINA_FALSE); - _unset_dev(dev, &ctxt->devices); _unset_dev(fdev, &ctxt->found_devices); } @@ -427,10 +423,6 @@ _on_device_found(void *context, const EDBus_Message *msg) dev->paired = paired; ctxt->found_devices = eina_list_append(ctxt->found_devices, dev); - if (dev->name && ebluez4_config->unlock_dev_name && - !strcmp(dev->name, ebluez4_config->unlock_dev_name)) - e_desklock_hide(); - ebluez4_update_instances(ctxt->found_devices); }