usb_moded.c 52.4 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 28 29 30 31 32 33 34 35 36
 * @file usb_moded.c
 *
 * Copyright (C) 2010 Nokia Corporation. All rights reserved.
 * Copyright (C) 2012-2018 Jolla. All rights reserved.
 *
 * @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: Jonni Rainisto <jonni.rainisto@jollamobile.com>
 * @author: Pekka Lundstrom <pekka.lundstrom@jollamobile.com>
 * @author: Vesa Halttunen <vesa.halttunen@jollamobile.com>
 * @author: Simo Piiroinen <simo.piiroinen@jollamobile.com>
 * @author: Thomas Perl <thomas.perl@jolla.com>
 * @author: Matti Lehtimaki <matti.lehtimaki@gmail.com>
 * @author: Thomas Perl <m@thp.io>
 * @author: Martin Jones <martin.jones@jollamobile.com>
 * @author: Andrew den Exter <andrew.den.exter@jolla.com>
 * @author: Andrew den Exter <andrew.den.exter@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
 */

37
#include <getopt.h>
38
#include <stdio.h>
39 40 41

#include <sys/stat.h>
#include <sys/wait.h>
42
#include <signal.h>
43

44
#include <libkmod.h>
45

46
#ifdef SYSTEMD
47
# include <systemd/sd-daemon.h>
48
#endif
49

50 51 52
#include "usb_moded.h"
#include "usb_moded-modes.h"
#include "usb_moded-dbus-private.h"
53
#include "usb_moded-udev.h"
54 55
#include "usb_moded-modules.h"
#include "usb_moded-log.h"
56
#include "usb_moded-devicelock.h"
57 58 59
#include "usb_moded-modesetting.h"
#include "usb_moded-modules.h"
#include "usb_moded-appsync.h"
60 61
#include "usb_moded-trigger.h"
#include "usb_moded-config.h"
62
#include "usb_moded-config-private.h"
63
#include "usb_moded-network.h"
64
#include "usb_moded-mac.h"
65
#include "usb_moded-android.h"
66
#include "usb_moded-configfs.h"
67
#include "usb_moded-systemd.h"
68

69
#ifdef MEEGOLOCK
70
# include "usb_moded-dsme.h"
71
#endif
72

73 74 75 76
/* ========================================================================= *
 * Constants
 * ========================================================================= */

77 78 79 80 81
/* Wakelogging is noisy, do not log it by default */
#ifndef  VERBOSE_WAKELOCKING
# define VERBOSE_WAKELOCKING 0
#endif

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
/** Default allowed cable detection delay
 *
 * To comply with USB standards, the delay should be
 * less than 2 seconds to ensure timely enumeration.
 *
 * Any value <= zero means no delay.
 */
#define CABLE_CONNECTION_DELAY_DEFAULT 0

/** Maximum allowed cable detection delay
 *
 * Must be shorter than initial probing delay expected by
 * dsme (currently 5 seconds) to avoid reboot loops in
 * act dead mode.
 *
 * And shorter than USB_MODED_SUSPEND_DELAY_DEFAULT_MS to
 * allow the timer to trigger also in display off scenarios.
 */

#define CABLE_CONNECTION_DELAY_MAXIMUM 4000

103 104 105
/* ========================================================================= *
 * Types
 * ========================================================================= */
106

107
/** A struct containing all the usb_moded info needed
108
 */
109
typedef struct usb_mode
110
{
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 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 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
    /** Connection status
     *
     * Access only via:
     * - usbmoded_get_connection_state()
     * - usbmoded_set_connection_state()
     */
    bool connected;

    /** Mount status, true for mounted -UNUSED atm- */
    bool mounted;

    /** The logical mode name
     *
     * Full set of valid modes can occur here
     */
    char *internal_mode;

    /* The hardware mode name
     *
     * How the usb hardware has been configured.
     *
     * For example internal_mode=MODE_ASK gets
     * mapped to hardware_mode=MODE_CHARGING */
    char *hardware_mode;

    /* The external mode;
     *
     * What was the last mode signaled over D-Bus.
     */
    char *external_mode;

    /**< The module name for the specific mode */
    char *module;

    /**< Contains the mode data */
    struct mode_list_elem *data;
} usb_mode;

/** Mapping usb mode from internal to hardware/broadcast use */
typedef struct modemapping_t
{
    /** Any valid usb mode */
    const char *internal_mode;

    /** Mode to use for usb configuration, or NULL = internal */
    const char *hardware_mode;

    /** Mode to use for D-Bus broadcast, or NULL = internal */
    const char *external_mode;
} modemapping_t;

/* ========================================================================= *
 * Prototypes
 * ========================================================================= */

/* -- usbmoded -- */

static const char *usbmoded_map_mode_to_hardware(const char *internal_mode);
static const char *usbmoded_map_mode_to_external(const char *internal_mode);

// ----------------------------------------------------------------
void                   usbmoded_rethink_usb_charging_fallback(void);

static bool            usbmoded_switch_to_charging           (void);
static void            usbmoded_switch_to_mode               (const char *mode);

const char            *usbmoded_get_hardware_mode            (void);
static void            usbmoded_update_hardware_mode         (void);

const char            *usbmoded_get_external_mode            (void);
static void            usbmoded_update_external_mode         (void);

// from here and there
const char            *usbmoded_get_usb_mode                 (void);
void                   usbmoded_set_usb_mode                 (const char *internal_mode);

// from usbmoded_set_usb_connected()
//      usbmoded_set_charger_connected()
bool                   usbmoded_get_connection_state         (void);
static bool            usbmoded_set_connection_state         (bool state);

// from usbmoded_set_usb_connected()
void                   usbmoded_set_usb_connected_state      (void);

// from udev cable_state_changed()
void                   usbmoded_set_usb_connected            (bool connected);
void                   usbmoded_set_charger_connected        (bool state);

// ----------------------------------------------------------------
// internal movements

static void            usbmoded_set_cable_connection_delay   (int delay_ms);

static bool            usbmoded_mode_in_list                 (const char *mode, char *const *modes);
int                    usbmoded_valid_mode                   (const char *mode);
gchar                 *usbmoded_get_mode_list                (mode_list_type_t type);

const char            *usbmoded_get_usb_module               (void);
209
bool                   usbmoded_set_usb_module               (const char *module);
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267

struct mode_list_elem *usbmoded_get_usb_mode_data            (void);
void                   usbmoded_set_usb_mode_data            (struct mode_list_elem *data);

void                   usbmoded_send_supported_modes_signal  (void);
void                   usbmoded_send_available_modes_signal  (void);
void                   usbmoded_send_hidden_modes_signal     (void);
void                   usbmoded_send_whitelisted_modes_signal(void);

