usb_moded-appsync.c 14.5 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 27
 * @file usb_moded-appsync.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: Thomas Perl <m@thp.io>
 * @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
 */
28

29
#include "usb_moded-appsync.h"
30

31
#include "usb_moded-log.h"
32
#include "usb_moded-systemd.h"
33

34 35 36 37 38
#include <sys/time.h>

#include <stdlib.h>
#include <string.h>

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

/* -- appsync -- */

static void              appsync_free_elem                 (struct list_elem *elem);
static void              appsync_free_elem_cb              (gpointer elem, gpointer user_data);
void                     appsync_free_appsync_list         (void);
static gint              appsync_list_sort_func            (gconstpointer a, gconstpointer b);
void                     appsync_read_list                 (int diag);
static struct list_elem *appsync_read_file                 (const gchar *filename, int diag);
int                      appsync_activate_sync             (const char *mode);
int                      appsync_activate_sync_post        (const char *mode);
int                      appsync_mark_active               (const gchar *name, int post);
static gboolean          appsync_enumerate_usb_cb          (gpointer data);
static void              appsync_start_enumerate_usb_timer (void);
static void              appsync_cancel_enumerate_usb_timer(void);
static void              appsync_enumerate_usb             (void);
58
void                     appsync_stop_apps                 (int post);
59 60 61 62 63
int                      appsync_stop                      (gboolean force);

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

65
static GList *appsync_sync_list = NULL;
66

67 68
static guint appsync_enumerate_usb_id = 0;
static struct timeval appsync_sync_tv = {0, 0};
69
#ifdef APP_SYNC_DBUS
70
static  int appsync_no_dbus = 0;
71
#else
72
static  int appsync_no_dbus = 0;
73 74
#endif /* APP_SYNC_DBUS */

75 76 77
/* ========================================================================= *
 * Functions
 * ========================================================================= */
phdeswer's avatar
phdeswer committed
78

79
static void appsync_free_elem(struct list_elem *elem)
80
{
81 82 83 84
    g_free(elem->name);
    g_free(elem->launch);
    g_free(elem->mode);
    free(elem);
85
}
86

87
static void appsync_free_elem_cb(gpointer elem, gpointer user_data)
88
{
89 90
    (void)user_data;
    appsync_free_elem(elem);
91 92
}

93
void appsync_free_appsync_list(void)
94
{
95
    if( appsync_sync_list != 0 )
96
    {
97 98 99 100
        /*g_list_free_full(appsync_sync_list, appsync_free_elem); */
        g_list_foreach (appsync_sync_list, appsync_free_elem_cb, NULL);
        g_list_free (appsync_sync_list);
        appsync_sync_list = 0;
101 102
        log_debug("Appsync list freed\n");
    }
103 104
}

105 106 107
static gint appsync_list_sort_func(gconstpointer a, gconstpointer b)
{
    return strcasecmp( (char*)a, (char*)b );
108 109
}

110
void appsync_read_list(int diag)
111
{
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
    GDir *confdir = 0;

    const gchar *dirname;
    struct list_elem *list_item;

    appsync_free_appsync_list();

    if(diag)
    {
        if( !(confdir = g_dir_open(CONF_DIR_DIAG_PATH, 0, NULL)) )
            goto cleanup;
    }
    else
    {
        if( !(confdir = g_dir_open(CONF_DIR_PATH, 0, NULL)) )
            goto cleanup;
    }

    while( (dirname = g_dir_read_name(confdir)) )
    {
        log_debug("Read file %s\n", dirname);
        if( (list_item = appsync_read_file(dirname, diag)) )
134
            appsync_sync_list = g_list_append(appsync_sync_list, list_item);
135
    }
136 137

cleanup:
138
    if( confdir ) g_dir_close(confdir);
139

140
    /* sort list alphabetically so services for a mode
141
     * can be run in a certain order */
142
    appsync_sync_list=g_list_sort(appsync_sync_list, appsync_list_sort_func);
143

144 145 146
    /* set up session bus connection if app sync in use
     * so we do not need to make the time consuming connect
     * operation at enumeration time ... */
147

148
    if( appsync_sync_list )
149 150
    {
        log_debug("Sync list valid\n");
151
#ifdef APP_SYN_DBUS
152
        dbusappsync_init_connection();
153
#endif
154
    }
155 156
}

