usb_moded-appsync-dbus.c 10.7 KB
Newer Older
1
/**
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 * @file        usb_moded-dbus.c
 *
 * Copyright (C) 2010 Nokia Corporation. All rights reserved.
 * Copyright (C) 2013-2018 Jolla Ltd.
 *
 * @author: Philippe De Swert <philippe.de-swert@nokia.com>
 * @author: Philippe De Swert <phdeswer@lumi.maa>
 * @author: Philippe De Swert <philippedeswert@gmail.com>
 * @author: Philippe De Swert <philippe.deswert@jollamobile.com>
 * @author: Simo Piiroinen <simo.piiroinen@jollamobile.com>
 *
 * 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.
 *
 * 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
 */
27

28 29 30 31 32
#include "usb_moded-appsync-dbus-private.h"

#include "usb_moded-appsync.h"
#include "usb_moded-log.h"

33 34 35 36
#include <string.h>

#include <dbus/dbus.h>

37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
/* ========================================================================= *
 * Prototypes
 * ========================================================================= */

/* -- dbusappsync -- */

static void              dbusappsync_release_name      (void);
static gboolean          dbusappsync_obtain_name       (void);
static DBusHandlerResult dbusappsync_msg_handler       (DBusConnection *const connection, DBusMessage *const msg, gpointer const user_data);
static DBusHandlerResult dbusappsync_handle_disconnect (DBusConnection *conn, DBusMessage *msg, void *user_data);
static void              dbusappsync_cleanup_connection(void);
gboolean                 dbusappsync_init_connection   (void);
gboolean                 dbusappsync_init              (void);
void                     dbusappsync_cleanup           (void);
int                      dbusappsync_launch_app        (char *launch);

/* ========================================================================= *
 * Data
 * ========================================================================= */

57 58 59 60
static DBusConnection *dbus_connection_ses  = NULL;  // connection
static gboolean        dbus_connection_name = FALSE; // have name
static gboolean        dbus_connection_disc = FALSE; // got disconnected

61 62 63
/* ========================================================================= *
 * Functions
 * ========================================================================= */
64

65
static void dbusappsync_release_name(void)
phdeswer's avatar
phdeswer committed
66
{
67 68
    /* Drop the service name - if we have it */
    if( dbus_connection_ses && dbus_connection_name )
phdeswer's avatar
phdeswer committed
69
    {
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
        DBusError error = DBUS_ERROR_INIT;
        int ret = dbus_bus_release_name(dbus_connection_ses, USB_MODE_SERVICE, &error);

        switch( ret )
        {
        case DBUS_RELEASE_NAME_REPLY_RELEASED:
            // as expected
            log_debug("released name: %s", USB_MODE_SERVICE);
            break;
        case DBUS_RELEASE_NAME_REPLY_NON_EXISTENT:
            // weird, but since nobody owns the name ...
            log_debug("nonexisting name: %s", USB_MODE_SERVICE);
            break;
        case DBUS_RELEASE_NAME_REPLY_NOT_OWNER:
            log_warning("somebody else owns: %s", USB_MODE_SERVICE);
        }

        if( dbus_error_is_set(&error) )
        {
            log_debug("DBUS ERROR: %s, %s \n", error.name, error.message);
            dbus_error_free(&error);
        }
phdeswer's avatar
phdeswer committed
92 93
    }

94
    dbus_connection_name = FALSE;
phdeswer's avatar
phdeswer committed
95 96
}