static void            usbmoded_write_to_sysfs_file          (const char *path, const char *text);
void                   usbmoded_acquire_wakelock             (const char *wakelock_name);
void                   usbmoded_release_wakelock             (const char *wakelock_name);

static gboolean        usbmoded_allow_suspend_timer_cb       (gpointer aptr);
void                   usbmoded_allow_suspend                (void);
void                   usbmoded_delay_suspend                (void);

bool                   usbmoded_init_done_p                  (void);
void                   usbmoded_set_init_done                (bool reached);
void                   usbmoded_probe_init_done              (void);

void                   usbmoded_exit_mainloop                (int exitcode);
static void            usbmoded_handle_signal                (int signum);

static void            usbmoded_init                         (void);
static void            usbmoded_cleanup                      (void);

int                    usbmoded_system_                      (const char *file, int line, const char *func, const char *command);
FILE                  *usbmoded_popen_                       (const char *file, int line, const char *func, const char *command, const char *type);
void                   usbmoded_usleep_                      (const char *file, int line, const char *func, useconds_t usec);

static void            usbmoded_usage                        (void);

/* -- sigpipe -- */

static gboolean sigpipe_read_signal_cb (GIOChannel *channel, GIOCondition condition, gpointer data);
static void     sigpipe_trap_signal_cb (int sig);
static bool     sigpipe_crate_pipe     (void);
static void     sigpipe_trap_signals   (void);
static bool     sigpipe_init           (void);

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

static int usb_moded_exitcode = EXIT_FAILURE;
static GMainLoop *usb_moded_mainloop = NULL;

bool usbmoded_rescue_mode = false;
static bool diag_mode = false;
static bool hw_fallback = false;
#ifdef SYSTEMD
static bool systemd_notify = false;
#endif

/** Currently allowed cable detection delay
 */
int usbmoded_cable_connection_delay = CABLE_CONNECTION_DELAY_DEFAULT;
268

269
static struct usb_mode current_mode = {
270 271 272 273 274
    .connected = false,
    .mounted = false,
    .internal_mode = NULL,
    .hardware_mode = NULL,
    .external_mode = NULL,
275 276 277 278 279
    .module = NULL,
    .data = NULL,
};

static GList *modelist = 0;
280

281 282
/** Path to init-done flag file */
static const char init_done_flagfile[] = "/run/systemd/boot-status/init-done";
283

284 285
/** cached init-done-reached state */
static bool init_done_reached = false;
phdeswer's avatar
phdeswer committed
286

287 288
/** Flag for: USB_MODED_WAKELOCK_STATE_CHANGE has been acquired */
static bool blocking_suspend = false;
phdeswer's avatar
phdeswer committed
289

290 291
/** Timer for releasing USB_MODED_WAKELOCK_STATE_CHANGE */
static guint allow_suspend_timer_id = 0;
292

293 294 295 296
/** Pipe fd for transferring signals to mainloop context */
static int sigpipe_fd = -1;

static const modemapping_t modemapping[] =
297
{
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
    {
        .internal_mode = MODE_UNDEFINED,
        .hardware_mode = MODE_CHARGING,
        .external_mode = 0,
    },
    {
        .internal_mode = MODE_ASK,
        .hardware_mode = MODE_CHARGING,
        .external_mode = 0,
    },
    {
        .internal_mode = MODE_MASS_STORAGE,
        .hardware_mode = 0,
        .external_mode = 0,
    },
    {
        .internal_mode = MODE_DEVELOPER,
        .hardware_mode = 0,
        .external_mode = 0,
    },
    {
        .internal_mode = MODE_MTP,
        .hardware_mode = 0,
        .external_mode = 0,
    },
    {
        .internal_mode = MODE_HOST,
        .hardware_mode = 0,
        .external_mode = 0,
    },
    {
        .internal_mode = MODE_CONNECTION_SHARING,
        .hardware_mode = 0,
        .external_mode = 0,
    },
    {
        .internal_mode = MODE_DIAG,
        .hardware_mode = 0,
        .external_mode = 0,
    },
    {
        .internal_mode = MODE_ADB,
        .hardware_mode = 0,
        .external_mode = 0,
    },
    {
        .internal_mode = MODE_PC_SUITE,
        .hardware_mode = 0,
        .external_mode = 0,
    },
    {
        .internal_mode = MODE_CHARGING,
        .hardware_mode = MODE_CHARGING,
        .external_mode = 0,
    },
    {
        .internal_mode = MODE_CHARGING_FALLBACK,
        .hardware_mode = MODE_CHARGING,
        .external_mode = MODE_ASK,
    },
    {
        .internal_mode = MODE_CHARGER,
        .hardware_mode = MODE_CHARGING,
        .external_mode = 0,
    },
    // sentinel
    {
        .internal_mode = 0,
        .hardware_mode = 0,
        .external_mode = 0,
    }
};
370

371 372 373 374 375 376 377 378
/* ========================================================================= *
 * Functions
 * ========================================================================= */

static const char *
usbmoded_map_mode_to_hardware(const char *internal_mode)
{
    const char *hardware_mode = 0;
379

380 381 382 383 384 385 386
    for( size_t i = 0; modemapping[i].internal_mode; ++i ) {
        if( strcmp(modemapping[i].internal_mode, internal_mode) )
            continue;
        hardware_mode = modemapping[i].hardware_mode;
        break;
    }
    return hardware_mode ?: internal_mode;
387 388
}

389 390
static const char *
usbmoded_map_mode_to_external(const char *internal_mode)
391
{
392 393 394 395 396 397 398 399 400
    const char *external_mode = 0;

    for( size_t i = 0; modemapping[i].internal_mode; ++i ) {
        if( strcmp(modemapping[i].internal_mode, internal_mode) )
            continue;
        external_mode = modemapping[i].external_mode;
        break;
    }
    return external_mode ?: internal_mode;
401
}
402

403
/** Check if we can/should leave charging fallback mode
404 405 406
 *
 * Called when device lock status, or device status (dsme)
 * changes.
407 408
 */