157
static struct list_elem *appsync_read_file(const gchar *filename, int diag)
158
{
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
    gchar *full_filename = NULL;
    GKeyFile *settingsfile = NULL;
    struct list_elem *list_item = NULL;

    if(diag)
    {
        if( !(full_filename = g_strconcat(CONF_DIR_DIAG_PATH, "/", filename, NULL)) )
            goto cleanup;
    }
    else
    {
        if( !(full_filename = g_strconcat(CONF_DIR_PATH, "/", filename, NULL)) )
            goto cleanup;
    }

    if( !(settingsfile = g_key_file_new()) )
        goto cleanup;

    if( !g_key_file_load_from_file(settingsfile, full_filename, G_KEY_FILE_NONE, NULL) )
        goto cleanup;

    if( !(list_item = calloc(1, sizeof *list_item)) )
        goto cleanup;

    list_item->name = g_key_file_get_string(settingsfile, APP_INFO_ENTRY, APP_INFO_NAME_KEY, NULL);
    log_debug("Appname = %s\n", list_item->name);
    list_item->launch = g_key_file_get_string(settingsfile, APP_INFO_ENTRY, APP_INFO_LAUNCH_KEY, NULL);
    log_debug("Launch = %s\n", list_item->launch);
    list_item->mode = g_key_file_get_string(settingsfile, APP_INFO_ENTRY, APP_INFO_MODE_KEY, NULL);
    log_debug("Launch mode = %s\n", list_item->mode);
    list_item->systemd = g_key_file_get_integer(settingsfile, APP_INFO_ENTRY, APP_INFO_SYSTEMD_KEY, NULL);
    log_debug("Systemd control = %d\n", list_item->systemd);
    list_item->post = g_key_file_get_integer(settingsfile, APP_INFO_ENTRY, APP_INFO_POST, NULL);
    list_item->state = APP_STATE_DONTCARE;
193 194 195

cleanup:

196 197 198
    if(settingsfile)
        g_key_file_free(settingsfile);
    g_free(full_filename);
199

200 201 202 203 204 205 206
    /* if a minimum set of required elements is not filled in we discard the list_item */
    if( list_item && !(list_item->name && list_item->mode) )
    {
        log_debug("Element invalid, discarding\n");
        appsync_free_elem(list_item);
        list_item = 0;
    }
207

208
    return list_item;
209
}
210

