diff --git a/src/usb_moded-appsync.c b/src/usb_moded-appsync.c index bfb10b9..eac7a77 100644 --- a/src/usb_moded-appsync.c +++ b/src/usb_moded-appsync.c @@ -2,8 +2,11 @@ @file usb_moded-appsync.c Copyright (C) 2010 Nokia Corporation. All rights reserved. + Copyright (C) 2013-2016 Jolla Ltd. @author: Philippe De Swert + @author: Philippe De Swert + @author: Simo Piiroinen This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License @@ -259,9 +262,8 @@ int activate_sync(const char *mode) if(data->systemd) { if(!systemd_control_service(data->name, SYSTEMD_START)) - mark_active(data->name, 0); - else goto error; + mark_active(data->name, 0); } else if(data->launch) { @@ -320,7 +322,7 @@ int activate_sync_post(const char *mode) log_debug("launching post-enum-app %s\n", data->name); if(data->systemd) { - if(systemd_control_service(data->name, SYSTEMD_START)) + if(!systemd_control_service(data->name, SYSTEMD_START)) goto error; mark_active(data->name, 1); } @@ -442,14 +444,14 @@ static void appsync_stop_apps(int post) if(data->systemd && data->state == APP_STATE_ACTIVE && data->post == post) { log_debug("stopping %s-enum-app %s", post ? "post" : "pre", data->name); - if(systemd_control_service(data->name, SYSTEMD_STOP)) + if(!systemd_control_service(data->name, SYSTEMD_STOP)) log_debug("Failed to stop %s\n", data->name); data->state = APP_STATE_DONTCARE; } } } -int appsync_stop(int force) +int appsync_stop(gboolean force) { /* If force arg is used, stop all applications that * could have been started by usb-moded */ diff --git a/src/usb_moded-appsync.h b/src/usb_moded-appsync.h index 3cca417..f1ef638 100644 --- a/src/usb_moded-appsync.h +++ b/src/usb_moded-appsync.h @@ -1,8 +1,11 @@ /* Copyright (C) 2010 Nokia Corporation. All rights reserved. + Copyright (C) 2013-2016 Jolla Ltd. author: Philippe De Swert + author: Philippe De Swert + author: Simo Piiroinen This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License @@ -57,6 +60,6 @@ void readlist(int diag); int activate_sync(const char *mode); int activate_sync_post(const char *mode); int mark_active(const gchar *name, int post); -int appsync_stop(int force); +int appsync_stop(gboolean force); void free_appsync_list(void); void usb_moded_appsync_cleanup(void); diff --git a/src/usb_moded-dbus-private.h b/src/usb_moded-dbus-private.h index d0b3729..ee44dd2 100644 --- a/src/usb_moded-dbus-private.h +++ b/src/usb_moded-dbus-private.h @@ -1,7 +1,12 @@ /* Copyright (C) 2010 Nokia Corporation. All rights reserved. + Copyright (C) 2013-2016 Jolla Ltd. Author: Philippe De Swert + Author: Philippe De Swert + Author: Vesa Halttunen + Author: Martin Jones + Author: Simo Piiroinen This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License @@ -18,8 +23,20 @@ 02110-1301 USA */ -/* initialize dbus communication channels */ -gboolean usb_moded_dbus_init(void); +/** Logical name for org.freedesktop.DBus.GetNameOwner method */ +#define DBUS_GET_NAME_OWNER_REQ "GetNameOwner" + +/** Logical name for org.freedesktop.DBus.NameOwnerChanged signal */ +#define DBUS_NAME_OWNER_CHANGED_SIG "NameOwnerChanged" + +/* Connect to D-Bus System Bus */ +gboolean usb_moded_dbus_init_connection(void); + +/* Claim D-Bus Service Name */ +gboolean usb_moded_dbus_init_service(void); + +/* Get current SystemBus connection */ +DBusConnection *usb_moded_dbus_get_connection(void); /* cleanup usb on exit */ void usb_moded_dbus_cleanup(void); @@ -35,3 +52,11 @@ int usb_moded_send_supported_modes_signal(const char *supported_modes); /* send hidden modes signal system bus */ int usb_moded_send_hidden_modes_signal(const char *hidden_modes); + +/* Callback function type used with usb_moded_get_name_owner_async() */ +typedef void (*usb_moded_get_name_owner_fn)(const char *owner); + +/* Asynchronous GetNameOwner query */ +gboolean usb_moded_get_name_owner_async(const char *name, + usb_moded_get_name_owner_fn cb, + DBusPendingCall **ppc); diff --git a/src/usb_moded-dbus.c b/src/usb_moded-dbus.c index e1e16bf..8d5c5a3 100644 --- a/src/usb_moded-dbus.c +++ b/src/usb_moded-dbus.c @@ -2,10 +2,11 @@ @file usb_moded-dbus.c Copyright (C) 2010 Nokia Corporation. All rights reserved. - Copyright (C) 2012-2015 Jolla. All rights reserved. + Copyright (C) 2012-2016 Jolla. All rights reserved. @author: Philippe De Swert @author: Philippe De Swert + @author: Simo Piiroinen This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License @@ -44,6 +45,8 @@ #define INIT_DONE_MATCH "type='signal',interface='"INIT_DONE_INTERFACE"',member='"INIT_DONE_SIGNAL"'" static DBusConnection *dbus_connection_sys = NULL; +static gboolean have_service_name = FALSE; + extern gboolean rescue_mode; /** @@ -52,7 +55,12 @@ extern gboolean rescue_mode; static void usb_moded_send_config_signal(const char *section, const char *key, const char *value) { log_debug(USB_MODE_CONFIG_SIGNAL_NAME ": %s %s %s\n", section, key, value); - if (dbus_connection_sys) + if( !have_service_name ) + { + log_err("config notification without service: [%s] %s=%s", + section, key, value); + } + else if (dbus_connection_sys) { DBusMessage* msg = dbus_message_new_signal(USB_MODE_OBJECT, USB_MODE_INTERFACE, USB_MODE_CONFIG_SIGNAL_NAME); if (msg) { @@ -451,7 +459,6 @@ static DBusHandlerResult msg_handler(DBusConnection *const connection, DBusMessa } } - EXIT: if(reply) @@ -467,18 +474,25 @@ static DBusHandlerResult msg_handler(DBusConnection *const connection, DBusMessa return status; } +DBusConnection *usb_moded_dbus_get_connection(void) +{ + DBusConnection *connection = 0; + if( dbus_connection_sys ) + connection = dbus_connection_ref(dbus_connection_sys); + else + log_err("something asked for connection ref while unconnected"); + return connection; +} + /** - * Init dbus for usb_moded + * Establish D-Bus SystemBus connection * * @return TRUE when everything went ok */ -gboolean usb_moded_dbus_init(void) +gboolean usb_moded_dbus_init_connection(void) { gboolean status = FALSE; - DBusError error; - int ret; - - dbus_error_init(&error); + DBusError error = DBUS_ERROR_INIT; /* connect to system bus */ if ((dbus_connection_sys = dbus_bus_get(DBUS_BUS_SYSTEM, &error)) == NULL) @@ -491,6 +505,35 @@ gboolean usb_moded_dbus_init(void) if (!dbus_connection_add_filter(dbus_connection_sys, msg_handler, NULL, NULL)) goto EXIT; + /* Listen to init-done signals */ + dbus_bus_add_match(dbus_connection_sys, INIT_DONE_MATCH, 0); + + /* Connect D-Bus to the mainloop */ + dbus_connection_setup_with_g_main(dbus_connection_sys, NULL); + + /* everything went fine */ + status = TRUE; + +EXIT: + dbus_error_free(&error); + return status; +} + +/** + * Reserve "com.meego.usb_moded" D-Bus Service Name + * + * @return TRUE when everything went ok + */ +gboolean usb_moded_dbus_init_service(void) +{ + gboolean status = FALSE; + DBusError error = DBUS_ERROR_INIT; + int ret; + + if( !dbus_connection_sys ) { + goto EXIT; + } + /* Acquire D-Bus service */ ret = dbus_bus_request_name(dbus_connection_sys, USB_MODE_SERVICE, DBUS_NAME_FLAG_DO_NOT_QUEUE, &error); if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) @@ -500,19 +543,8 @@ gboolean usb_moded_dbus_init(void) log_debug("DBUS ERROR: %s, %s \n", error.name, error.message); goto EXIT; } - - /* only match on signals/methods we support (if needed) - dbus_bus_add_match(dbus_connection_sys, USB_MODE_INTERFACE, &error); - */ - - /* Listen to init-done signals */ - dbus_bus_add_match(dbus_connection_sys, INIT_DONE_MATCH, 0); - - dbus_threads_init_default(); - - /* Connect D-Bus to the mainloop */ - dbus_connection_setup_with_g_main(dbus_connection_sys, NULL); - + log_debug("claimed name %s", USB_MODE_SERVICE); + have_service_name = TRUE; /* everything went fine */ status = TRUE; @@ -521,20 +553,42 @@ gboolean usb_moded_dbus_init(void) return status; } +/** Release "com.meego.usb_moded" D-Bus Service Name + */ +static void usb_moded_dbus_cleanup_service(void) +{ + if( !have_service_name ) + goto EXIT; + + have_service_name = FALSE; + log_debug("release name %s", USB_MODE_SERVICE); + + if( dbus_connection_sys && + dbus_connection_get_is_connected(dbus_connection_sys) ) + { + dbus_bus_release_name(dbus_connection_sys, USB_MODE_SERVICE, NULL); + } + +EXIT: + return; +} + /** * Clean up the dbus connections on exit * */ void usb_moded_dbus_cleanup(void) { - /* clean up system bus connection */ - if (dbus_connection_sys != NULL) - { - dbus_bus_release_name(dbus_connection_sys, USB_MODE_SERVICE, NULL); - dbus_connection_remove_filter(dbus_connection_sys, msg_handler, NULL); - dbus_connection_unref(dbus_connection_sys); - dbus_connection_sys = NULL; - } + /* clean up system bus connection */ + if (dbus_connection_sys != NULL) + { + usb_moded_dbus_cleanup_service(); + + dbus_connection_remove_filter(dbus_connection_sys, msg_handler, NULL); + + dbus_connection_unref(dbus_connection_sys), + dbus_connection_sys = NULL; + } } /** @@ -549,6 +603,12 @@ static int usb_moded_dbus_signal(const char *signal_type, const char *content) int result = 1; DBusMessage* msg = 0; + if( !have_service_name ) + { + log_err("sending signal without service: %s(%s)", + signal_type, content); + goto EXIT; + } if(!dbus_connection_sys) { log_err("Dbus system connection broken!\n"); @@ -632,3 +692,104 @@ int usb_moded_send_hidden_modes_signal(const char *hidden_modes) { return(usb_moded_dbus_signal(USB_MODE_HIDDEN_MODES_SIGNAL_NAME, hidden_modes)); } + +/** Async reply handler for usb_moded_get_name_owner_async() + * + * @param pc Pending call object pointer + * @param aptr Notify function to call (as a void pointer) + */ +static void usb_moded_get_name_owner_cb(DBusPendingCall *pc, void *aptr) +{ + usb_moded_get_name_owner_fn cb = aptr; + + DBusMessage *rsp = 0; + const char *dta = 0; + DBusError err = DBUS_ERROR_INIT; + + if( !(rsp = dbus_pending_call_steal_reply(pc)) ) { + log_err("did not get reply"); + goto EXIT; + } + + if( dbus_set_error_from_message(&err, rsp) ) + { + if( strcmp(err.name, DBUS_ERROR_NAME_HAS_NO_OWNER) ) + log_err("error reply: %s: %s", err.name, err.message); + goto EXIT; + } + + if( !dbus_message_get_args(rsp, &err, + DBUS_TYPE_STRING, &dta, + DBUS_TYPE_INVALID) ) + { + if( strcmp(err.name, DBUS_ERROR_NAME_HAS_NO_OWNER) ) + log_err("parse error: %s: %s", err.name, err.message); + goto EXIT; + } + +EXIT: + /* Allways call the notification function. Equate any error + * situations with "service does not have an owner". */ + cb(dta ?: ""); + + if( rsp ) dbus_message_unref(rsp); + + dbus_error_free(&err); +} + +/** Helper function for making async dbus name owner queries + * + * @param name D-Bus name to query + * @param cb Function to call when async reply is received + * @param ppc Where to store pending call object, or NULL + * + * @return TRUE if method call was sent, FALSE otherwise + */ +gboolean usb_moded_get_name_owner_async(const char *name, + usb_moded_get_name_owner_fn cb, + DBusPendingCall **ppc) +{ + gboolean ack = FALSE; + DBusMessage *req = 0; + DBusPendingCall *pc = 0; + + if(!dbus_connection_sys) + goto EXIT; + + req = dbus_message_new_method_call(DBUS_INTERFACE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + DBUS_GET_NAME_OWNER_REQ); + if( !req ) { + log_err("could not create method call message"); + goto EXIT; + } + + if( !dbus_message_append_args(req, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID) ) { + log_err("could not add method call parameters"); + goto EXIT; + } + + if( !dbus_connection_send_with_reply(dbus_connection_sys, req, &pc, -1) ) + goto EXIT; + + if( !pc ) + goto EXIT; + + if( !dbus_pending_call_set_notify(pc, usb_moded_get_name_owner_cb, cb, 0) ) + goto EXIT; + + ack = TRUE; + + if( ppc ) + *ppc = pc, pc = 0; + +EXIT: + + if( pc ) dbus_pending_call_unref(pc); + if( req ) dbus_message_unref(req); + + return ack; +} diff --git a/src/usb_moded-devicelock.c b/src/usb_moded-devicelock.c index 330ba5b..d9ad5bd 100644 --- a/src/usb_moded-devicelock.c +++ b/src/usb_moded-devicelock.c @@ -1,28 +1,34 @@ /** - + @file: usb_moded-devicelock.c - + Copyright (C) 2010 Nokia Corporation. All rights reserved. + Copyright (C) 2013-2016 Jolla Ltd. @author: Philippe De Swert + @author: Philippe De Swert + @author: Jonni Rainisto + @author: Vesa Halttunen + @author: Simo Piiroinen This program is free software; you can redistribute it and/or - modify it under the terms of the Lesser GNU General Public License - version 2 as published by the Free Software Foundation. + modify it under the terms of the Lesser GNU General Public License + version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the Lesser GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + /* - * Interacts with the devicelock to know if we can expose the system contents or not -*/ + * Interacts with the devicelock to know if we can expose the system contents or not + */ /*============================================================================= */ @@ -36,122 +42,389 @@ #include "usb_moded-log.h" #include "usb_moded.h" #include "usb_moded-modes.h" +#include "usb_moded-dbus-private.h" + +/* ========================================================================= * + * devicelock state type + * ========================================================================= */ + +/** Devicelock states */ +typedef enum +{ + /** Devicelock is not active */ + DEVICE_LOCK_UNLOCKED = 0, + + /** Devicelock is active */ + DEVICE_LOCK_LOCKED = 1, + + /** Initial startup value; from usb-moded p.o.v. equals locked */ + DEVICE_LOCK_UNDEFINED = 2, +} devicelock_state_t; + +/** Return human readable representation of devicelock state enum + */ +static const char * +devicelock_state_repr(devicelock_state_t state) +{ + const char *repr = "DEVICE_LOCK_"; -static DBusHandlerResult devicelock_unlocked_cb(DBusConnection *conn, DBusMessage *msg, void *user_data); + switch( state ) + { + case DEVICE_LOCK_UNLOCKED: repr = "DEVICE_LOCK_UNLOCKED"; break; + case DEVICE_LOCK_LOCKED: repr = "DEVICE_LOCK_LOCKED"; break; + case DEVICE_LOCK_UNDEFINED: repr = "DEVICE_LOCK_UNDEFINED"; break; + default: break; + } + + return repr; +} + +/* ========================================================================= * + * module state data + * ========================================================================= */ + +/* SystemBus connection ref used for devicelock ipc */ +static DBusConnection *devicelock_con = NULL; + +/* Cached devicelock state */ +static devicelock_state_t device_lock_state = DEVICE_LOCK_UNDEFINED; + +/* Flag for: devicelock is available on system bus */ +static gboolean devicelock_is_available = FALSE; + +/* ========================================================================= * + * functionality provided to the other modules + * ========================================================================= */ -DBusConnection *dbus_conn_devicelock = NULL; - /** Checks if the device is locked. - * + * * @return 0 for unlocked, 1 for locked * */ int usb_moded_get_export_permission(void) { - DBusConnection *dbus_conn_devicelock = NULL; - DBusMessage *msg = NULL, *reply = NULL; - DBusError error; - int ret = 2; - - dbus_error_init(&error); - - if( (dbus_conn_devicelock = dbus_bus_get(DBUS_BUS_SYSTEM, &error)) == 0 ) - { - log_err("Could not connect to dbus for devicelock\n"); - } - - if ((msg = dbus_message_new_method_call(DEVICELOCK_SERVICE, DEVICELOCK_REQUEST_PATH, DEVICELOCK_REQUEST_IF, DEVICELOCK_STATE_REQ)) != NULL) - { - /* default dbus timeout is too long, timeout after 1,5 seconds */ - if ((reply = dbus_connection_send_with_reply_and_block(dbus_conn_devicelock, msg, 1500, NULL)) != NULL) - { - dbus_message_get_args(reply, NULL, DBUS_TYPE_INT32, &ret, DBUS_TYPE_INVALID); - dbus_message_unref(reply); - } - dbus_message_unref(msg); - } - dbus_connection_unref(dbus_conn_devicelock); - - log_debug("devicelock state = %d\n", ret); - return(ret); + gboolean unlocked = (device_lock_state == DEVICE_LOCK_UNLOCKED); + + // FIXME: this is reverse from what function name makes to expect + + return unlocked ? 0 : 1; } -int start_devicelock_listener(void) +/* ========================================================================= * + * devicelock state queries + * ========================================================================= */ + +static void devicelock_state_changed(devicelock_state_t state) { - DBusError err = DBUS_ERROR_INIT; + if( device_lock_state != state ) { + log_debug("devicelock state: %s -> %s", + devicelock_state_repr(device_lock_state), + devicelock_state_repr(state)); + device_lock_state = state; - if( (dbus_conn_devicelock = dbus_bus_get(DBUS_BUS_SYSTEM, &err)) == 0 ) - { - log_err("Could not connect to dbus for devicelock\n"); - goto cleanup; - } + if( device_lock_state == DEVICE_LOCK_UNLOCKED && + get_usb_connection_state() == 1 ) + { + const char *usb_mode = get_usb_mode(); + log_debug("usb_mode %s\n", usb_mode); - dbus_bus_add_match(dbus_conn_devicelock, MATCH_DEVICELOCK_SIGNALS, &err); - if( dbus_error_is_set(&err) ) - { - goto cleanup; - } - if( !dbus_connection_add_filter(dbus_conn_devicelock, devicelock_unlocked_cb , 0, 0) ) - { - log_err("adding system dbus filter for devicelock failed"); - goto cleanup; - } - //dbus_connection_setup_with_g_main(dbus_conn_devicelock, NULL); + /* if the mode is MODE_CHARGING_FALLBACK we know the user + * has not selected any mode, in case it things are still + * undefined it cannot hurt to try again to set a mode */ + if(!strcmp(usb_mode, MODE_UNDEFINED) || + !strcmp(usb_mode, MODE_CHARGING_FALLBACK)) + { + log_debug("set_usb"); + set_usb_connected_state(); + } + } + } +} -cleanup: - dbus_error_free(&err); - return(1); +static DBusPendingCall *devicelock_state_query_pc = 0; + +static void devicelock_state_cancel(void) +{ + if( devicelock_state_query_pc ) { + dbus_pending_call_cancel(devicelock_state_query_pc); + dbus_pending_call_unref(devicelock_state_query_pc), + devicelock_state_query_pc = 0; + } } -int stop_devicelock_listener(void) +static void devicelock_state_query_cb(DBusPendingCall *pending, void *aptr) { - if(dbus_conn_devicelock) - { - dbus_connection_remove_filter(dbus_conn_devicelock, devicelock_unlocked_cb, NULL); - dbus_bus_remove_match(dbus_conn_devicelock, MATCH_DEVICELOCK_SIGNALS, NULL); - dbus_connection_unref(dbus_conn_devicelock); - } + DBusMessage *rsp = 0; + dbus_int32_t dta = DEVICE_LOCK_UNDEFINED; + DBusError err = DBUS_ERROR_INIT; + + (void)aptr; + + if( !(rsp = dbus_pending_call_steal_reply(pending)) ) + { + log_err("%s.%s: no reply", + DEVICELOCK_INTERFACE, DEVICELOCK_GET_STATE_REQ); + goto EXIT; + } + + if( dbus_set_error_from_message(&err, rsp) ) + { + log_err("%s.%s: error reply: %s: %s", + DEVICELOCK_INTERFACE, DEVICELOCK_GET_STATE_REQ, + err.name, err.message); + goto EXIT; + } - return 0; + if( !dbus_message_get_args(rsp, &err, DBUS_TYPE_INT32, &dta, + DBUS_TYPE_INVALID) ) + { + log_err("%s.%s: parse error: %s: %s", + DEVICELOCK_INTERFACE, DEVICELOCK_GET_STATE_REQ, + err.name, err.message); + goto EXIT; + } + +EXIT: + devicelock_state_changed(dta); + + if( rsp ) dbus_message_unref(rsp); + + dbus_error_free(&err); + + dbus_pending_call_unref(devicelock_state_query_pc), + devicelock_state_query_pc = 0; } -static DBusHandlerResult devicelock_unlocked_cb(DBusConnection *conn, DBusMessage *msg, void *user_data) -{ - /* regardless what happens, we want that other programs get a chance to process this */ - DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - const char *interface = dbus_message_get_interface(msg); - const char *member = dbus_message_get_member(msg); - const char *object = dbus_message_get_path(msg); - int type = dbus_message_get_type(msg); - int ret=0; - - (void) user_data; - - // sanity checks - if( !interface || !member || !object ) goto cleanup; - if( type != DBUS_MESSAGE_TYPE_SIGNAL ) goto cleanup; - if( strcmp(interface, DEVICELOCK_REQUEST_IF) ) goto cleanup; - if( strcmp(object, DEVICELOCK_REQUEST_PATH) ) goto cleanup; - - // handle known signals - else if( !strcmp(member, "stateChanged") ) - { - dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &ret, DBUS_TYPE_INVALID); - log_debug("Devicelock state changed. New state = %d\n", ret); - if(ret == 0 && get_usb_connection_state() == 1 ) - { - log_debug("usb_mode %s\n", get_usb_mode()); - /* if the mode is MODE_CHARGING_FALLBACK we know the user has not selected any mode, in case it - things are still undefined it cannot hurt to try again to set a mode */ - if(!strcmp(get_usb_mode(), MODE_UNDEFINED) || !strcmp(get_usb_mode(), MODE_CHARGING_FALLBACK)) - { - log_debug("set_usb"); - set_usb_connected_state(); - } - } - } +static void devicelock_state_query(void) +{ + DBusMessage *req = NULL; + DBusPendingCall *pc = 0; + + devicelock_state_cancel(); + + log_debug("querying device lock state"); + + if( !devicelock_con ) { + log_err("not connected to system bus; skip device state query"); + goto EXIT; + } + + req = dbus_message_new_method_call(DEVICELOCK_SERVICE, + DEVICELOCK_OBJECT, + DEVICELOCK_INTERFACE, + DEVICELOCK_GET_STATE_REQ); + + if( !req ) + { + log_err("%s.%s: failed to construct request", + DEVICELOCK_INTERFACE, DEVICELOCK_GET_STATE_REQ); + goto EXIT; + } + + if( !dbus_connection_send_with_reply(devicelock_con, req, &pc, -1) ) + goto EXIT; + + if( !pc ) + goto EXIT; + + if( !dbus_pending_call_set_notify(pc, devicelock_state_query_cb, 0, 0) ) + goto EXIT; + + devicelock_state_query_pc = pc, pc = 0; + +EXIT: + + if( pc ) dbus_pending_call_unref(pc); + if( req ) dbus_message_unref(req); +} + +static void devicelock_state_signal(DBusMessage *msg) +{ + DBusError err = DBUS_ERROR_INIT; + dbus_int32_t dta = DEVICE_LOCK_LOCKED; + + if( !dbus_message_get_args(msg, &err, + DBUS_TYPE_INT32, &dta, + DBUS_TYPE_INVALID) ) + { + log_err("failed to parse %s.%s signal: %s: %s", + DEVICELOCK_INTERFACE, DEVICELOCK_STATE_CHANGED_SIG, + err.name, err.message); + } + + devicelock_state_changed(dta); + + dbus_error_free(&err); +} + +/* ========================================================================= * + * devicelock name owner tracking + * ========================================================================= */ + +static void devicelock_available_changed(const char *owner) +{ + gboolean is_available = (owner && *owner); + + if( devicelock_is_available != is_available ) { + devicelock_is_available = is_available; + log_debug("devicelock is %s", + devicelock_is_available ? "running" : "stopped"); + + /* Forget cached device state */ + devicelock_state_changed(DEVICE_LOCK_UNDEFINED); + + /* Query current state on devicelock startup */ + if( devicelock_is_available ) { + devicelock_state_query(); + } + } +} + +static DBusPendingCall *devicelock_available_pc = 0; + +static void devicelock_available_cb(const char *owner) +{ + devicelock_available_changed(owner); + + dbus_pending_call_unref(devicelock_available_pc), + devicelock_available_pc = 0; +} + +static void devicelock_available_cancel(void) +{ + if( devicelock_available_pc ) + { + dbus_pending_call_cancel(devicelock_available_pc); + dbus_pending_call_unref(devicelock_available_pc), + devicelock_available_pc = 0; + } +} + +static void devicelock_available_query(void) +{ + devicelock_available_cancel(); + + log_debug("querying %s name owner", DEVICELOCK_SERVICE); + + usb_moded_get_name_owner_async(DEVICELOCK_SERVICE, + devicelock_available_cb, + &devicelock_available_pc); +} + +static void name_owner_signal(DBusMessage *msg) +{ + DBusError err = DBUS_ERROR_INIT; + const char *name = 0; + const char *prev = 0; + const char *curr = 0; + + if( !dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &prev, + DBUS_TYPE_STRING, &curr, + DBUS_TYPE_INVALID) ) + { + log_err("failed to parse signal: %s: %s", + err.name, err.message); + } + else if( !strcmp(name, DEVICELOCK_SERVICE) ) + { + devicelock_available_changed(curr); + } + dbus_error_free(&err); +} + +/* ========================================================================= * + * dbus message filter + * ========================================================================= */ + +static DBusHandlerResult +devicelock_dbus_filter_cb(DBusConnection *con, DBusMessage *msg, void *aptr) +{ + (void)con; + (void)aptr; + + if( dbus_message_is_signal(msg, + DEVICELOCK_INTERFACE, + DEVICELOCK_STATE_CHANGED_SIG) ) + { + devicelock_state_signal(msg); + } + else if( dbus_message_is_signal(msg, + DBUS_INTERFACE_DBUS, + DBUS_NAME_OWNER_CHANGED_SIG) ) + { + name_owner_signal(msg); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +/* ========================================================================= * + * start/stop devicelock state tracking + * ========================================================================= */ + +gboolean +start_devicelock_listener(void) +{ + gboolean ack = FALSE; + + log_debug("starting devicelock listener"); + + /* Get connection ref */ + if( (devicelock_con = usb_moded_dbus_get_connection()) == 0 ) + { + log_err("Could not connect to dbus for devicelock\n"); + goto cleanup; + } + + /* Add filter callback */ + if( !dbus_connection_add_filter(devicelock_con, + devicelock_dbus_filter_cb , 0, 0) ) + { + log_err("adding system dbus filter for devicelock failed"); + goto cleanup; + } + + /* Add match without blocking / error checking */ + dbus_bus_add_match(devicelock_con, DEVICELOCK_STATE_CHANGED_MATCH,0); + dbus_bus_add_match(devicelock_con, DEVICELOCK_NAME_OWNER_CHANGED_MATCH,0); + + /* Initiate async devicelock name owner query */ + devicelock_available_query(); + + ack = TRUE; cleanup: - return result; + + return ack; } +void +stop_devicelock_listener(void) +{ + log_debug("stopping devicelock listener"); + + /* Do note leave pending queries behind */ + devicelock_state_cancel(); + devicelock_available_cancel(); + + if(devicelock_con) + { + /* Remove filter callback */ + dbus_connection_remove_filter(devicelock_con, + devicelock_dbus_filter_cb, 0); + + if( dbus_connection_get_is_connected(devicelock_con) ) { + /* Remove match without blocking / error checking */ + dbus_bus_remove_match(devicelock_con, + DEVICELOCK_STATE_CHANGED_MATCH, 0); + dbus_bus_remove_match(devicelock_con, + DEVICELOCK_NAME_OWNER_CHANGED_MATCH, 0); + } + + /* Let go of connection ref */ + dbus_connection_unref(devicelock_con), + devicelock_con = 0; + } +} diff --git a/src/usb_moded-devicelock.h b/src/usb_moded-devicelock.h index dc4f61b..8143320 100644 --- a/src/usb_moded-devicelock.h +++ b/src/usb_moded-devicelock.h @@ -1,7 +1,10 @@ /* Copyright (C) 2010 Nokia Corporation. All rights reserved. + Copyright (C) 2013-2016 Jolla Ltd. Author: Philippe De Swert + Author: Vesa Halttunen + Author: Simo Piiroinen This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License @@ -23,15 +26,20 @@ /*============================================================================= */ -#define DEVICELOCK_SERVICE "org.nemomobile.lipstick" -#define DEVICELOCK_REQUEST_PATH "/devicelock" -#define DEVICELOCK_REQUEST_IF "org.nemomobile.lipstick.devicelock" -#define DEVICELOCK_STATE_REQ "state" - -#define DEVICELOCK_LOCKED "Locked" - -#define MATCH_DEVICELOCK_SIGNALS\ - "type='signal'"\ - ",interface='"DEVICELOCK_REQUEST_IF"'"\ - ",path='"DEVICELOCK_REQUEST_PATH"'" - +#define DEVICELOCK_SERVICE "org.nemomobile.lipstick" +#define DEVICELOCK_OBJECT "/devicelock" +#define DEVICELOCK_INTERFACE "org.nemomobile.lipstick.devicelock" +#define DEVICELOCK_GET_STATE_REQ "state" +#define DEVICELOCK_STATE_CHANGED_SIG "stateChanged" + +#define DEVICELOCK_STATE_CHANGED_MATCH\ + "type='signal'"\ + ",interface='"DEVICELOCK_INTERFACE"'"\ + ",path='"DEVICELOCK_OBJECT"'"\ + ",member='"DEVICELOCK_STATE_CHANGED_SIG"'" + +#define DEVICELOCK_NAME_OWNER_CHANGED_MATCH\ + "type='signal'"\ + ",interface='"DBUS_INTERFACE_DBUS"'"\ + ",member='"DBUS_NAME_OWNER_CHANGED_SIG"'"\ + ",arg0='"DEVICELOCK_SERVICE"'" diff --git a/src/usb_moded-dsme.c b/src/usb_moded-dsme.c index fbb2ec1..0b04414 100644 --- a/src/usb_moded-dsme.c +++ b/src/usb_moded-dsme.c @@ -1,20 +1,21 @@ /** @file usb_moded-dsme.c - Copyright (C) 2013 Jolla. All rights reserved. + Copyright (C) 2013-2016 Jolla. All rights reserved. @author: Philippe De Swert @author: Jonni Rainisto + @author: Simo Piiroinen This program is free software; you can redistribute it and/or - modify it under the terms of the Lesser GNU General Public License - version 2 as published by the Free Software Foundation. + modify it under the terms of the Lesser GNU General Public License + version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the Lesser GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA @@ -25,46 +26,343 @@ #include #include "usb_moded-dsme.h" +#include "usb_moded-dbus-private.h" #include "usb_moded-log.h" +/* ========================================================================= * + * dsme dbus constants + * ========================================================================= */ + +#define DSME_DBUS_SERVICE "com.nokia.dsme" + +#define DSME_DBUS_REQUEST_PATH "/com/nokia/dsme/request" +#define DSME_DBUS_REQUEST_IFACE "com.nokia.dsme.request" +#define DSME_DBUS_GET_STATE_REQ "get_state" + +#define DSME_DBUS_SIGNAL_PATH "/com/nokia/dsme/signal" +#define DSME_DBUS_SIGNAL_IFACE "com.nokia.dsme.signal" +#define DSME_STATE_CHANGE_SIG "state_change_ind" + +#define DSME_STATE_CHANGE_MATCH\ + "type='signal'"\ + ",interface='"DSME_DBUS_SIGNAL_IFACE"'"\ + ",member='"DSME_STATE_CHANGE_SIG"'" + +#define DSME_OWNER_CHANGE_MATCH\ + "type='signal'"\ + ",interface='"DBUS_INTERFACE_DBUS"'"\ + ",member='"DBUS_NAME_OWNER_CHANGED_SIG"'"\ + ",arg0='"DSME_DBUS_SERVICE"'" + +/* ========================================================================= * + * state data + * ========================================================================= */ + +/* SystemBus connection ref used for dsme ipc */ +static DBusConnection *dsme_con = NULL; + +/* Flag for: device state == "USER" */ +static gboolean in_user_state = FALSE; + +/* Flag for: dsme is available on system bus */ +static gboolean dsme_is_available = FALSE; + /** Checks if the device is is USER-state. * * @return 1 if it is in USER-state, 0 for not * */ -int is_in_user_state(void) +gboolean is_in_user_state(void) +{ + return in_user_state; +} + +/* ========================================================================= * + * device state queries + * ========================================================================= */ + +static void device_state_changed(const char *state) +{ + gboolean to_user_state = state && !strcmp(state, "USER"); + + log_debug("device state: %s", state ?: "(null)"); + + if( in_user_state != to_user_state ) { + in_user_state = to_user_state; + log_debug("in user state: %s", + in_user_state ? "true" : "false"); + } +} + +static DBusPendingCall *device_state_query_pc = 0; + +static void device_state_cancel(void) +{ + if( device_state_query_pc ) { + dbus_pending_call_cancel(device_state_query_pc); + dbus_pending_call_unref(device_state_query_pc), + device_state_query_pc = 0; + } +} + +static void device_state_query_cb(DBusPendingCall *pending, void *aptr) +{ + DBusMessage *rsp = 0; + const char *dta = 0; + DBusError err = DBUS_ERROR_INIT; + + (void)aptr; + + if( !(rsp = dbus_pending_call_steal_reply(pending)) ) { + log_err("did not get reply"); + goto EXIT; + } + + if( dbus_set_error_from_message(&err, rsp) ) + { + log_err("error reply: %s: %s", err.name, err.message); + goto EXIT; + } + + if( !dbus_message_get_args(rsp, &err, + DBUS_TYPE_STRING, &dta, + DBUS_TYPE_INVALID) ) + { + if( strcmp(err.name, DBUS_ERROR_NAME_HAS_NO_OWNER) ) + log_err("parse error: %s: %s", err.name, err.message); + goto EXIT; + } + + device_state_changed(dta); + +EXIT: + + if( rsp ) dbus_message_unref(rsp); + + dbus_error_free(&err); + + dbus_pending_call_unref(device_state_query_pc), + device_state_query_pc = 0; +} + +static void device_state_query(void) { - DBusConnection *dbus_conn = NULL; - DBusMessage *msg = NULL, *reply = NULL; - DBusError error; - int ret = 0; - char* buffer = NULL; - - dbus_error_init(&error); - - if( (dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error)) == 0 ) - { - log_err("Could not connect to dbus systembus (is_in_user_state)\n"); - /* dbus system bus is broken or not there, so assume not in USER state */ - return(ret); - } - - if ((msg = dbus_message_new_method_call("com.nokia.dsme", "/request", "com.nokia.dsme.request", "get_state")) != NULL) - { - if ((reply = dbus_connection_send_with_reply_and_block(dbus_conn, msg, -1, NULL)) != NULL) + DBusMessage *req = NULL; + DBusPendingCall *pc = 0; + + device_state_cancel(); + + if( !dsme_con ) { + log_err("not connected to system bus; skip device state query"); + goto EXIT; + } + + req = dbus_message_new_method_call(DSME_DBUS_SERVICE, + DSME_DBUS_REQUEST_PATH, + DSME_DBUS_REQUEST_IFACE, + DSME_DBUS_GET_STATE_REQ); + if( !req ) { + log_err("failed to construct %s.%s request", + DSME_DBUS_REQUEST_IFACE, + DSME_DBUS_GET_STATE_REQ); + goto EXIT; + } + + if( !dbus_connection_send_with_reply(dsme_con, req, &pc, -1) ) + goto EXIT; + + if( !pc ) + goto EXIT; + + if( !dbus_pending_call_set_notify(pc, device_state_query_cb, 0, 0) ) + goto EXIT; + + device_state_query_pc = pc, pc = 0; + +EXIT: + + if( pc ) dbus_pending_call_unref(pc); + if( req ) dbus_message_unref(req); +} + +static void device_state_signal(DBusMessage *msg) +{ + DBusError err = DBUS_ERROR_INIT; + const char *dta = 0; + + if( !dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &dta, + DBUS_TYPE_INVALID) ) { - dbus_message_get_args(reply, &error, DBUS_TYPE_STRING, &buffer, DBUS_TYPE_INVALID); - dbus_message_unref(reply); - } - dbus_message_unref(msg); - } - dbus_connection_unref(dbus_conn); - - if(buffer) - { - log_debug("user state = %s\n", buffer); - if (strcmp(buffer, "USER")==0) ret = 1; - } - return(ret); + log_err("failed to parse signal: %s: %s", + err.name, err.message); + } + else + { + device_state_changed(dta); + } + dbus_error_free(&err); } +/* ========================================================================= * + * dsme name owner tracking + * ========================================================================= */ + +static void dsme_available_changed(const char *owner) +{ + gboolean is_available = (owner && *owner); + + if( dsme_is_available != is_available ) { + dsme_is_available = is_available; + log_debug("dsme is %s", dsme_is_available ? "running" : "stopped"); + + /* Forget cached device state */ + device_state_changed("UNKNOWN"); + + /* Query current state on dsme startup */ + if( dsme_is_available ) { + device_state_query(); + } + } +} + +static DBusPendingCall *dsme_available_pc = 0; + +static void dsme_available_cb(const char *owner) +{ + dsme_available_changed(owner); + + dbus_pending_call_unref(dsme_available_pc), + dsme_available_pc = 0; +} + +static void dsme_available_cancel(void) +{ + if( dsme_available_pc ) + { + dbus_pending_call_cancel(dsme_available_pc); + dbus_pending_call_unref(dsme_available_pc), + dsme_available_pc = 0; + } +} + +static void dsme_available_query(void) +{ + dsme_available_cancel(); + + usb_moded_get_name_owner_async(DSME_DBUS_SERVICE, + dsme_available_cb, + &dsme_available_pc); +} + +static void name_owner_signal(DBusMessage *msg) +{ + DBusError err = DBUS_ERROR_INIT; + const char *name = 0; + const char *prev = 0; + const char *curr = 0; + + if( !dbus_message_get_args(msg, &err, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &prev, + DBUS_TYPE_STRING, &curr, + DBUS_TYPE_INVALID) ) + { + log_err("failed to parse signal: %s: %s", + err.name, err.message); + } + else if( !strcmp(name, DSME_DBUS_SERVICE) ) + { + dsme_available_changed(curr); + } + dbus_error_free(&err); +} + +/* ========================================================================= * + * dbus message filter + * ========================================================================= */ + +static DBusHandlerResult +dsme_dbus_filter_cb(DBusConnection *con, DBusMessage *msg, void *user_data) +{ + (void)con; + (void)user_data; + + if( dbus_message_is_signal(msg, + DSME_DBUS_SIGNAL_IFACE, + DSME_STATE_CHANGE_SIG) ) + { + device_state_signal(msg); + } + else if( dbus_message_is_signal(msg, + DBUS_INTERFACE_DBUS, + DBUS_NAME_OWNER_CHANGED_SIG) ) + { + name_owner_signal(msg); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +/* ========================================================================= * + * start/stop dsme tracking + * ========================================================================= */ + +gboolean +dsme_listener_start(void) +{ + gboolean ack = FALSE; + + /* Get connection ref */ + if( (dsme_con = usb_moded_dbus_get_connection()) == 0 ) + { + log_err("Could not connect to dbus for dsme\n"); + goto cleanup; + } + + /* Add filter callback */ + if( !dbus_connection_add_filter(dsme_con, + dsme_dbus_filter_cb , 0, 0) ) + { + log_err("adding system dbus filter for dsme failed"); + goto cleanup; + } + + /* Add match without blocking / error checking */ + dbus_bus_add_match(dsme_con, DSME_STATE_CHANGE_MATCH, 0); + dbus_bus_add_match(dsme_con, DSME_OWNER_CHANGE_MATCH, 0); + + /* Initiate async dsme name owner query */ + dsme_available_query(); + + ack = TRUE; + +cleanup: + + return ack; +} + +void +dsme_listener_stop(void) +{ + /* Cancel pending dbus queries */ + dsme_available_cancel(); + device_state_cancel(); + + if(dsme_con) + { + /* Remove filter callback */ + dbus_connection_remove_filter(dsme_con, + dsme_dbus_filter_cb, 0); + + if( dbus_connection_get_is_connected(dsme_con) ) { + /* Remove match without blocking / error checking */ + dbus_bus_remove_match(dsme_con, DSME_STATE_CHANGE_MATCH, 0); + dbus_bus_remove_match(dsme_con, DSME_OWNER_CHANGE_MATCH, 0); + } + + /* Let go of connection ref */ + dbus_connection_unref(dsme_con), + dsme_con = 0; + } +} diff --git a/src/usb_moded-dsme.h b/src/usb_moded-dsme.h index c64da19..33d87cf 100644 --- a/src/usb_moded-dsme.h +++ b/src/usb_moded-dsme.h @@ -1,9 +1,10 @@ /** @file usb_moded-dsme.h - Copyright (C) 2013 Jolla. All rights reserved. + Copyright (C) 2013-2016 Jolla. All rights reserved. @author: Philippe De Swert + @author: Simo Piiroinen This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License @@ -20,4 +21,6 @@ 02110-1301 USA */ -int is_in_user_state(void); +gboolean is_in_user_state(void); +gboolean dsme_listener_start(void); +void dsme_listener_stop(void); diff --git a/src/usb_moded-lock.h b/src/usb_moded-lock.h index bde9018..dd91fcd 100644 --- a/src/usb_moded-lock.h +++ b/src/usb_moded-lock.h @@ -1,7 +1,10 @@ /* Copyright (C) 2012 Nokia Corporation. All rights reserved. + Copyright (C) 2013-2016 Jolla Ltd. Author: Philippe De Swert + Author: Philippe De Swert + Author: Simo Piiroinen This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License @@ -23,6 +26,6 @@ */ /*============================================================================= */ -int usb_moded_get_export_permission(void); -int start_devicelock_listener(void); -int stop_devicelock_listener(void); +int usb_moded_get_export_permission(void); +gboolean start_devicelock_listener(void); +void stop_devicelock_listener(void); diff --git a/src/usb_moded-log.c b/src/usb_moded-log.c index d804f6b..da2c5cd 100644 --- a/src/usb_moded-log.c +++ b/src/usb_moded-log.c @@ -2,9 +2,12 @@ @file usb_moded-log.c Copyright (C) 2010 Nokia Corporation. All rights reserved. + Copyright (C) 2016 Jolla Ltd. @author: Philippe De Swert @author: Simo Piiroinen + @author: Simo Piiroinen + This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License @@ -53,6 +56,14 @@ static char *strip(char *str) return str; } +static struct timeval log_begtime = { 0, 0 }; + +static void log_gettime(struct timeval *tv) +{ + gettimeofday(tv, 0); + timersub(tv, &log_begtime, tv); +} + /** * Print the logged messages to the selected output * @@ -79,10 +90,10 @@ void log_emit_va(int lev, const char *fmt, va_list va) #if LOG_ENABLE_TIMESTAMPS { struct timeval tv; - gettimeofday(&tv, 0); - fprintf(stderr, "%ld.%06ld ", + log_gettime(&tv); + fprintf(stderr, "%3ld.%03ld ", (long)tv.tv_sec, - (long)tv.tv_usec); + (long)tv.tv_usec/1000); } #endif @@ -156,3 +167,10 @@ inline void log_set_level(int lev) log_level = lev; } +/** Initialize logging */ +void log_init(void) +{ + /* Get reference time used for verbose logging */ + if( !timerisset(&log_begtime) ) + gettimeofday(&log_begtime, 0); +} diff --git a/src/usb_moded-log.h b/src/usb_moded-log.h index eabb4d7..73d0fb5 100644 --- a/src/usb_moded-log.h +++ b/src/usb_moded-log.h @@ -1,7 +1,9 @@ /* Copyright (C) 2010 Nokia Corporation. All rights reserved. + Copyright (C) 2016 Jolla Ltd. Author: Philippe De Swert + Author: Simo Piiroinen This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License @@ -44,6 +46,7 @@ enum void log_set_level(int lev); int log_get_level(void); +void log_init(void); void log_emit_va(int lev, const char *fmt, va_list va); void log_emit(int lev, const char *fmt, ...) __attribute__((format(printf,2,3))); void log_debugf(const char *fmt, ...) __attribute__((format(printf,1,2))); diff --git a/src/usb_moded-modesetting.c b/src/usb_moded-modesetting.c index 6d01f26..1f2d7fc 100644 --- a/src/usb_moded-modesetting.c +++ b/src/usb_moded-modesetting.c @@ -2,8 +2,13 @@ @file usb_moded-modesetting.c Copyright (C) 2010 Nokia Corporation. All rights reserved. + Copyright (C) 2013-2016 Jolla Ltd. @author: Philippe De Swert + @author: Philippe De Swert + @author: Bernd Wachter + @author: Slava Monich + @author: Simo Piiroinen This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License @@ -567,7 +572,7 @@ int usb_moded_mode_cleanup(const char *module) #ifdef APP_SYNC /* Stop applications started due to entering this mode */ - appsync_stop(0); + appsync_stop(FALSE); #endif /* APP_SYNC */ if(!strcmp(module, MODULE_MASS_STORAGE)|| !strcmp(module, MODULE_FILE_STORAGE)) diff --git a/src/usb_moded-modules.c b/src/usb_moded-modules.c index cfb7784..c6afae2 100644 --- a/src/usb_moded-modules.c +++ b/src/usb_moded-modules.c @@ -2,10 +2,12 @@ @file usb_moded-modules.c Copyright (C) 2010 Nokia Corporation. All rights reserved. - Copyright (C) 2012-2015 Jolla. All rights reserved. + Copyright (C) 2012-2016 Jolla. All rights reserved. @author: Philippe De Swert @author: Philippe De Swert + @author: Slava Monich + @author: Simo Piiroinen This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License @@ -43,7 +45,7 @@ /* kmod context - initialized at start in usb_moded_init by ctx_init() and cleaned up by ctx_cleanup() functions */ -struct kmod_ctx *ctx; +static struct kmod_ctx *ctx = 0; /* kmod module init */ void usb_moded_module_ctx_init(void) @@ -55,7 +57,8 @@ void usb_moded_module_ctx_init(void) /* kmod module cleanup */ void usb_moded_module_ctx_cleanup(void) { - kmod_unref(ctx); + if( ctx ) + kmod_unref(ctx), ctx = 0; } /** load module diff --git a/src/usb_moded-ssu.c b/src/usb_moded-ssu.c index 52d40bc..ba9c206 100644 --- a/src/usb_moded-ssu.c +++ b/src/usb_moded-ssu.c @@ -26,6 +26,7 @@ #include "usb_moded-ssu.h" #include "usb_moded-log.h" +#include "usb_moded-dbus-private.h" /** SSU D-Bus service name */ #define SSU_DBUS_SERVICE "org.nemo.ssu" @@ -74,9 +75,8 @@ usb_moded_get_ssu_display_name(SsuDisplayType type_id) const char *val = 0; DBusError err = DBUS_ERROR_INIT; - if( !(con = dbus_bus_get(DBUS_BUS_SYSTEM, &err)) ) { - log_err("could not connect to system bus: %s: %s", - err.name, err.message); + if( !(con = usb_moded_dbus_get_connection()) ) { + log_err("not connected to system bus"); goto EXIT; } diff --git a/src/usb_moded-systemd.c b/src/usb_moded-systemd.c index 4a27cfb..5b62ea1 100644 --- a/src/usb_moded-systemd.c +++ b/src/usb_moded-systemd.c @@ -1,19 +1,20 @@ /** - @file usb_moded-systemd.c + @file usb_moded-systemd.c - Copyright (C) 2013 Jolla oy. All rights reserved. + Copyright (C) 2013-2016 Jolla oy. All rights reserved. @author: Philippe De Swert + @author: Simo Piiroinen This program is free software; you can redistribute it and/or - modify it under the terms of the Lesser GNU General Public License - version 2 as published by the Free Software Foundation. + modify it under the terms of the Lesser GNU General Public License + version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the Lesser GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA @@ -34,69 +35,129 @@ #include "usb_moded-log.h" #include "usb_moded-systemd.h" -static DBusConnection * get_systemd_dbus_connection(void) -{ - DBusError error; - DBusConnection *conn = 0; - - dbus_error_init(&error); - - conn = dbus_connection_open_private("unix:path=/run/systemd/private", &error); - if (!conn) - { - if (dbus_error_is_set(&error)) - log_err("Cannot connect to systemd: %s", error.message); - else - log_err("Cannot connect to systemd"); - dbus_error_free(&error); - return 0; - } - - return conn; -} +#define SYSTEMD_DBUS_SERVICE "org.freedesktop.systemd1" +#define SYSTEMD_DBUS_PATH "/org/freedesktop/systemd1" +#define SYSTEMD_DBUS_INTERFACE "org.freedesktop.systemd1.Manager" + + +/* SystemBus connection ref used for systemd control ipc */ +static DBusConnection *systemd_con = NULL; + +// QDBusObjectPath org.freedesktop.systemd1.Manager.StartUnit(QString name, QString mode) +// QDBusObjectPath org.freedesktop.systemd1.Manager.StopUnit(QString name, QString mode) + // mode = replace // method = StartUnit or StopUnit -int systemd_control_service(const char *name, const char *method) +gboolean systemd_control_service(const char *name, const char *method) { + DBusMessage *req = NULL; + DBusMessage *rsp = NULL; + DBusError err = DBUS_ERROR_INIT; + const char *arg = "replace"; + const char *res = 0; + + + log_debug("%s(%s) ...", method, name); + + if( !systemd_con ) { + log_err("not connected to system bus; skip systemd unit control"); + goto EXIT; + } + + req = dbus_message_new_method_call(SYSTEMD_DBUS_SERVICE, + SYSTEMD_DBUS_PATH, + SYSTEMD_DBUS_INTERFACE, + method); + if( !req ) { + log_err("failed to construct %s.%s request", + SYSTEMD_DBUS_INTERFACE, + method); + goto EXIT; + } + + if( !dbus_message_append_args(req, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &arg, + DBUS_TYPE_INVALID)) + { + log_debug("error appending arguments"); + goto EXIT; + } + + + rsp = dbus_connection_send_with_reply_and_block(systemd_con, req, -1, &err); + if( !rsp ) { + log_err("no reply to %s.%s request: %s: %s", + SYSTEMD_DBUS_INTERFACE, + method, + err.name, err.message); + goto EXIT; + } + + if( dbus_set_error_from_message(&err, rsp) ) { + log_err("got error reply to %s.%s request: %s: %s", + SYSTEMD_DBUS_INTERFACE, + method, + err.name, err.message); + goto EXIT; + } + + if( !dbus_message_get_args(rsp, &err, + DBUS_TYPE_OBJECT_PATH, &res, + DBUS_TYPE_INVALID) ) { + log_err("failed to parse reply to %s.%s request: %s: %s", + SYSTEMD_DBUS_INTERFACE, + method, + err.name, err.message); + goto EXIT; + } + +EXIT: + + dbus_error_free(&err); + + if( rsp ) dbus_message_unref(rsp); + if( req ) dbus_message_unref(req); + + log_debug("%s(%s) -> %s", method, name, res ?: "N/A"); + + return res != 0; +} + +/* ========================================================================= * + * start/stop systemd control availability + * ========================================================================= */ - DBusConnection *bus; - DBusError error; - DBusMessage *msg = NULL, *reply = NULL; - int ret = 1; - const char * replace = "replace"; - - dbus_error_init(&error); - - log_debug("Handling %s, with systemd, method %s\n", name, method); - - bus = get_systemd_dbus_connection(); - if(!bus) - return(ret); - - msg = dbus_message_new_method_call("org.freedesktop.systemd1", - "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", method); - if(msg) - { - if(!dbus_message_append_args (msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &replace, DBUS_TYPE_INVALID)) - { - log_debug("error appending arguments\n"); - dbus_message_unref(msg); - goto quit; - } - reply = dbus_connection_send_with_reply_and_block(bus, msg, -1, &error); - if(reply) - { - dbus_message_unref(reply); - ret = 0; - } - dbus_message_unref(msg); - } - -quit: - dbus_connection_close(bus); - dbus_connection_unref(bus); - dbus_error_free(&error); - - return(ret); +gboolean +systemd_control_start(void) +{ + gboolean ack = FALSE; + + log_debug("starting systemd control"); + + /* Get connection ref */ + if( (systemd_con = usb_moded_dbus_get_connection()) == 0 ) + { + log_err("Could not connect to dbus for systemd control\n"); + goto cleanup; + } + ack = TRUE; + +cleanup: + + return ack; +} + +void +systemd_control_stop(void) +{ + log_debug("stopping systemd control"); + + if(systemd_con) + { + /* Let go of connection ref */ + dbus_connection_unref(systemd_con), + systemd_con = 0; + } } diff --git a/src/usb_moded-systemd.h b/src/usb_moded-systemd.h index 495d3bd..05f9452 100644 --- a/src/usb_moded-systemd.h +++ b/src/usb_moded-systemd.h @@ -1,8 +1,9 @@ /* - Copyright (C) 2013 Jolla Oy. All rights reserved. + Copyright (C) 2013-2016 Jolla Oy. All rights reserved. author: Philippe De Swert + author: Simo Piiroinen This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License @@ -22,4 +23,6 @@ #define SYSTEMD_STOP "StopUnit" #define SYSTEMD_START "StartUnit" -int systemd_control_service(const char *name, const char *method); +gboolean systemd_control_service(const char *name, const char *method); +gboolean systemd_control_start(void); +void systemd_control_stop(void); diff --git a/src/usb_moded.c b/src/usb_moded.c index 7188fe0..9962920 100644 --- a/src/usb_moded.c +++ b/src/usb_moded.c @@ -2,10 +2,11 @@ @file usb_moded.c Copyright (C) 2010 Nokia Corporation. All rights reserved. - Copyright (C) 2012 Jolla. All rights reserved. + Copyright (C) 2012-2016 Jolla. All rights reserved. @author: Philippe De Swert @author: Philippe De Swert + @author: Simo Piiroinen This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License @@ -37,6 +38,7 @@ #include "usb_moded.h" #include "usb_moded-modes.h" +#include "usb_moded-dsme.h" #include "usb_moded-dbus.h" #include "usb_moded-dbus-private.h" #include "usb_moded-hw-ab.h" @@ -52,13 +54,16 @@ #include "usb_moded-network.h" #include "usb_moded-mac.h" #include "usb_moded-android.h" +#include "usb_moded-systemd.h" #ifdef MEEGOLOCK #include "usb_moded-dsme.h" #endif /* global definitions */ -GMainLoop *mainloop = NULL; +static int usb_moded_exitcode = EXIT_FAILURE; +static GMainLoop *usb_moded_mainloop = NULL; + extern const char *log_name; extern int log_level; extern int log_type; @@ -123,6 +128,7 @@ static gboolean set_disconnected_silent(gpointer data); static void usb_moded_init(void); static gboolean charging_fallback(gpointer data); static void usage(void); +static bool init_done_p(void); /* ============= Implementation starts here =========================================== */ /** set the usb connection status @@ -622,9 +628,7 @@ static void usb_moded_init(void) #ifdef APP_SYNC readlist(diag_mode); - /* make sure all services are down when starting */ - appsync_stop(1); -#endif /* APP_SYNC */ +#endif /* always read dyn modes even if appsync is not used */ modelist = read_mode_list(diag_mode); @@ -647,6 +651,32 @@ static void usb_moded_init(void) /* TODO: add more start-up clean-up and init here if needed */ } +/** Release resources allocated by usb_moded_init() + */ +static void usb_moded_cleanup(void) +{ + /* Undo usb_moded_module_ctx_init() */ + usb_moded_module_ctx_cleanup(); + + /* Undo trigger_init() */ + trigger_stop(); + + /* Undo read_mode_list() */ + free_mode_list(modelist); + +#ifdef APP_SYNC + /* Undo readlist() */ + free_appsync_list(); +#endif + + /* Release dynamic memory */ + free(current_mode.module), + current_mode.module = 0; + + free(current_mode.mode), + current_mode.mode = 0; +} + /* charging fallback handler */ static gboolean charging_fallback(gpointer data) { @@ -669,62 +699,35 @@ static gboolean charging_fallback(gpointer data) return(FALSE); } -static void handle_exit(void) -{ - /* exiting and clean-up when mainloop ended */ - appsync_stop(1); - hwal_cleanup(); - usb_moded_dbus_cleanup(); -#ifdef MEEGOLOCK - stop_devicelock_listener(); -#endif /* MEEGOLOCK */ - - free_mode_list(modelist); - usb_moded_module_ctx_cleanup(); - -#ifdef APP_SYNC - free_appsync_list(); -#ifdef APP_SYNC_DBUS - usb_moded_appsync_cleanup(); -#endif /* APP_SYNC_DBUS */ -#endif /* APP_SYNC */ - /* dbus_shutdown(); This causes exit(1) and don't seem - to behave as documented */ - - /* If the mainloop is initialised, unreference it */ - if (mainloop != NULL) - { - g_main_loop_quit(mainloop); - g_main_loop_unref(mainloop); - } - free(current_mode.mode); - free(current_mode.module); - - log_debug("All resources freed. Exiting!\n"); - - exit(0); -} - static void sigint_handler(int signum) { - struct mode_list_elem *data; + log_debug("handle signal: %s\n", strsignal(signum)); - if(signum == SIGINT || signum == SIGTERM) - handle_exit(); - if(signum == SIGHUP) - { - /* clean up current mode */ - data = get_usb_mode_data(); - set_disconnected_silent(data); - /* clear existing data to be sure */ - set_usb_mode_data(NULL); - /* free and read in modelist again */ - free_mode_list(modelist); + if( signum == SIGTERM ) + { + /* Assume: Stopped by init process */ + usb_moded_stop(EXIT_SUCCESS); + } + else if( signum == SIGHUP ) + { + struct mode_list_elem *data; + + /* clean up current mode */ + data = get_usb_mode_data(); + set_disconnected_silent(data); + /* clear existing data to be sure */ + set_usb_mode_data(NULL); + /* free and read in modelist again */ + free_mode_list(modelist); - modelist = read_mode_list(diag_mode); + modelist = read_mode_list(diag_mode); send_supported_modes_signal(); - } + } + else + { + usb_moded_stop(EXIT_FAILURE); + } } /* Display usage information */ @@ -804,7 +807,6 @@ static gboolean sigpipe_read_signal_cb(GIOChannel *channel, abort(); /* handle the signal */ - log_warning("handle signal: %s\n", strsignal(sig)); sigint_handler(sig); keep_watch = TRUE; @@ -1063,10 +1065,29 @@ static bool init_done_p(void) return access("/run/systemd/boot-status/init-done", F_OK) == 0; } +/** Request orderly exit from mainloop + */ +void usb_moded_stop(int exitcode) +{ + /* In case multiple exit request get done, retain the + * highest exit code used. */ + if( usb_moded_exitcode < exitcode ) + usb_moded_exitcode = exitcode; + + /* If there is no mainloop to exit, terminate immediately */ + if( !usb_moded_mainloop ) + { + log_warning("exit requested outside mainloop; exit(%d) now", + usb_moded_exitcode); + exit(usb_moded_exitcode); + } + + log_debug("stopping usb-moded mainloop"); + g_main_loop_quit(usb_moded_mainloop); +} int main(int argc, char* argv[]) { - int result = EXIT_FAILURE; int opt = 0, opt_idx = 0; struct option const options[] = { @@ -1085,8 +1106,13 @@ int main(int argc, char* argv[]) { 0, 0, 0, 0 } }; + log_init(); log_name = basename(*argv); + /* - - - - - - - - - - - - - - - - - - - * + * OPTIONS + * - - - - - - - - - - - - - - - - - - - */ + /* Parse the command-line options */ while ((opt = getopt_long(argc, argv, "aifsTDdhrnvm:", options, &opt_idx)) != -1) { @@ -1143,20 +1169,36 @@ int main(int argc, char* argv[]) } } - printf("usb_moded %s starting\n", VERSION); + fprintf(stderr, "usb_moded %s starting\n", VERSION); + fflush(stderr); + + /* - - - - - - - - - - - - - - - - - - - * + * INITIALIZE + * - - - - - - - - - - - - - - - - - - - */ + /* silence system() calls */ if(log_type != LOG_TO_STDERR || log_level != LOG_DEBUG ) { freopen("/dev/null", "a", stdout); freopen("/dev/null", "a", stderr); } + #if !GLIB_CHECK_VERSION(2, 36, 0) g_type_init(); #endif #if !GLIB_CHECK_VERSION(2, 31, 0) g_thread_init(NULL); #endif - mainloop = g_main_loop_new(NULL, FALSE); + + /* Must be the 1st libdbus call that is made */ + dbus_threads_init_default(); + + /* signal handling */ + if( !sigpipe_init() ) + { + log_crit("signal handler init failed\n"); + goto EXIT; + } if (rescue_mode && init_done_p()) { @@ -1164,45 +1206,84 @@ int main(int argc, char* argv[]) log_warning("init done passed; rescue mode ignored"); } - /* init daemon into a clean state first, then dbus and hw_abstraction last */ - usb_moded_init(); - if( !usb_moded_dbus_init() ) + /* Connect to SystemBus */ + if( !usb_moded_dbus_init_connection() ) { - log_crit("dbus ipc init failed\n"); + log_crit("dbus systembus connection failed\n"); goto EXIT; } - if( !hwal_init() ) - { - /* if hw_fallback is active we can live with a failed hwal_init */ - if(!hw_fallback) - { - log_crit("hwal init failed\n"); - goto EXIT; - } + + /* Start DBus trackers that do async initialization + * so that initial method calls are on the way while + * we do initialization actions that might block. */ + + /* DSME listener maintains in-user-mode state and is relevant + * only when MEEGOLOCK configure option has been chosen. */ +#ifdef MEEGOLOCK + if( !dsme_listener_start() ) { + log_crit("dsme tracking could not be started"); + goto EXIT; } +#endif + /* Devicelock listener maintains devicelock state and is relevant + * only when MEEGOLOCK configure option has been chosen. */ #ifdef MEEGOLOCK - start_devicelock_listener(); -#endif /* MEEGOLOCK */ + if( !start_devicelock_listener() ) { + log_crit("devicelock tracking could not be started"); + goto EXIT; + } +#endif - /* signal handling */ - if( !sigpipe_init() ) + /* Set daemon config/state data to sane state */ + usb_moded_init(); + + /* Allos making systemd control ipc */ + if( !systemd_control_start() ) { + log_crit("systemd control could not be started"); + goto EXIT; + } + + /* If usb-moded happens to crash, it could leave appsync processes + * running. To make sure things are in the order expected by usb-moded + * force stopping of appsync processes during usb-moded startup. + * + * The exception is: When usb-moded starts as a part of bootup. Then + * we can be relatively sure that usb-moded has not been running yet + * and therefore no appsync processes have been started and we can + * skip the blocking ipc required to stop the appsync systemd units. */ +#ifdef APP_SYNC + if( init_done_p() ) { - log_crit("signal handler init failed\n"); + log_warning("usb-moded started after init-done; " + "forcing appsync stop"); + appsync_stop(TRUE); + } +#endif + + /* Claim D-Bus service name before proceeding with things that + * could result in dbus signals from usb-moded interfaces to + * be broadcast */ + if( !usb_moded_dbus_init_service() ) + { + log_crit("usb-moded dbus service init failed\n"); goto EXIT; } -#ifdef SYSTEMD - /* Tell systemd that we have started up */ - if( systemd_notify ) + /* Initialize udev listener. Can cause mode changes. + * + * Failing here is allowed if '--fallback' commandline option is used. */ + if( !hwal_init() && !hw_fallback ) { - log_debug("notifying systemd\n"); - sd_notify(0, "READY=1"); + log_crit("hwal init failed\n"); + goto EXIT; } -#endif /* SYSTEMD */ + /* Broadcast supported / hidden modes */ + // TODO: should this happen before hwal_init()? send_supported_modes_signal(); send_hidden_modes_signal(); + /* Act on '--fallback' commandline option */ if(hw_fallback) { log_warning("Forcing USB state to connected always. ASK mode non functional!\n"); @@ -1210,12 +1291,77 @@ int main(int argc, char* argv[]) set_usb_connected(TRUE); } + /* - - - - - - - - - - - - - - - - - - - * + * EXECUTE + * - - - - - - - - - - - - - - - - - - - */ + + /* Tell systemd that we have started up */ +#ifdef SYSTEMD + if( systemd_notify ) + { + log_debug("notifying systemd\n"); + sd_notify(0, "READY=1"); + } +#endif + /* init succesful, run main loop */ - result = EXIT_SUCCESS; - g_main_loop_run(mainloop); + usb_moded_exitcode = EXIT_SUCCESS; + usb_moded_mainloop = g_main_loop_new(NULL, FALSE); + + log_debug("enter usb-moded mainloop"); + g_main_loop_run(usb_moded_mainloop); + log_debug("leave usb-moded mainloop"); + + g_main_loop_unref(usb_moded_mainloop), + usb_moded_mainloop = 0; + + /* - - - - - - - - - - - - - - - - - - - * + * CLEANUP + * - - - - - - - - - - - - - - - - - - - */ EXIT: - handle_exit(); + /* Detach from SystemBus. Components that hold reference to the + * shared bus connection can still perform cleanup tasks, but new + * references can't be obtained anymore and usb-moded method call + * processing no longer occurs. */ + usb_moded_dbus_cleanup(); + + /* Stop appsync processes that have been started by usb-moded */ +#ifdef APP_SYNC + appsync_stop(FALSE); +#endif + + /* Deny making systemd control ipc */ + systemd_control_stop(); + + /* Stop tracking devicelock status */ +#ifdef MEEGOLOCK + stop_devicelock_listener(); +#endif + /* Stop tracking device state */ +#ifdef MEEGOLOCK + dsme_listener_stop(); +#endif + + /* Stop udev listener */ + hwal_cleanup(); + /* Release dynamically allocated config/state data */ + usb_moded_cleanup(); + + /* Detach from SessionBus connection used for APP_SYNC_DBUS. + * + * Can be handled separately from SystemBus side wind down. */ +#ifdef APP_SYNC +# ifdef APP_SYNC_DBUS + usb_moded_appsync_cleanup(); +# endif +#endif + + /* Must be done just before exit to make sure no more wakelocks + * are taken and left behind on exit path */ allow_suspend(); - return result; + + log_debug("usb-moded return from main, with exit code %d", + usb_moded_exitcode); + return usb_moded_exitcode; } diff --git a/src/usb_moded.h b/src/usb_moded.h index f965993..ed7be40 100644 --- a/src/usb_moded.h +++ b/src/usb_moded.h @@ -1,9 +1,10 @@ /* Copyright (C) 2010 Nokia Corporation. All rights reserved. - Copyright (C) 2012 Jolla. All rights reserved. + Copyright (C) 2012-2016 Jolla. All rights reserved. @author: Philippe De Swert @author: Philippe De Swert + @author: Simo Piiroinen This program is free software; you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License @@ -92,4 +93,6 @@ void delay_suspend(void); extern int cable_connection_delay; +void usb_moded_stop(int exitcode); + #endif /* USB_MODED_H */