void
409
usbmoded_rethink_usb_charging_fallback(void)
410
{
411 412
    /* Cable must be connected */
    if( !usbmoded_get_connection_state() )
413 414
        goto EXIT;

415 416
    /* Suitable usb-mode mode selected */
    const char *usb_mode = usbmoded_get_usb_mode();
417 418 419 420 421

    if( strcmp(usb_mode, MODE_UNDEFINED) &&
        strcmp(usb_mode, MODE_CHARGING_FALLBACK) )
        goto EXIT;

422
#ifdef MEEGOLOCK
423 424 425
    /* If device locking is supported, the device must be in
     * unlocked state (or rescue mode must be active).
     */
426
    if( !devicelock_have_export_permission() && !usbmoded_rescue_mode ) {
427 428 429 430 431 432
        log_notice("device is locked; stay in %s", usb_mode);
        goto EXIT;
    }

    /* Device must be in USER state or in rescue mode
     */
433
    if( !dsme_in_user_state() && !usbmoded_rescue_mode ) {
434 435 436
        log_notice("device is not in USER mode; stay in %s", usb_mode);
        goto EXIT;
    }
437
#endif
438 439

    log_debug("attempt to leave %s", usb_mode);
440
    usbmoded_set_usb_connected_state();
441 442 443 444

EXIT:
    return;
}
445

446 447 448
static bool usbmoded_switch_to_charging(void)
{
    bool ack = true;
449

450 451 452 453 454 455
    if( android_set_charging_mode() )
        goto SUCCESS;

    if( configfs_set_charging_mode() )
        goto SUCCESS;

456 457 458 459 460 461
    if( modules_in_use() ) {
        if( usbmoded_set_usb_module(MODULE_MASS_STORAGE) )
            goto SUCCESS;
        usbmoded_set_usb_module(MODULE_NONE);
    }

462 463 464 465 466
    log_err("switch to charging mode failed");

    ack = false;
SUCCESS:
    return ack;
467 468
}

469
static void usbmoded_switch_to_mode(const char *mode)
470
{
471
    /* set return to 1 to be sure to error out if no matching mode is found either */
472 473 474 475 476 477 478

    log_debug("Cleaning up previous mode");

    if( usbmoded_get_usb_mode_data() ) {
        modesetting_unset_dynamic_mode();
        usbmoded_set_usb_mode_data(NULL);
    }
479 480 481 482 483 484 485 486 487 488 489 490 491

    log_debug("Setting %s\n", mode);

    /* Mode mapping should mean we only see MODE_CHARGING here, but just
     * in case redirect fixed charging related things to charging ... */

    if( !strcmp(mode, MODE_CHARGING) ||
        !strcmp(mode, MODE_CHARGING_FALLBACK) ||
        !strcmp(mode, MODE_CHARGER) ||
        !strcmp(mode, MODE_UNDEFINED) ||
        !strcmp(mode, MODE_ASK)) {
        goto CHARGE;
    }
492

493
#ifdef MEEGOLOCK
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
    /* Potentially data exposing modes are allowed only when
     * device has been booted to user mode and unlocked.
     *
     * Except if rescue mode is still active.
     */
    bool can_export = (dsme_in_user_state() &&
                       devicelock_have_export_permission());

    if( !can_export && !usbmoded_rescue_mode ) {
        log_warning("Policy does not allow mode: %s", mode);
        goto CHARGE;
    }
#endif

    /* go through all the dynamic modes if the modelist exists*/
    for( GList *iter = modelist; iter; iter = g_list_next(iter) )
phdeswer's avatar
phdeswer committed
510
    {
511 512 513 514 515 516 517
        struct mode_list_elem *data = iter->data;
        if( strcmp(mode, data->mode_name) )
            continue;

        log_debug("Matching mode %s found.\n", mode);

        /* set data before calling any of the dynamic mode functions
518
         * as they will use the usbmoded_get_usb_mode_data function */
519 520
        usbmoded_set_usb_mode_data(data);

521 522 523 524 525 526 527
        if( !usbmoded_set_usb_module(data->mode_module) )
            break;

        if( !modesetting_set_dynamic_mode() )
            break;

        goto SUCCESS;
phdeswer's avatar
phdeswer committed
528
    }
529

530
    log_warning("mode setting failed, fall back to charging");
531
    usbmoded_set_usb_mode_data(NULL);
532

533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
CHARGE:
    if( usbmoded_switch_to_charging() )
        goto SUCCESS;

    log_crit("failed to activate charging, all bets are off");

    /* FIXME: double check this error path */

    /* If we get here then usb_module loading failed,
     * no mode matched, and charging setup failed too.
     */

    usbmoded_set_usb_module(MODULE_NONE);
    mode = MODE_UNDEFINED;
    log_debug("mode setting failed or device disconnected, mode to set was = %s\n", mode);

SUCCESS:
    return;
}
552

553 554 555
const char *usbmoded_get_hardware_mode(void)
{
    return current_mode.hardware_mode ?: MODE_UNDEFINED;
556 557
}

558
static void usbmoded_update_hardware_mode(void)
phdeswer's avatar
phdeswer committed
559
{
560 561
    const char *internal_mode = usbmoded_get_usb_mode();
    const char *hardware_mode = usbmoded_map_mode_to_hardware(internal_mode);
562

563 564 565
    gchar *previous = current_mode.hardware_mode;
    if( !g_strcmp0(previous, hardware_mode) )
        goto EXIT;
566

567 568
    log_debug("hardware_mode: %s -> %s",
              previous, hardware_mode);
phdeswer's avatar
phdeswer committed
569

570 571
    current_mode.hardware_mode = g_strdup(hardware_mode);
    g_free(previous);
572

573 574 575
    // DO THE MODESWITCH

    usbmoded_switch_to_mode(current_mode.hardware_mode);
phdeswer's avatar
phdeswer committed
576

577 578
EXIT:
    return;
phdeswer's avatar
phdeswer committed
579
}
580

581
const char *usbmoded_get_external_mode(void)
582
{
583 584
    return current_mode.external_mode ?: MODE_UNDEFINED;
}
585

586 587 588 589
static void usbmoded_update_external_mode(void)
{
    const char *internal_mode = usbmoded_get_usb_mode();
    const char *external_mode = usbmoded_map_mode_to_external(internal_mode);
590

591 592 593
    gchar *previous = current_mode.external_mode;
    if( !g_strcmp0(previous, external_mode) )
        goto EXIT;
594

595 596
    log_debug("external_mode: %s -> %s",
              previous, external_mode);
597

598 599
    current_mode.external_mode = g_strdup(external_mode);
    g_free(previous);
600

601
    // DO THE DBUS BROADCAST
602

603
    umdbus_send_state_signal(current_mode.external_mode);
604

605 606
EXIT:
    return;
607 608
}

609
/** get the usb mode
610 611 612 613
 *
 * @return the currently set mode
 *
 */
614
const char * usbmoded_get_usb_mode(void)
615
{
616
    return current_mode.internal_mode;
617 618
}

619
/** set the usb mode
620
 *
621
 * @param mode The requested USB mode
622
 */