97
static gboolean dbusappsync_obtain_name(void)
phdeswer's avatar
phdeswer committed
98
{
99
    DBusError error = DBUS_ERROR_INIT;
phdeswer's avatar
phdeswer committed
100

101
    int ret;
phdeswer's avatar
phdeswer committed
102

103 104 105 106
    if( dbus_connection_name )
    {
        goto EXIT;
    }
phdeswer's avatar
phdeswer committed
107

108 109 110 111
    if( dbus_connection_ses == 0 )
    {
        goto EXIT;
    }
phdeswer's avatar
phdeswer committed
112

113 114
    /* Acquire D-Bus service name */
    ret = dbus_bus_request_name(dbus_connection_ses, USB_MODE_SERVICE, DBUS_NAME_FLAG_DO_NOT_QUEUE , &error);
phdeswer's avatar
phdeswer committed
115

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
    switch( ret )
    {
    case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
        // expected result
        log_debug("primary owner of: %s", USB_MODE_SERVICE);
        break;

    case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
        // functionally ok, but we do have a logic error somewhere
        log_warning("already owner of: %s", USB_MODE_SERVICE);
        break;

    default:
        // something odd
        log_err("failed to claim: %s", USB_MODE_SERVICE);
        goto EXIT;
    }
phdeswer's avatar
phdeswer committed
133

134
    dbus_connection_name = TRUE;
phdeswer's avatar
phdeswer committed
135 136 137

EXIT:

138 139 140 141 142
    if( dbus_error_is_set(&error) )
    {
        log_debug("DBUS ERROR: %s, %s \n", error.name, error.message);
        dbus_error_free(&error);
    }
phdeswer's avatar
phdeswer committed
143

144
    return dbus_connection_name;
phdeswer's avatar
phdeswer committed
145 146
}

147 148 149
/**
 * Handle USB_MODE_INTERFACE method calls
 */
150

151
static DBusHandlerResult dbusappsync_msg_handler(DBusConnection *const connection, DBusMessage *const msg, gpointer const user_data)
152
{
153 154 155 156 157
    DBusHandlerResult   status    = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    int                 type      = dbus_message_get_type(msg);
    const char         *interface = dbus_message_get_interface(msg);
    const char         *member    = dbus_message_get_member(msg);
    const char         *object    = dbus_message_get_path(msg);
158

159
    if(!interface || !member || !object) goto IGNORE;
160

161 162 163
    if( type == DBUS_MESSAGE_TYPE_METHOD_CALL &&
        !strcmp(interface, USB_MODE_INTERFACE) &&
        !strcmp(object, USB_MODE_OBJECT) )
164 165

    {
166 167 168 169 170 171 172 173 174 175 176 177 178 179
        DBusMessage *reply = 0;

        status = DBUS_HANDLER_RESULT_HANDLED;

        if(!strcmp(member, USB_MODE_APP_STATE))
        {
            char      *use = 0;
            DBusError  err = DBUS_ERROR_INIT;

            if(!dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &use, DBUS_TYPE_INVALID))
            {
                // could not parse method call args
                reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, member);
            }
180
            else if( appsync_mark_active(use, 1) < 0 )
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
            {
                // name could not be marked active
                reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, member);
            }
            else if((reply = dbus_message_new_method_return(msg)))
            {
                // generate normal reply
                dbus_message_append_args (reply, DBUS_TYPE_STRING, &use, DBUS_TYPE_INVALID);
            }
            dbus_error_free(&err);
        }
        else
        {
            /*unknown methods are handled here */
            reply = dbus_message_new_error(msg, DBUS_ERROR_UNKNOWN_METHOD, member);
        }

        if( !dbus_message_get_no_reply(msg) )
        {
            if( !reply )
            {
                // we failed to generate reply above -> generate one
                reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, member);
            }
            if( !reply || !dbus_connection_send(connection, reply, 0) )
            {
                log_debug("Failed sending reply. Out Of Memory!\n");
            }
        }

        if( reply ) dbus_message_unref(reply);
212 213 214 215
    }

IGNORE:

216
    return status;
217 218 219 220 221
}

/**
 * Handle disconnect signals
 */
222
static DBusHandlerResult dbusappsync_handle_disconnect(DBusConnection *conn, DBusMessage *msg, void *user_data)
223
{
224 225 226 227 228 229 230
    if( dbus_message_is_signal(msg, DBUS_INTERFACE_LOCAL, "Disconnected") )
    {
        log_warning("disconnected from session bus - expecting restart/stop soon\n");
        dbus_connection_disc = TRUE;
        dbusappsync_cleanup_connection();
    }
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
231
}
232

233 234 235
/**
 * Detach from session bus
 */