211
/* @return 0 on succes, 1 if there is a failure */
212
int appsync_activate_sync(const char *mode)
213
{
214 215
    GList *iter;
    int count = 0;
phdeswer's avatar
phdeswer committed
216

217
    log_debug("activate sync");
218

219
    /* Get start of activation timestamp */
220
    gettimeofday(&appsync_sync_tv, 0);
221

222
    if( appsync_sync_list == 0 )
223
    {
224 225 226
        log_debug("No sync list! Enumerating\n");
        appsync_enumerate_usb();
        return 0;
227
    }
228 229 230

    /* Count apps that need to be activated for this mode and
     * mark them as currently inactive */
231
    for( iter = appsync_sync_list; iter; iter = g_list_next(iter) )
232
    {
233 234 235 236 237 238 239 240 241 242 243
        struct list_elem *data = iter->data;

        if(!strcmp(data->mode, mode))
        {
            ++count;
            data->state = APP_STATE_INACTIVE;
        }
        else
        {
            data->state = APP_STATE_DONTCARE;
        }
244
    }
245

246 247 248 249 250 251 252
    /* If there is nothing to activate, enumerate immediately */
    if(count <= 0)
    {
        log_debug("Nothing to launch.\n");
        appsync_enumerate_usb();
        return(0);
    }
253

254
#ifdef APP_SYNC_DBUS
255 256 257 258
    /* check dbus initialisation, skip dbus activated services if this fails */
    if(!dbusappsync_init())
    {
        log_debug("dbus setup failed => skipping dbus launched apps \n");
259
        appsync_no_dbus = 1;
260
    }
261
#endif /* APP_SYNC_DBUS */
262

263 264
    /* start timer */
    appsync_start_enumerate_usb_timer();
phdeswer's avatar
phdeswer committed
265

266
    /* go through list and launch apps */
267
    for( iter = appsync_sync_list; iter; iter = g_list_next(iter) )
phdeswer's avatar
phdeswer committed
268
    {
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
        struct list_elem *data = iter->data;
        if(!strcmp(mode, data->mode))
        {
            /* do not launch items marked as post, will be launched after usb is up */
            if(data->post)
            {
                continue;
            }
            log_debug("launching pre-enum-app %s\n", data->name);
            if(data->systemd)
            {
                if(!systemd_control_service(data->name, SYSTEMD_START))
                    goto error;
                appsync_mark_active(data->name, 0);
            }
            else if(data->launch)
            {
                /* skipping if dbus session bus is not available,
287
                 * or not compiled in */
288
                if(appsync_no_dbus)
289
                    appsync_mark_active(data->name, 0);
290
#ifdef APP_SYNC_DBUS
291 292 293 294 295
                else
                    if(!dbusappsync_launch_app(data->launch))
                        appsync_mark_active(data->name, 0);
                else
                    goto error;
296
#endif /* APP_SYNC_DBUS */
297 298
            }
        }
phdeswer's avatar
phdeswer committed
299
    }
300

301
    return(0);
302 303

error:
304 305
    log_warning("Error launching a service!\n");
    return(1);
306 307
}

308
int appsync_activate_sync_post(const char *mode)
309
{
310
    GList *iter;
311

312
    log_debug("activate post sync");
313

314
    if( appsync_sync_list == 0 )
315 316 317 318
    {
        log_debug("No sync list! skipping post sync\n");
        return 0;
    }
319 320

#ifdef APP_SYNC_DBUS
321 322 323 324
    /* check dbus initialisation, skip dbus activated services if this fails */
    if(!dbusappsync_init())
    {
        log_debug("dbus setup failed => skipping dbus launched apps \n");
325
        appsync_no_dbus = 1;
326
    }
327 328
#endif /* APP_SYNC_DBUS */

329
    /* go through list and launch apps */
330
    for( iter = appsync_sync_list; iter; iter = g_list_next(iter) )
331
    {
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
        struct list_elem *data = iter->data;
        if(!strcmp(mode, data->mode))
        {
            /* launch only items marked as post, others are already running */
            if(!data->post)
                continue;
            log_debug("launching post-enum-app %s\n", data->name);
            if(data->systemd)
            {
                if(!systemd_control_service(data->name, SYSTEMD_START))
                    goto error;
                appsync_mark_active(data->name, 1);
            }
            else if(data->launch)
            {
                /* skipping if dbus session bus is not available,
348
                 * or not compiled in */
349
                if(appsync_no_dbus)
350
                    continue;
351
#ifdef APP_SYNC_DBUS
352 353 354
                else
                    if(dbusappsync_launch_app(data->launch) != 0)
                        goto error;
355
#endif /* APP_SYNC_DBUS */
356 357
            }
        }
358 359
    }

360
    return(0);
361 362

error:
363 364
    log_warning("Error launching a service!\n");
    return(1);
365 366
}