623
void usbmoded_set_usb_mode(const char *mode)
624
{
625
    gchar *previous = current_mode.internal_mode;
626
    if( !g_strcmp0(previous, mode) )
627 628 629
        goto EXIT;

    log_debug("internal_mode: %s -> %s",
630
              previous, mode);
631

632
    current_mode.internal_mode = g_strdup(mode);
633 634 635 636 637 638 639 640 641 642
    g_free(previous);

    // PROPAGATE DOWN TO USB
    usbmoded_update_hardware_mode();

    // PROPAGATE UP DBUS
    usbmoded_update_external_mode();

EXIT:
    return;
643 644
}

645
/** Get if the cable (pc or charger) is connected or not
646
 *
647
 * @ return true if connected, false if disconnected
648
 */
649
bool usbmoded_get_connection_state(void)
650
{
651
    return current_mode.connected;
652 653
}

654
/** Set if the cable (pc or charger) is connected or not
655
 *
656
 * @param state true for connected, false for disconnected
657
 *
658
 * @return true if value changed, false otherwise
659
 */
660
static bool usbmoded_set_connection_state(bool state)
661
{
662 663 664 665 666 667 668 669
    bool changed = false;
    if( current_mode.connected != state ) {
        log_debug("current_mode.connected: %d -> %d",
                  current_mode.connected,  state);
        current_mode.connected = state;
        changed = true;
    }
    return changed;
670 671
}

672
/** set the chosen usb state
673 674
 *
 * gauge what mode to enter and then call usbmoded_set_usb_mode()
675 676
 *
 */
677
void usbmoded_set_usb_connected_state(void)
678
{
679 680
    char *mode_to_set = 0;
    bool can_export = true;
681

682 683 684 685 686 687
    if(usbmoded_rescue_mode)
    {
        log_debug("Entering rescue mode!\n");
        usbmoded_set_usb_mode(MODE_DEVELOPER);
        goto EXIT;
    }
688

689 690 691 692 693 694 695 696 697 698 699
    if(diag_mode)
    {
        log_debug("Entering diagnostic mode!\n");
        if(modelist)
        {
            GList *iter = modelist;
            struct mode_list_elem *data = iter->data;
            usbmoded_set_usb_mode(data->mode_name);
        }
        goto EXIT;
    }
700

701
    mode_to_set = config_get_mode_setting();
702

703 704 705 706 707 708 709
#ifdef MEEGOLOCK
    /* Check if we are allowed to export system contents 0 is unlocked.
     * We check also if the device is in user state or not.
     * If not we do not export anything. We presume ACT_DEAD charging
     */
    can_export = (devicelock_have_export_permission()
                  && dsme_in_user_state());
710 711
#endif

712 713 714 715 716 717 718
    if( mode_to_set && can_export )
    {
        /* This is safe to do here as the starting condition is
         * MODE_UNDEFINED, and having a devicelock being activated when
         * a mode is set will not interrupt it */
        if(!strcmp(mode_to_set, usbmoded_get_usb_mode()))
            goto EXIT;
719

720 721
        if (!strcmp(MODE_ASK, mode_to_set))
        {
722 723
            /* If charging mode is the only available selection, don't ask
             * just select it */
724 725 726 727 728 729 730 731
            gchar *available_modes = usbmoded_get_mode_list(AVAILABLE_MODES_LIST);
            if (!strcmp(MODE_CHARGING, available_modes)) {
                gchar *temp = mode_to_set;
                mode_to_set = available_modes;
                available_modes = temp;
            }
            g_free(available_modes);
        }
732

733 734 735
        if(!strcmp(MODE_ASK, mode_to_set))
        {
            /* send signal, mode will be set when the dialog service calls
736
             * the set_mode method call.
737 738 739 740
             */
            umdbus_send_state_signal(USB_CONNECTED_DIALOG_SHOW);

            /* in case there was nobody listening for the UI, they will know
741
             * that the UI is needed by requesting the current mode */
742 743 744 745 746 747 748 749
            usbmoded_set_usb_mode(MODE_ASK);
        }
        else
            usbmoded_set_usb_mode(mode_to_set);
    }
    else
    {
        /* config is corrupted or we do not have a mode configured, fallback to charging
750 751
         * We also fall back here in case the device is locked and we do not
         * export the system contents. Or if we are in acting dead mode.
752 753 754 755 756
         */
        usbmoded_set_usb_mode(MODE_CHARGING_FALLBACK);
    }
EXIT:
    free(mode_to_set);
757 758
}

759 760 761 762 763 764
/** set the usb connection status
 *
 * @param connected The connection status, true for connected
 *
 */
void usbmoded_set_usb_connected(bool connected)
765
{
766 767 768 769 770 771
    /* Do not go through the routine if already connected to avoid
     * spurious load/unloads due to faulty signalling
     * NOKIA: careful with devicelock
     */
    if( !usbmoded_set_connection_state(connected) )
        goto EXIT;
772

773 774
    if( usbmoded_get_connection_state() ) {
        log_debug("usb connected\n");
775

776 777
        /* signal usb connected */
        umdbus_send_state_signal(USB_CONNECTED);
778 779 780

        /* choose mode, then call  usbmoded_set_usb_mode(chosen_mode)
         */
781
        usbmoded_set_usb_connected_state();
782
    }
783 784
    else {
        log_debug("usb disconnected\n");
785 786
        umdbus_send_state_signal(USB_DISCONNECTED);
        usbmoded_set_usb_mode(MODE_UNDEFINED);
787
    }
788 789
EXIT:
    return;
790 791
}

792 793 794 795
/** set and track charger state
 *
 */
void usbmoded_set_charger_connected(bool state)
796
{
797
    /* check if charger is already connected
798
     * to avoid spamming dbus */
799 800 801 802 803 804 805 806 807 808 809 810 811
    if( !usbmoded_set_connection_state(state) )
        goto EXIT;

    if( usbmoded_get_connection_state() ) {
        umdbus_send_state_signal(CHARGER_CONNECTED);
        usbmoded_set_usb_mode(MODE_CHARGER);
    }
    else {
        umdbus_send_state_signal(CHARGER_DISCONNECTED);
        usbmoded_set_usb_mode(MODE_UNDEFINED);
    }
EXIT:
    return;
812 813
}

814 815 816 817 818
/** Helper for setting allowed cable detection delay
 *
 * Used for implementing --max-cable-delay=<ms> option.
 */
static void usbmoded_set_cable_connection_delay(int delay_ms)
819
{
820 821 822 823 824 825
    if( delay_ms < CABLE_CONNECTION_DELAY_MAXIMUM )
        usbmoded_cable_connection_delay = delay_ms;
    else {
        usbmoded_cable_connection_delay = CABLE_CONNECTION_DELAY_MAXIMUM;
        log_warning("using maximum connection delay: %d ms",
                    usbmoded_cable_connection_delay);
826 827 828
    }
}

