Commit 899ce529 authored by spiiroin's avatar spiiroin

Merge branch 'jb35683_mode_change' into 'master'

Mode change and cable detection fixes

Also contains fixes for various issues spotted while debugging.

See merge request !12
parents ed512dc6 931f01a2
......@@ -22,7 +22,7 @@ test_gcc_flag() {
# We use gnu99 instead of c99 because many have interpreted the standard
# in a way that int64_t isn't defined on non-64 bit platforms.
CFLAGS="-Os -std=gnu99 -Wall -W -Wextra -pedantic -pipe -Wformat -Wold-style-definition -Wdeclaration-after-statement -Wfloat-equal -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wendif-labels -Wpointer-arith -Wcast-align -Wwrite-strings -Winline -Wno-unused-parameter -finline-small-functions -Wno-unused-result -fstack-protector -D_FORTIFY_SOURCE=2 -Wl,-z,relro,-z,now -fPIE -fpie -pie"
CFLAGS="-Os -std=gnu99 -Wall -W -Wextra -pipe -Wformat -Wold-style-definition -Wdeclaration-after-statement -Wfloat-equal -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wendif-labels -Wpointer-arith -Wcast-align -Wwrite-strings -Winline -Wno-unused-parameter -finline-small-functions -Wno-unused-result -fstack-protector -D_FORTIFY_SOURCE=2 -Wl,-z,relro,-z,now -fPIE -fpie -pie"
AC_ARG_ENABLE([debug], AS_HELP_STRING([--enable-debug],[Enable debug @<:@default=false@:>@]),
[case "${enableval}" in
......
......@@ -81,6 +81,10 @@ void android_init_values(void)
}
/* For rndis to be discovered correctly in M$ Windows (vista and later) */
write_to_file("/sys/class/android_usb/f_rndis/wceis", "1");
/* Make sure android_usb does not stay disabled in case usb moded
* has crashed / been killed in inconvenient time. */
write_to_file("/sys/class/android_usb/android0/enable", "1");
}
/* Set a charging mode for the android gadget
......
......@@ -41,12 +41,14 @@
#include "usb_moded-systemd.h"
static struct list_elem *read_file(const gchar *filename, int diag);
static gboolean enumerate_usb(gpointer data);
static void enumerate_usb(void);
static gboolean enumerate_usb_cb(gpointer data);
static void start_enumerate_usb_timer(void);
static void cancel_enumerate_usb_timer(void);
static GList *sync_list = NULL;
static unsigned sync_tag = 0;
static unsigned enum_tag = 0;
static guint enumerate_usb_id = 0;
static struct timeval sync_tv;
#ifdef APP_SYNC_DBUS
static int no_dbus = 0;
......@@ -55,21 +57,26 @@ static int no_dbus = 0;
#endif /* APP_SYNC_DBUS */
static void free_elem(gpointer aptr)
static void free_elem(struct list_elem *elem)
{
struct list_elem *elem = aptr;
free(elem->name);
free(elem->launch);
free(elem->mode);
g_free(elem->name);
g_free(elem->launch);
g_free(elem->mode);
free(elem);
}
static void free_elem_cb(gpointer elem, gpointer user_data)
{
(void)user_data;
free_elem(elem);
}
void free_appsync_list(void)
{
if( sync_list != 0 )
{
/*g_list_free_full(sync_list, free_elem); */
g_list_foreach (sync_list, (GFunc) free_elem, NULL);
g_list_foreach (sync_list, free_elem_cb, NULL);
g_list_free (sync_list);
sync_list = 0;
log_debug("Appsync list freed\n");
......@@ -163,6 +170,7 @@ static struct list_elem *read_file(const gchar *filename, int diag)
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;
cleanup:
......@@ -185,41 +193,42 @@ cleanup:
int activate_sync(const char *mode)
{
GList *iter;
int count = 0, count2 = 0;
int count = 0;
log_debug("activate sync");
/* Bump tag, see enumerate_usb() */
++sync_tag; gettimeofday(&sync_tv, 0);
/* Get start of activation timestamp */
gettimeofday(&sync_tv, 0);
if( sync_list == 0 )
{
log_debug("No sync list! Enumerating\n");
enumerate_usb(NULL);
enumerate_usb();
return 0;
}
/* set list to inactive, mark other modes as active already */
/* Count apps that need to be activated for this mode and
* mark them as currently inactive */
for( iter = sync_list; iter; iter = g_list_next(iter) )
{
struct list_elem *data = iter->data;
count++;
if(!strcmp(data->mode, mode))
data->active = 0;
{
++count;
data->state = APP_STATE_INACTIVE;
}
else
{
count2++;
data->active = 1;
data->state = APP_STATE_DONTCARE;
}
}
/* if the number of active modes is equal to the number of existing modes
we enumerate immediately */
if(count == count2)
/* If there is nothing to activate, enumerate immediately */
if(count <= 0)
{
log_debug("Nothing to launch.\n");
enumerate_usb(NULL);
enumerate_usb();
return(0);
}
......@@ -233,8 +242,7 @@ int activate_sync(const char *mode)
#endif /* APP_SYNC_DBUS */
/* start timer */
log_debug("Starting appsync timer\n");
g_timeout_add_seconds(2, enumerate_usb, NULL);
start_enumerate_usb_timer();
/* go through list and launch apps */
for( iter = sync_list; iter; iter = g_list_next(iter) )
......@@ -245,10 +253,9 @@ int activate_sync(const char *mode)
/* do not launch items marked as post, will be launched after usb is up */
if(data->post)
{
mark_active(data->name, data->post);
continue;
}
log_debug("launching app %s\n", data->name);
log_debug("launching pre-enum-app %s\n", data->name);
if(data->systemd)
{
if(!systemd_control_service(data->name, SYSTEMD_START))
......@@ -310,11 +317,12 @@ int activate_sync_post(const char *mode)
/* launch only items marked as post, others are already running */
if(!data->post)
continue;
log_debug("launching app %s\n", data->name);
log_debug("launching post-enum-app %s\n", data->name);
if(data->systemd)
{
if(systemd_control_service(data->name, SYSTEMD_START))
goto error;
mark_active(data->name, 1);
}
else if(data->launch)
{
......@@ -345,22 +353,22 @@ int mark_active(const gchar *name, int post)
GList *iter;
if(post)
log_debug("App %s notified it is ready\n", name);
log_debug("%s-enum-app %s is started\n", post ? "post" : "pre", name);
for( iter = sync_list; iter; iter = g_list_next(iter) )
{
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->active;
data->active = 1;
ret = (data->state != APP_STATE_ACTIVE);
data->state = APP_STATE_ACTIVE;
/* updated + missing -> not going to enumerate */
if( missing ) break;
}
else if( data->active == 0 )
else if( data->state == APP_STATE_INACTIVE && data->post == post )
{
missing = 1;
......@@ -368,49 +376,62 @@ int mark_active(const gchar *name, int post)
if( ret != -1 ) break;
}
}
if( !missing )
if( !post && !missing )
{
log_debug("All apps active. Let's enumerate\n");
enumerate_usb(NULL);
log_debug("All pre-enum-apps active. Let's enumerate\n");
enumerate_usb();
}
/* -1=not found, 0=already active, 1=activated now */
return ret;
}
static gboolean enumerate_usb(gpointer data)
static gboolean enumerate_usb_cb(gpointer data)
{
struct timeval tv;
(void)data;
enumerate_usb_id = 0;
log_debug("handling enumeration timeout");
enumerate_usb();
/* return false to stop the timer from repeating */
return FALSE;
}
/* We arrive here twice: when app sync is done
* and when the app sync timeout gets triggered.
* The tags are used to filter out these repeats.
*/
static void start_enumerate_usb_timer(void)
{
log_debug("scheduling enumeration timeout");
if( enumerate_usb_id )
g_source_remove(enumerate_usb_id), enumerate_usb_id = 0;
enumerate_usb_id = g_timeout_add_seconds(2, enumerate_usb_cb, NULL);
}
if( enum_tag == sync_tag )
static void cancel_enumerate_usb_timer(void)
{
if( enumerate_usb_id )
{
log_debug("ignoring enumeration trigger");
log_debug("canceling enumeration timeout");
g_source_remove(enumerate_usb_id), enumerate_usb_id = 0;
}
else
{
}
enum_tag = sync_tag;
static void enumerate_usb(void)
{
struct timeval tv;
/* Debug: how long it took from sync start to get here */
gettimeofday(&tv, 0);
timersub(&tv, &sync_tv, &tv);
log_debug("sync to enum: %.3f seconds", tv.tv_sec + tv.tv_usec * 1e-6);
/* Stop the timer in case of explicit enumeration call */
cancel_enumerate_usb_timer();
/* Debug: how long it took from sync start to get here */
gettimeofday(&tv, 0);
timersub(&tv, &sync_tv, &tv);
log_debug("sync to enum: %.3f seconds", tv.tv_sec + tv.tv_usec * 1e-6);
#ifdef APP_SYNC_DBUS
/* remove dbus service */
usb_moded_appsync_cleanup();
/* remove dbus service */
usb_moded_appsync_cleanup();
#endif /* APP_SYNC_DBUS */
}
/* return false to stop the timer from repeating */
return FALSE;
}
int appsync_stop(void)
static void appsync_stop_apps(int post)
{
GList *iter = 0;
......@@ -418,12 +439,39 @@ int appsync_stop(void)
{
struct list_elem *data = iter->data;
if(data->systemd)
if(data->systemd && data->state == APP_STATE_ACTIVE && data->post == post)
{
if(!systemd_control_service(data->name, SYSTEMD_STOP))
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;
}
}
}
int appsync_stop(int force)
{
/* If force arg is used, stop all applications that
* could have been started by usb-moded */
if(force)
{
GList *iter;
log_debug("assuming all applications are active");
for( iter = sync_list; iter; iter = g_list_next(iter) )
{
struct list_elem *data = iter->data;
data->state = APP_STATE_ACTIVE;
}
}
/* Stop post-apps 1st */
appsync_stop_apps(1);
/* Then pre-apps */
appsync_stop_apps(0);
/* Do not leave active timers behind */
cancel_enumerate_usb_timer();
return(0);
}
......@@ -29,6 +29,15 @@
#define APP_INFO_SYSTEMD_KEY "systemd"
#define APP_INFO_POST "post"
typedef enum {
/** Application is not relevant for the current mode */
APP_STATE_DONTCARE = 0,
/** Application should be started */
APP_STATE_INACTIVE = 1,
/** Application should be stopped when exiting the mode */
APP_STATE_ACTIVE = 2,
} app_state_t;
/**
* keep all the needed info together for launching an app
*/
......@@ -38,7 +47,7 @@ typedef struct list_elem
char *name; /* name of the app to launch */
char *mode; /* mode in which to launch the app */
char *launch; /* dbus launch command/address */
int active; /* marker to check if the app has started sucessfully */
app_state_t state; /* marker to check if the app has started sucessfully */
int systemd; /* marker to know if we start it with systemd or not */
int post; /* marker to indicate when to start the app */
/*@}*/
......@@ -48,6 +57,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(void);
int appsync_stop(int force);
void free_appsync_list(void);
void usb_moded_appsync_cleanup(void);
......@@ -66,10 +66,10 @@ static int validate_ip(const char *ipadd)
return 0;
}
const char *find_mounts(void)
char *find_mounts(void)
{
const char *ret = NULL;
char *ret = NULL;
ret = get_conf_string(FS_MOUNT_ENTRY, FS_MOUNT_KEY);
if(ret == NULL)
......@@ -86,7 +86,7 @@ int find_sync(void)
return(get_conf_int(FS_SYNC_ENTRY, FS_SYNC_KEY));
}
const char * find_alt_mount(void)
char * find_alt_mount(void)
{
return(get_conf_string(ALT_MOUNT_ENTRY, ALT_MOUNT_KEY));
}
......@@ -355,22 +355,14 @@ char * get_mode_setting(void)
* @param key: key value of the entry we want to read
* @new_value: potentially new value we want to compare against
*
* @return: 1 when the old value is the same as the new one, 0 otherwise
* @return: 0 when the old value is the same as the new one, 1 otherwise
*/
int config_value_changed(GKeyFile *settingsfile, const char *entry, const char *key, const char *new_value)
{
char *old = g_key_file_get_string(settingsfile, entry, key, NULL);
if (old)
{
gboolean unchanged = (g_strcmp0(old, entry) == 0);
g_free(old);
if (unchanged)
{
return 1;
}
}
return 0;
char *old_value = g_key_file_get_string(settingsfile, entry, key, NULL);
int changed = (g_strcmp0(old_value, new_value) != 0);
g_free(old_value);
return changed;
}
set_config_result_t set_config_setting(const char *entry, const char *key, const char *value)
......@@ -384,7 +376,7 @@ set_config_result_t set_config_setting(const char *entry, const char *key, const
test = g_key_file_load_from_file(settingsfile, FS_MOUNT_CONFIG_FILE, G_KEY_FILE_NONE, NULL);
if(test)
{
if(config_value_changed(settingsfile, entry, key, value))
if(!config_value_changed(settingsfile, entry, key, value))
{
g_key_file_free(settingsfile);
return SET_CONFIG_UNCHANGED;
......@@ -416,75 +408,102 @@ set_config_result_t set_mode_setting(const char *mode)
/* Builds the string used for hidden modes, when hide set to one builds the
new string of hidden modes when adding one, otherwise it will remove one */
static const char * make_hidden_modes_string(const char *hidden, int hide)
static char * make_hidden_modes_string(const char *mode_name, int hide)
{
GString *modelist_str;
char *hidden_modes_list;
gchar **hidden_mode_split;
char *hidden_new = 0;
char *hidden_old = 0;
gchar **hidden_arr = 0;
GString *hidden_tmp = 0;
int i;
hidden_modes_list = get_hidden_modes();
if(hidden_modes_list)
/* Get current comma separated list of hidden modes */
hidden_old = get_hidden_modes();
if(!hidden_old)
{
hidden_mode_split = g_strsplit(hidden_modes_list, ",", 0);
}
else
{
/* no hidden modes yet. So just return the original string */
return hidden;
hidden_old = g_strdup("");
}
modelist_str = g_string_new(NULL);
hidden_arr = g_strsplit(hidden_old, ",", 0);
hidden_tmp = g_string_new(NULL);
for(i = 0; hidden_mode_split[i] != NULL; i++)
for(i = 0; hidden_arr[i] != NULL; i++)
{
if(strlen(hidden_mode_split[i]) == 0)
continue;
if(!strcmp(hidden_mode_split[i], hidden))
{
/* if hiding a mode that is already hidden do nothing */
if(hide)
return(NULL);
if(!hide)
continue;
}
if(strlen(modelist_str->str) != 0)
modelist_str = g_string_append(modelist_str, ",");
modelist_str = g_string_append(modelist_str, hidden_mode_split[i]);
if(strlen(hidden_arr[i]) == 0)
{
/* Skip any empty strings */
continue;
}
if(!strcmp(hidden_arr[i], mode_name))
{
/* When unhiding, just skip all matching entries */
if(!hide)
continue;
/* When hiding, keep the 1st match and ignore the rest */
hide = 0;
}
if(hidden_tmp->len > 0)
hidden_tmp = g_string_append(hidden_tmp, ",");
hidden_tmp = g_string_append(hidden_tmp, hidden_arr[i]);
}
if(hide)
{
if(strlen(modelist_str->str) != 0)
modelist_str = g_string_append(modelist_str, ",");
modelist_str = g_string_append(modelist_str, hidden);
/* Adding a hidden mode and no matching entry was found */
if(hidden_tmp->len > 0)
hidden_tmp = g_string_append(hidden_tmp, ",");
hidden_tmp = g_string_append(hidden_tmp, mode_name);
}
g_strfreev(hidden_mode_split);
return(g_string_free(modelist_str, FALSE));
hidden_new = g_string_free(hidden_tmp, FALSE), hidden_tmp = 0;
g_strfreev(hidden_arr), hidden_arr = 0;
g_free(hidden_old), hidden_old = 0;
return hidden_new;
}
set_config_result_t set_hide_mode_setting(const char *mode)
{
set_config_result_t ret;
set_config_result_t ret = SET_CONFIG_UNCHANGED;
char *hidden_modes = make_hidden_modes_string(mode, 1);
if( hidden_modes ) {
ret = set_config_setting(MODE_SETTING_ENTRY, MODE_HIDE_KEY, hidden_modes);
}
ret = set_config_setting(MODE_SETTING_ENTRY, MODE_HIDE_KEY, make_hidden_modes_string(mode, 1));
if(ret == SET_CONFIG_UPDATED) {
send_hidden_modes_signal();
send_supported_modes_signal();
}
g_free(hidden_modes);
return(ret);
}
set_config_result_t set_unhide_mode_setting(const char *mode)
{
set_config_result_t ret;
set_config_result_t ret = SET_CONFIG_UNCHANGED;
char *hidden_modes = make_hidden_modes_string(mode, 0);
if( hidden_modes ) {
ret = set_config_setting(MODE_SETTING_ENTRY, MODE_HIDE_KEY, hidden_modes);
}
ret = set_config_setting(MODE_SETTING_ENTRY, MODE_HIDE_KEY, make_hidden_modes_string(mode, 0));
if(ret == SET_CONFIG_UPDATED) {
send_hidden_modes_signal();
send_supported_modes_signal();
}
g_free(hidden_modes);
return(ret);
}
......@@ -510,7 +529,7 @@ set_config_result_t set_network_setting(const char *config, const char *setting)
set_config_result_t ret = SET_CONFIG_ERROR;
if (test)
{
if(config_value_changed(settingsfile, NETWORK_ENTRY, config, setting))
if(!config_value_changed(settingsfile, NETWORK_ENTRY, config, setting))
{
g_key_file_free(settingsfile);
return SET_CONFIG_UNCHANGED;
......
......@@ -59,9 +59,9 @@
#define ANDROID_PRODUCT_ID_KEY "idProduct"
#define MODE_HIDE_KEY "hide"
const char * find_mounts(void);
char * find_mounts(void);
int find_sync(void);
const char * find_alt_mount(void);
char * find_alt_mount(void);
char * find_udev_path(void);
char * find_udev_subsystem(void);
......
......@@ -248,10 +248,10 @@ error_reply:
{
char *config = get_hidden_modes();
if(!config)
config = strdup("");
config = g_strdup("");
if((reply = dbus_message_new_method_return(msg)))
dbus_message_append_args (reply, DBUS_TYPE_STRING, &config, DBUS_TYPE_INVALID);
free(config);
g_free(config);
}
else if(!strcmp(member, USB_MODE_NETWORK_SET))
{
......@@ -294,7 +294,7 @@ error_reply:
{
if((reply = dbus_message_new_method_return(msg)))
dbus_message_append_args (reply, DBUS_TYPE_STRING, &config, DBUS_TYPE_STRING, &setting, DBUS_TYPE_INVALID);
free((void *)setting);
free(setting);
}
else
reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, config);
......
......@@ -43,7 +43,62 @@
#include "usb_moded-android.h"
static void report_mass_storage_blocker(const char *mountpoint, int try);
guint delayed_network = 0;
static guint delayed_network = 0;
#if LOG_ENABLE_DEBUG
static char *strip(char *str)
{
unsigned char *src = (unsigned char *)str;
unsigned char *dst = (unsigned char *)str;
while( *src > 0 && *src <= 32 ) ++src;
for( ;; )
{
while( *src > 32 ) *dst++ = *src++;
while( *src > 0 && *src <= 32 ) ++src;
if( *src == 0 ) break;
*dst++ = ' ';
}
*dst = 0;
return str;
}
static char *read_from_file(const char *path, size_t maxsize)
{
int fd = -1;
ssize_t done = 0;
char *data = 0;
char *text = 0;
if((fd = open(path, O_RDONLY)) == -1)
{
/* Silently ignore things that could result
* from missing / read-only files */
if( errno != ENOENT && errno != EACCES )
log_warning("%s: open: %m", path);
goto cleanup;
}
if( !(data = malloc(maxsize + 1)) )
goto cleanup;
if((done = read(fd, data, maxsize)) == -1)
{
log_warning("%s: read: %m", path);
goto cleanup;
}
text = realloc(data, done + 1), data = 0;
text[done] = 0;
strip(text);
cleanup:
free(data);
if(fd != -1) close(fd);
return text;
}
#endif /* LOG_ENABLE_DEBUG */
int write_to_file(const char *path, const char *text)
{
......@@ -56,14 +111,21 @@ int write_to_file(const char *path, const char *text)
if(!text || !path)
return err;
#if LOG_ENABLE_DEBUG
if(log_level >= LOG_DEBUG)
{
char *prev = read_from_file(path, 0x1000);
log_debug("WRITE '%s' : '%s' --> '%s'", path, prev ?: "???", text);
free(prev);
}
#endif
todo = strlen(text);
/* no O_CREAT -> writes only to already existing files */
if( (fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY))) == -1 )
{
/* gcc -pedantic does not like "%m"
log_warning("open(%s): %m", path); */
log_warning("open(%s): %s", path, strerror(errno));
log_warning("open(%s): %m", path);
goto cleanup;
}
......@@ -72,7 +134,7 @@ int write_to_file(const char *path, const char *text)
ssize_t n = TEMP_FAILURE_RETRY(write(fd, text, todo));
if( n < 0 )
{
log_warning("write(%s): %s", path, strerror(errno));
log_warning("write(%s): %m", path);
goto cleanup;
}
todo -= n;
......@@ -90,6 +152,7 @@ cleanup:
static gboolean network_retry(gpointer data)
{
delayed_network = 0;
usb_network_up(data);
return(FALSE);
}
......@@ -98,7 +161,7 @@ static int set_mass_storage_mode(struct mode_list_elem *data)
{
gchar *command;
char command2[256], *real_path = NULL, *mountpath;
const char *mount;
char *mount;
gchar **mounts;
int ret = 0, i = 0, mountpoints = 0, fua = 0, try = 0;
......@@ -199,7 +262,7 @@ umount: command = g_strconcat("mount | grep ", mountpath, NULL);
}
}
g_strfreev(mounts);
g_free((gpointer *)mount);
g_free(mount);
if(real_path)
free(real_path);
}
......@@ -216,7 +279,7 @@ static int unset_mass_storage_mode(struct mode_list_elem *data)
{
gchar *command;
char command2[256], *real_path = NULL, *mountpath;
const char *mount;
char *mount;
gchar **mounts;
int ret = 1, i = 0;
......@@ -247,6 +310,7 @@ static int unset_mass_storage_mode(struct mode_list_elem *data)
log_err("Mounting %s failed\n", mount);
if(ret)
{
g_free(mount);
mount = find_alt_mount();
if(mount)
{
......@@ -277,7 +341,7 @@ static int unset_mass_storage_mode(struct mode_list_elem *data)
}
}
g_strfreev(mounts);
g_free((gpointer *)mount);