367
int appsync_mark_active(const gchar *name, int post)
368
{
369 370
    int ret = -1; // assume name not found
    int missing = 0;
371

372
    GList *iter;
373

374
    log_debug("%s-enum-app %s is started\n", post ? "post" : "pre", name);
375

376
    for( iter = appsync_sync_list; iter; iter = g_list_next(iter) )
377
    {
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
        struct list_elem *data = iter->data;

        if(!strcmp(data->name, name))
        {
            /* TODO: do we need to worry about duplicate names in the list? */
            ret = (data->state != APP_STATE_ACTIVE);
            data->state = APP_STATE_ACTIVE;

            /* updated + missing -> not going to enumerate */
            if( missing ) break;
        }
        else if( data->state == APP_STATE_INACTIVE && data->post == post )
        {
            missing = 1;

            /* updated + missing -> not going to enumerate */
            if( ret != -1 ) break;
        }
396
    }
397
    if( !post && !missing )
398
    {
399 400
        log_debug("All pre-enum-apps active. Let's enumerate\n");
        appsync_enumerate_usb();
401
    }
402 403 404

    /* -1=not found, 0=already active, 1=activated now */
    return ret;
405 406
}

407
static gboolean appsync_enumerate_usb_cb(gpointer data)
408
{
409
    (void)data;
410
    appsync_enumerate_usb_id = 0;
411 412 413 414
    log_debug("handling enumeration timeout");
    appsync_enumerate_usb();
    /* return false to stop the timer from repeating */
    return FALSE;
415 416
}

417
static void appsync_start_enumerate_usb_timer(void)
418
{
419
    log_debug("scheduling enumeration timeout");
420 421 422
    if( appsync_enumerate_usb_id )
        g_source_remove(appsync_enumerate_usb_id), appsync_enumerate_usb_id = 0;
    appsync_enumerate_usb_id = g_timeout_add_seconds(2, appsync_enumerate_usb_cb, NULL);
423 424
}

425
static void appsync_cancel_enumerate_usb_timer(void)
426
{
427
    if( appsync_enumerate_usb_id )
428 429
    {
        log_debug("canceling enumeration timeout");
430
        g_source_remove(appsync_enumerate_usb_id), appsync_enumerate_usb_id = 0;
431
    }
432 433
}

434
static void appsync_enumerate_usb(void)
435
{
436
    struct timeval tv;
phdeswer's avatar
phdeswer committed
437

438 439
    /* Stop the timer in case of explicit enumeration call */
    appsync_cancel_enumerate_usb_timer();
440

441 442
    /* Debug: how long it took from sync start to get here */
    gettimeofday(&tv, 0);
443
    timersub(&tv, &appsync_sync_tv, &tv);
444
    log_debug("sync to enum: %.3f seconds", tv.tv_sec + tv.tv_usec * 1e-6);
phdeswer's avatar
phdeswer committed
445

446
#ifdef APP_SYNC_DBUS
447 448
    /* remove dbus service */
    dbusappsync_cleanup();
449
#endif /* APP_SYNC_DBUS */
450
}
451

452
void appsync_stop_apps(int post)
453
{
454
    GList *iter = 0;
455

456
    for( iter = appsync_sync_list; iter; iter = g_list_next(iter) )
457
    {
458 459 460 461 462 463 464 465 466
        struct list_elem *data = iter->data;

        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))
                log_debug("Failed to stop %s\n", data->name);
            data->state = APP_STATE_DONTCARE;
        }
467 468 469
    }
}

470
int appsync_stop(gboolean force)
471
{
472 473 474
    /* If force arg is used, stop all applications that
     * could have been started by usb-moded */
    if(force)
475
    {
476 477 478
        GList *iter;
        log_debug("assuming all applications are active");

479
        for( iter = appsync_sync_list; iter; iter = g_list_next(iter) )
480 481 482 483
        {
            struct list_elem *data = iter->data;
            data->state = APP_STATE_ACTIVE;
        }
484
    }
485

486 487
    /* Stop post-apps 1st */
    appsync_stop_apps(1);
488

489 490
    /* Then pre-apps */
    appsync_stop_apps(0);
491

492 493 494
    /* Do not leave active timers behind */
    appsync_cancel_enumerate_usb_timer();
    return(0);
495
}