829 830
/* check if a mode is in a list */
static bool usbmoded_mode_in_list(const char *mode, char * const *modes)
831
{
832 833 834 835 836 837 838 839 840
    int i;

    if (!modes)
        return false;

    for(i = 0; modes[i] != NULL; i++)
    {
        if(!strcmp(modes[i], mode))
            return true;
841
    }
842
    return false;
843 844
}

845
/** check if a given usb_mode exists
846
 *
847 848
 * @param mode The mode to look for
 * @return 0 if mode exists, 1 if it does not exist
849 850
 *
 */
851
int usbmoded_valid_mode(const char *mode)
852
{
853 854
    int valid = 1;
    /* MODE_ASK, MODE_CHARGER and MODE_CHARGING_FALLBACK are not modes that are settable seen their special 'internal' status
855
     * so we only check the modes that are announed outside. Only exception is the built in MODE_CHARGING */
856 857 858 859 860 861
    if(!strcmp(MODE_CHARGING, mode))
        valid = 0;
    else
    {
        char *whitelist;
        gchar **whitelist_split = NULL;
862

863 864 865 866 867 868
        whitelist = config_get_mode_whitelist();
        if (whitelist)
        {
            whitelist_split = g_strsplit(whitelist, ",", 0);
            g_free(whitelist);
        }
869

870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
        /* check dynamic modes */
        if(modelist)
        {
            GList *iter;

            for( iter = modelist; iter; iter = g_list_next(iter) )
            {
                struct mode_list_elem *data = iter->data;
                if(!strcmp(mode, data->mode_name))
                {
                    if (!whitelist_split || usbmoded_mode_in_list(data->mode_name, whitelist_split))
                        valid = 0;
                    break;
                }
            }
885

886 887 888 889
            g_strfreev(whitelist_split);
        }
    }
    return valid;
890 891 892

}

893 894 895 896
/** make a list of all available usb modes
 *
 * @param type The type of list to return. Supported or available.
 * @return a comma-separated list of modes (MODE_ASK not included as it is not a real mode)
897 898
 *
 */
899
gchar *usbmoded_get_mode_list(mode_list_type_t type)
900
{
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929
    GString *modelist_str;

    modelist_str = g_string_new(NULL);

    if(!diag_mode)
    {
        /* check dynamic modes */
        if(modelist)
        {
            GList *iter;
            char *hidden_modes_list, *whitelist;
            gchar **hidden_mode_split = NULL, **whitelist_split = NULL;

            hidden_modes_list = config_get_hidden_modes();
            if(hidden_modes_list)
            {
                hidden_mode_split = g_strsplit(hidden_modes_list, ",", 0);
                g_free(hidden_modes_list);
            }

            if (type == AVAILABLE_MODES_LIST)
            {
                whitelist = config_get_mode_whitelist();
                if (whitelist)
                {
                    whitelist_split = g_strsplit(whitelist, ",", 0);
                    g_free(whitelist);
                }
            }
930

931 932 933
            for( iter = modelist; iter; iter = g_list_next(iter) )
            {
                struct mode_list_elem *data = iter->data;
934

935 936 937
                /* skip items in the hidden list */
                if (usbmoded_mode_in_list(data->mode_name, hidden_mode_split))
                    continue;
938

939 940 941
                /* if there is a whitelist skip items not in the list */
                if (whitelist_split && !usbmoded_mode_in_list(data->mode_name, whitelist_split))
                    continue;
942

943 944 945
                modelist_str = g_string_append(modelist_str, data->mode_name);
                modelist_str = g_string_append(modelist_str, ", ");
            }
946

947 948
            g_strfreev(hidden_mode_split);
            g_strfreev(whitelist_split);
949 950
        }

951 952 953 954 955 956 957 958 959 960
        /* end with charging mode */
        g_string_append(modelist_str, MODE_CHARGING);
        return g_string_free(modelist_str, false);
    }
    else
    {
        /* diag mode. there is only one active mode */
        g_string_append(modelist_str, MODE_DIAG);
        return g_string_free(modelist_str, false);
    }
961 962
}

963 964 965
/** get the supposedly loaded module
 *
 * @return The name of the loaded module
966 967
 *
 */
968
const char * usbmoded_get_usb_module(void)
969
{
970
    return current_mode.module ?: MODULE_NONE;
971
}
972

973 974 975 976 977
/** set the loaded module
 *
 * @param module The module name for the requested mode
 *
 */
978
bool usbmoded_set_usb_module(const char *module)
979
{
980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
    bool ack = false;

    if( !module )
        module = MODULE_NONE;

    const char *current = usbmoded_get_usb_module();

    log_debug("current module: %s -> %s", current, module);

    if( !g_strcmp0(current, module) )
        goto SUCCESS;

    if( modules_unload_module(current) != 0 )
        goto EXIT;

    free(current_mode.module), current_mode.module = 0;

    if( modules_load_module(module) != 0 )
        goto EXIT;

    if( g_strcmp0(module, MODULE_NONE) )
        current_mode.module = strdup(module);

SUCCESS:
    ack = true;
EXIT:
    return ack;
1007 1008
}

1009 1010 1011 1012
/** get the usb mode data
 *
 * @return a pointer to the usb mode data
 *
1013
 */
1014
struct mode_list_elem * usbmoded_get_usb_mode_data(void)
1015
{
1016
    return current_mode.data;
1017 1018
}

1019 1020 1021
/** set the mode_list_elem data
 *
 * @param data mode_list_element pointer
1022 1023
 *
 */
1024
void usbmoded_set_usb_mode_data(struct mode_list_elem *data)
1025
{
1026 1027
    current_mode.data = data;
}
1028

1029 1030 1031 1032 1033 1034 1035 1036
/** Send supported modes signal
 */
void usbmoded_send_supported_modes_signal(void)
{
    gchar *mode_list = usbmoded_get_mode_list(SUPPORTED_MODES_LIST);
    umdbus_send_supported_modes_signal(mode_list);
    g_free(mode_list);
}
1037

1038 1039 1040 1041 1042 1043 1044 1045
/** Send available modes signal
 */
void usbmoded_send_available_modes_signal(void)
{
    gchar *mode_list = usbmoded_get_mode_list(AVAILABLE_MODES_LIST);
    umdbus_send_available_modes_signal(mode_list);
    g_free(mode_list);
}
1046

1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
/** Send hidden modes signal
 */
void usbmoded_send_hidden_modes_signal(void)
{
    gchar *mode_list = config_get_hidden_modes();
    if(mode_list) {
        // TODO: cleared list not signaled?
        umdbus_send_hidden_modes_signal(mode_list);
        g_free(mode_list);
    }
}
1058

1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
/** Send whitelisted modes signal
 */