236
static void dbusappsync_cleanup_connection(void)
237
{
238
    if( dbus_connection_ses != 0 )
239
    {
240 241 242 243 244 245 246 247 248 249 250 251 252
        /* Remove message filters */
        dbus_connection_remove_filter(dbus_connection_ses, dbusappsync_msg_handler, 0);
        dbus_connection_remove_filter(dbus_connection_ses, dbusappsync_handle_disconnect, 0);

        /* Release name, but only if we can still talk to dbus daemon */
        if( !dbus_connection_disc )
        {
            dbusappsync_release_name();
        }

        dbus_connection_unref(dbus_connection_ses);
        dbus_connection_ses = NULL;
        //dbus_connection_disc = FALSE;
253
    }
254
    log_debug("succesfully cleaned up appsync dbus\n");
255
}
256

257 258 259
/**
 * Attach to session bus
 */
260
gboolean dbusappsync_init_connection(void)
261
{
262 263
    gboolean  result = FALSE;
    DBusError error  = DBUS_ERROR_INIT;
264

265 266 267 268 269
    if( dbus_connection_ses != 0 )
    {
        result = TRUE;
        goto EXIT;
    }
270

271 272 273 274 275
    if( dbus_connection_disc )
    {
        // we've already observed death of session
        goto EXIT;
    }
276

277 278 279 280 281 282
    /* Connect to session bus */
    if ((dbus_connection_ses = dbus_bus_get(DBUS_BUS_SESSION, &error)) == NULL)
    {
        log_err("Failed to open connection to session message bus; %s\n",  error.message);
        goto EXIT;
    }
283

284 285
    /* Add disconnect handler */
    dbus_connection_add_filter(dbus_connection_ses, dbusappsync_handle_disconnect, 0, 0);
286

287 288
    /* Add method call handler */
    dbus_connection_add_filter(dbus_connection_ses, dbusappsync_msg_handler, 0, 0);
289

290 291
    /* Make sure we do not get forced to exit if dbus session dies or stops */
    dbus_connection_set_exit_on_disconnect(dbus_connection_ses, FALSE);
292

293
    /* Connect D-Bus to the mainloop (Seems it is only needed once and is done at the main
294 295
     * D-Bus init
     * dbus_connection_setup_with_g_main(dbus_connection_ses, NULL);
296
     */
297

298 299 300 301 302
    /* Request service name */
    if( !dbusappsync_obtain_name() )
    {
        goto EXIT;
    }
phdeswer's avatar
phdeswer committed
303

304 305
    /* everything went fine */
    result = TRUE;
306 307

EXIT:
308 309
    dbus_error_free(&error);
    return result;
310 311 312 313 314 315 316
}

/**
 * Init dbus for usb_moded application synchronisation
 *
 * @return TRUE when everything went ok
 */
317
gboolean dbusappsync_init(void)
318
{
319
    gboolean status = FALSE;
320

321 322 323 324
    if( !dbusappsync_init_connection() )
    {
        goto EXIT;
    }
325

326 327
    /* everything went fine */
    status = TRUE;
328

329
EXIT:
330
    return status;
331 332 333
}

/**
334
 * Clean up the dbus connections for the application
335 336
 * synchronisation after sync is done
 */
337
void dbusappsync_cleanup(void)
338
{
339 340
    dbusappsync_cleanup_connection();
    // NOP
phdeswer's avatar
phdeswer committed
341 342
}

343 344 345
/**
 * Launch applications over dbus that need to be synchronized
 */
346
int dbusappsync_launch_app(char *launch)
347
{
348 349 350
    int ret = -1; // assume failure

    if( dbus_connection_ses == 0 )
351
    {
352
        log_err("could not start '%s': no session bus connection", launch);
353 354 355
    }
    else
    {
356 357 358 359 360 361 362 363 364 365
        DBusError error = DBUS_ERROR_INIT;
        if( !dbus_bus_start_service_by_name(dbus_connection_ses, launch, 0, NULL, &error) )
        {
            log_err("could not start '%s': %s: %s", launch, error.name, error.message);
            dbus_error_free(&error);
        }
        else
        {
            ret = 0; // success
        }
366
    }
367
    return ret;
368
}