void usbmoded_send_whitelisted_modes_signal(void)
{
    gchar *mode_list = config_get_mode_whitelist();
    if(mode_list) {
        // TODO: cleared list not signaled?
        umdbus_send_whitelisted_modes_signal(mode_list);
        g_free(mode_list);
    }
1069 1070
}

1071 1072 1073 1074 1075 1076 1077
/** Write string to already existing sysfs file
 *
 * Note: Attempts to write to nonexisting files are silently ignored.
 *
 * @param path Where to write
 * @param text What to write
 */
1078
static void usbmoded_write_to_sysfs_file(const char *path, const char *text)
1079
{
1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
    int fd = -1;

    if (!path || !text)
        goto EXIT;

    if ((fd = open(path, O_WRONLY)) == -1) {
        if (errno != ENOENT) {
            log_warning("%s: open for writing failed: %m", path);
        }
        goto EXIT;
    }

    if (write(fd, text, strlen(text)) == -1) {
        log_warning("%s: write failed : %m", path);
        goto EXIT;
    }
1096
EXIT:
1097 1098
    if (fd != -1)
        close(fd);
1099 1100 1101 1102
}

/** Acquire wakelock via sysfs
 *
1103
 * Wakelock must be released via usbmoded_release_wakelock().
1104 1105 1106 1107 1108 1109 1110 1111 1112
 *
 * Automatically terminating wakelock is used, so that we
 * do not block suspend  indefinately in case usb_moded
 * gets stuck or crashes.
 *
 * Note: The name should be unique within the system.
 *
 * @param wakelock_name Wake lock to be acquired
 */
1113
void usbmoded_acquire_wakelock(const char *wakelock_name)
1114
{
1115 1116 1117 1118 1119
    char buff[256];
    snprintf(buff, sizeof buff, "%s %lld",
             wakelock_name,
             USB_MODED_SUSPEND_DELAY_MAXIMUM_MS * 1000000LL);
    usbmoded_write_to_sysfs_file("/sys/power/wake_lock", buff);
1120

1121
#if VERBOSE_WAKELOCKING
1122
    log_debug("usbmoded_acquire_wakelock %s", wakelock_name);
1123
#endif
1124 1125 1126 1127 1128 1129
}

/** Release wakelock via sysfs
 *
 * @param wakelock_name Wake lock to be released
 */
1130
void usbmoded_release_wakelock(const char *wakelock_name)
1131
{
1132
#if VERBOSE_WAKELOCKING
1133
    log_debug("usbmoded_release_wakelock %s", wakelock_name);
1134
#endif
1135

1136
    usbmoded_write_to_sysfs_file("/sys/power/wake_unlock", wakelock_name);
1137 1138
}

1139
/** Timer callback for releasing wakelock acquired via usbmoded_delay_suspend()
1140
 *
1141
 * @param aptr callback argument (not used)
1142
 */
1143
static gboolean usbmoded_allow_suspend_timer_cb(gpointer aptr)
1144
{
1145 1146 1147 1148 1149 1150 1151
    (void)aptr;

    allow_suspend_timer_id = 0;

    usbmoded_allow_suspend();

    return FALSE;
1152 1153
}

1154
/** Release wakelock acquired via usbmoded_delay_suspend()
1155
 *
1156 1157
 * Meant to be called on usb-moded exit so that wakelocks
 * are not left behind.
1158
 */
1159
void usbmoded_allow_suspend(void)
1160
{
1161 1162 1163 1164
    if( allow_suspend_timer_id ) {
        g_source_remove(allow_suspend_timer_id),
            allow_suspend_timer_id = 0;
    }
1165

1166 1167 1168 1169
    if( blocking_suspend ) {
        blocking_suspend = false;
        usbmoded_release_wakelock(USB_MODED_WAKELOCK_STATE_CHANGE);
    }
1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181
}

/** Block suspend briefly
 *
 * Meant to be called in situations usb activity might have woken
 * up the device (cable connect while display is off), or could
 * allow device to suspend (cable disconnect while display is off).
 *
 * Allows usb moded some time to finish asynchronous activity and
 * other processes to receive and process state changes broadcast
 * by usb-moded.
 */
1182
void usbmoded_delay_suspend(void)
1183
{
1184 1185 1186
    /* Use of automatically terminating wakelocks also means we need
     * to renew the wakelock when extending the suspend delay. */
    usbmoded_acquire_wakelock(USB_MODED_WAKELOCK_STATE_CHANGE);
1187

1188
    blocking_suspend = true;
1189

1190 1191
    if( allow_suspend_timer_id )
        g_source_remove(allow_suspend_timer_id);
1192

1193 1194 1195
    allow_suspend_timer_id =
        g_timeout_add(USB_MODED_SUSPEND_DELAY_DEFAULT_MS,
                      usbmoded_allow_suspend_timer_cb, 0);
1196 1197
}

1198 1199 1200 1201
/** Check if system has already been successfully booted up
 *
 * @return true if init-done has been reached, or false otherwise
 */
1202
bool usbmoded_init_done_p(void)
1203
{
1204
    return init_done_reached;
1205 1206 1207
}

/** Update cached init-done-reached state */
1208
void usbmoded_set_init_done(bool reached)
1209
{
1210 1211 1212 1213 1214
    if( init_done_reached != reached ) {
        init_done_reached = reached;
        log_warning("init_done -> %s",
                    init_done_reached ? "reached" : "not reached");
    }
1215 1216 1217
}

/** Check whether init-done flag file exists */
1218
void usbmoded_probe_init_done(void)
1219
{
1220
    usbmoded_set_init_done(access(init_done_flagfile, F_OK) == 0);
1221 1222
}

1223 1224
/** Request orderly exit from mainloop
 */
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244
void usbmoded_exit_mainloop(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);
}

static void usbmoded_handle_signal(int signum)
1245
{
1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261
    log_debug("handle signal: %s\n", strsignal(signum));

    if( signum == SIGTERM )
    {
        /* Assume: Stopped by init process */
        usbmoded_exit_mainloop(EXIT_SUCCESS);
    }
    else if( signum == SIGHUP )
    {
        /* free and read in modelist again */
        dynconfig_free_mode_list(modelist);

        modelist = dynconfig_read_mode_list(diag_mode);

        usbmoded_send_supported_modes_signal();
        usbmoded_send_available_modes_signal();
1262 1263

        // FIXME invalidate current mode
1264 1265 1266 1267 1268 1269 1270 1271 1272 1273
    }
    else
    {
        usbmoded_exit_mainloop(EXIT_FAILURE);
    }
}

/* set default values for usb_moded */
static void usbmoded_init(void)
{
1274 1275
    current_mode.connected     = false;
    current_mode.mounted       = false;
1276 1277 1278
    current_mode.internal_mode = strdup(MODE_UNDEFINED);
    current_mode.hardware_mode = NULL;
    current_mode.external_mode = NULL;
1279
    current_mode.module        = NULL;
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360

    /* check config, merge or create if outdated */
    if(config_merge_conf_file() != 0)
    {
        log_err("Cannot create or find a valid configuration. Exiting.\n");
        exit(1);
    }

#ifdef APP_SYNC
    appsync_read_list(diag_mode);
#endif

    /* always read dyn modes even if appsync is not used */
    modelist = dynconfig_read_mode_list(diag_mode);

    if(config_check_trigger())
        trigger_init();

    /* Set-up mac address before kmod */
    if(access("/etc/modprobe.d/g_ether.conf", F_OK) != 0)
    {
        mac_generate_random_mac();
    }

    /* During bootup the sysfs control structures might
     * not be already in there when usb-moded starts up.
     * Retry few times unless init done is / gets reached
     * while waiting.
     */
    for( int i = 10; ; ) {
        if( configfs_init_values() )
            break;

        if( android_init_values() )
            break;

        /* Must probe /pollsince we're not yet running mainloop */
        usbmoded_probe_init_done();

        if( usbmoded_init_done_p() || --i <= 0 ) {
            if( !modules_init() )
                log_crit("No supported usb control mechanisms found");
            break;
        }

        usbmoded_msleep(1000);
    }

    /* TODO: add more start-up clean-up and init here if needed */
}

/** Release resources allocated by usbmoded_init()
 */
static void usbmoded_cleanup(void)
{
    /* Undo modules_init() */
    modules_quit();

    /* Undo trigger_init() */
    trigger_stop();

    /* Undo dynconfig_read_mode_list() */
    dynconfig_free_mode_list(modelist);

#ifdef APP_SYNC
    /* Undo appsync_read_list() */
    appsync_free_appsync_list();
#endif

    /* Release dynamic memory */
    free(current_mode.module),
        current_mode.module = 0;

    free(current_mode.internal_mode),
        current_mode.internal_mode = 0;

    free(current_mode.hardware_mode),
        current_mode.hardware_mode = 0;

    free(current_mode.external_mode),
        current_mode.external_mode = 0;
1361
}
1362

1363 1364 1365
/** Wrapper to give visibility to blocking system() calls usb-moded is making
 */
int
1366 1367
usbmoded_system_(const char *file, int line, const char *func,
                 const char *command)
1368
{
1369 1370
    log_debug("EXEC %s; from %s:%d: %s()",
              command, file, line, func);
1371

1372
    int rc = system(command);
1373

1374 1375
    if( rc != 0 )
        log_warning("EXEC %s; exit code is %d", command, rc);
1376

1377
    return rc;
1378 1379
}

1380 1381 1382
/** Wrapper to give visibility subprocesses usb-moded is invoking via popen()
 */
FILE *
1383 1384
usbmoded_popen_(const char *file, int line, const char *func,
                const char *command, const char *type)
1385
{
1386 1387
    log_debug("EXEC %s; from %s:%d: %s()",
              command, file, line, func);
1388

1389
    return popen(command, type);
1390 1391
}

1392 1393 1394
/** Wrapper to give visibility to blocking sleeps usb-moded is making
 */
void
1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564
usbmoded_usleep_(const char *file, int line, const char *func,
                 useconds_t usec)
{
    struct timespec ts = {
        .tv_sec  = (usec / 1000000),
        .tv_nsec = (usec % 1000000) * 1000
    };

    long ms = (ts.tv_nsec + 1000000 - 1) / 1000000;

    if( !ms ) {
        log_debug("SLEEP %ld seconds; from %s:%d: %s()",
                  (long)ts.tv_sec, file, line, func);
    }
    else if( ts.tv_sec ) {
        log_debug("SLEEP %ld.%03ld seconds; from %s:%d: %s()",
                  (long)ts.tv_sec, ms, file, line, func);
    }
    else {
        log_debug("SLEEP %ld milliseconds; from %s:%d: %s()",
                  ms, file, line, func);
    }

    do { } while( nanosleep(&ts, &ts) == -1 && errno != EINTR );
}

/** Glib io watch callback for reading signals from signal pipe
 *
 * @param channel   glib io channel
 * @param condition wakeup reason
 * @param data      user data (unused)
 *
 * @return TRUE to keep the iowatch, or FALSE to disable it
 */
static gboolean
sigpipe_read_signal_cb(GIOChannel *channel,
                       GIOCondition condition,
                       gpointer data)
{
    gboolean keep_watch = FALSE;

    int fd, rc, sig;

    (void)data;

    /* Should never happen, but we must disable the io watch
     * if the pipe fd still goes into unexpected state ... */
    if( condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL) )
        goto EXIT;

    if( (fd = g_io_channel_unix_get_fd(channel)) == -1 )
        goto EXIT;

    /* If the actual read fails, terminate with core dump */
    rc = TEMP_FAILURE_RETRY(read(fd, &sig, sizeof sig));
    if( rc != (int)sizeof sig )
        abort();

    /* handle the signal */
    usbmoded_handle_signal(sig);

    keep_watch = TRUE;

EXIT:
    if( !keep_watch )
        log_crit("disabled signal handler io watch\n");

    return keep_watch;
}

/** Async signal handler for writing signals to signal pipe
 *
 * @param sig the signal number to pass to mainloop via pipe
 */
static void
sigpipe_trap_signal_cb(int sig)
{
    /* NOTE: This function *MUST* be kept async-signal-safe! */

    static volatile int exit_tries = 0;

    int rc;

    /* Restore signal handler */
    signal(sig, sigpipe_trap_signal_cb);

    switch( sig )
    {
    case SIGINT:
    case SIGQUIT:
    case SIGTERM:
        /* If we receive multiple signals that should have
         * caused the process to exit, assume that mainloop
         * is stuck and terminate with core dump. */
        if( ++exit_tries >= 2 )
            abort();
        break;

    default:
        break;
    }

    /* Transfer the signal to mainloop via pipe ... */
    rc = TEMP_FAILURE_RETRY(write(sigpipe_fd, &sig, sizeof sig));

    /* ... or terminate with core dump in case of failures */
    if( rc != (int)sizeof sig )
        abort();
}

/** Create a pipe and io watch for handling signal from glib mainloop
 *
 * @return true on success, or false in case of errors
 */
static bool
sigpipe_crate_pipe(void)
{
    bool        res    = false;
    GIOChannel *chn    = 0;
    int         pfd[2] = { -1, -1 };

    if( pipe2(pfd, O_CLOEXEC) == -1 )
        goto EXIT;

    if( (chn = g_io_channel_unix_new(pfd[0])) == 0 )
        goto EXIT;

    if( !g_io_add_watch(chn, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
                        sigpipe_read_signal_cb, 0) )
        goto EXIT;

    g_io_channel_set_close_on_unref(chn, true), pfd[0] = -1;
    sigpipe_fd = pfd[1], pfd[1] = -1;

    res = true;

EXIT:
    if( chn ) g_io_channel_unref(chn);
    if( pfd[0] != -1 ) close(pfd[0]);
    if( pfd[1] != -1 ) close(pfd[1]);

    return res;
}

/** Install async signal handlers
 */
static void
sigpipe_trap_signals(void)
{
    static const int sig[] =
    {
        SIGINT,
        SIGQUIT,
        SIGTERM,
        SIGHUP,
        -1
    };

    for( size_t i = 0; sig[i] != -1; ++i )
    {
        signal(sig[i], sigpipe_trap_signal_cb);
    }
}

/** Initialize signal trapping
 *
 * @return true on success, or false in case of errors
 */
static bool
sigpipe_init(void)
1565
{
1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622
    bool success = false;

    if( !sigpipe_crate_pipe() )
        goto EXIT;

    sigpipe_trap_signals();

    success = true;

EXIT:
    return success;
}

/* ========================================================================= *
 * MAIN ENTRY
 * ========================================================================= */

/* Display usbmoded_usage information */
static void usbmoded_usage(void)
{
    fprintf(stdout,
            "Usage: usb_moded [OPTION]...\n"
            "USB mode daemon\n"
            "\n"
            "  -a,  --android_usb_broken\n"
            "      keep gadget active on broken android kernels\n"
            "  -i,  --android_usb_broken_udev_events\n"
            "      ignore incorrect disconnect events after mode setting\n"
            "  -f,  --fallback       \n"
            "      assume always connected\n"
            "  -s,  --force-syslog \n"
            "      log to syslog\n"
            "  -T,  --force-stderr \n"
            "      log to stderr\n"
            "  -l,  --log-line-info\n"
            "      log to stderr and show origin of logging\n"
            "  -D,  --debug  \n"
            "      turn on debug printing\n"
            "  -d,  --diag   \n"
            "      turn on diag mode\n"
            "  -h,  --help         \n"
            "      display this help and exit\n"
            "  -r,  --rescue         \n"
            "      rescue mode\n"
#ifdef SYSTEMD
            "  -n,  --systemd      \n"
            "      notify systemd when started up\n"
#endif
            "  -v,  --version      \n"
            "      output version information and exit\n"
            "  -m,  --max-cable-delay=<ms>\n"
            "      maximum delay before accepting cable connection\n"
            "  -b,  --android-bootup-function=<function>\n"
            "      Setup given function during bootup. Might be required\n"
            "      on some devices to make enumeration work on the 1st\n"
            "      cable connect.\n"
            "\n");
1623
}
1624

1625 1626
int main(int argc, char* argv[])
{
1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659
    int opt = 0, opt_idx = 0;

    struct option const options[] = {
        { "android_usb_broken", no_argument, 0, 'a' },
        { "android_usb_broken_udev_events", no_argument, 0, 'i' },
        { "fallback", no_argument, 0, 'd' },
        { "force-syslog", no_argument, 0, 's' },
        { "force-stderr", no_argument, 0, 'T' },
        { "log-line-info", no_argument, 0, 'l' },
        { "debug", no_argument, 0, 'D' },
        { "diag", no_argument, 0, 'd' },
        { "help", no_argument, 0, 'h' },
        { "rescue", no_argument, 0, 'r' },
        { "systemd", no_argument, 0, 'n' },
        { "version", no_argument, 0, 'v' },
        { "max-cable-delay", required_argument, 0, 'm' },
        { "android-bootup-function", required_argument, 0, 'b' },
        { 0, 0, 0, 0 }
    };

    log_init();
    log_set_name(basename(*argv));

    /* - - - - - - - - - - - - - - - - - - - *
     * OPTIONS
     * - - - - - - - - - - - - - - - - - - - */

    /* Parse the command-line options */
    while ((opt = getopt_long(argc, argv, "aifsTlDdhrnvm:b:", options, &opt_idx)) != -1)
    {
        switch (opt)
        {
        case 'a':
1660
            log_warning("Deprecated option: --android_usb_broken");
1661 1662
            break;
        case 'i':
1663
            log_warning("Deprecated option: --android_usb_broken_udev_events");
1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695
            break;
        case 'f':
            hw_fallback = true;
            break;
        case 's':
            log_set_type(LOG_TO_SYSLOG);
            break;

        case 'T':
            log_set_type(LOG_TO_STDERR);
            break;

        case 'D':
            log_set_level(LOG_DEBUG);
            break;

        case 'l':
            log_set_type(LOG_TO_STDERR);
            log_set_lineinfo(true);
            break;

        case 'd':
            diag_mode = true;
            break;

        case 'h':
            usbmoded_usage();
            exit(0);

        case 'r':
            usbmoded_rescue_mode = true;
            break;
1696
#ifdef SYSTEMD
1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709
        case 'n':
            systemd_notify = true;
            break;
#endif
        case 'v':
            printf("USB mode daemon version: %s\n", VERSION);
            exit(0);

        case 'm':
            usbmoded_set_cable_connection_delay(strtol(optarg, 0, 0));
            break;

        case 'b':
1710
            log_warning("Deprecated option: --android-bootup-function");
1711 1712 1713 1714 1715
            break;

        default:
            usbmoded_usage();
            exit(0);
1716
        }
1717
    }
1718

1719 1720
    fprintf(stderr, "usb_moded %s starting\n", VERSION);
    fflush(stderr);
1721

1722 1723 1724
    /* - - - - - - - - - - - - - - - - - - - *
     * INITIALIZE
     * - - - - - - - - - - - - - - - - - - - */
1725

1726 1727 1728 1729 1730 1731 1732 1733 1734 1735
    /* silence usbmoded_system() calls */
    if( log_get_type() != LOG_TO_STDERR && log_get_level() != LOG_DEBUG )
    {
        if( !freopen("/dev/null", "a", stdout) ) {
            log_err("can't redirect stdout: %m");
        }
        if( !freopen("/dev/null", "a", stderr) ) {
            log_err("can't redirect stderr: %m");
        }
    }
1736

1737
#if !GLIB_CHECK_VERSION(2, 36, 0)
1738
    g_type_init();
1739
#endif
1740
#if !GLIB_CHECK_VERSION(2, 31, 0)
1741
    g_thread_init(NULL);
1742
#endif
1743

1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762