diff mbox

[LEDE-DEV] Mountd: Add sysupgrade functionality.

Message ID 20160704165638.18177-1-zaolin@das-labor.org
State Changes Requested
Headers show

Commit Message

Philipp Deppenwiese July 4, 2016, 4:56 p.m. UTC
Extend the mountd with the ability to apply sysupgrades from mounted devices.
Upgrade files are identified by filename and executed through the
commandline sysupgrade utility of LEDE.

Option List:

config mountd 'sysupgrade'
	option  check_filename  firmware.bin (required)
	option  save_config     1	(optional, default disabled)
	option  delay           10	(optional, default disabled)
	option	preserve_part	1	(optional, default disabled)
	option	enabled		1

The so called "filesearch" is done only in the root of a mounted
devices.

Signed-off-by: Philipp Deppenwiese <zaolin@das-labor.org>
---
 CMakeLists.txt    |   2 +-
 autofs.c          |  19 ++++++-
 include/upgrade.h |   6 ++
 mount.c           |   2 +
 upgrade.c         | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 189 insertions(+), 2 deletions(-)
 create mode 100644 include/upgrade.h
 create mode 100644 upgrade.c

Comments

John Crispin July 6, 2016, 5:58 a.m. UTC | #1
Hi Philipp

the thing that i am worried about is this process

1) plug a stick
2) mountd detects the upgrade file
3) mountd triggers sysupgrade
4) system reboots
5) goto 1)

there is no way to know at what point between 3 and 4 we need to unplug
the usb stick

	John

On 04/07/2016 18:56, Philipp Deppenwiese wrote:
> Extend the mountd with the ability to apply sysupgrades from mounted devices.
> Upgrade files are identified by filename and executed through the
> commandline sysupgrade utility of LEDE.
> 
> Option List:
> 
> config mountd 'sysupgrade'
> 	option  check_filename  firmware.bin (required)
> 	option  save_config     1	(optional, default disabled)
> 	option  delay           10	(optional, default disabled)
> 	option	preserve_part	1	(optional, default disabled)
> 	option	enabled		1
> 
> The so called "filesearch" is done only in the root of a mounted
> devices.
> 
> Signed-off-by: Philipp Deppenwiese <zaolin@das-labor.org>
> ---
>  CMakeLists.txt    |   2 +-
>  autofs.c          |  19 ++++++-
>  include/upgrade.h |   6 ++
>  mount.c           |   2 +
>  upgrade.c         | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 189 insertions(+), 2 deletions(-)
>  create mode 100644 include/upgrade.h
>  create mode 100644 upgrade.c
> 
> diff --git a/CMakeLists.txt b/CMakeLists.txt
> index 2e712cd..ed3602e 100644
> --- a/CMakeLists.txt
> +++ b/CMakeLists.txt
> @@ -5,7 +5,7 @@ ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
>  
>  SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
>  
> -ADD_EXECUTABLE(mountd main.c log.c sys.c autofs.c mount.c timer.c signal.c ucix.c led.c fs.c ucix.c)
> +ADD_EXECUTABLE(mountd main.c log.c sys.c autofs.c mount.c timer.c signal.c ucix.c led.c fs.c ucix.c upgrade.c)
>  TARGET_LINK_LIBRARIES(mountd uci ubox)
>  
>  INSTALL(TARGETS mountd
> diff --git a/autofs.c b/autofs.c
> index 4ad782d..79a3e97 100644
> --- a/autofs.c
> +++ b/autofs.c
> @@ -37,6 +37,13 @@ dev_t dev;
>  time_t uci_timeout;
>  char uci_path[32];
>  
> +// Sysupgrade uci options
> +char uci_upgrade_filename[255];
> +int uci_upgrade_backup;
> +int uci_upgrade_delay;
> +int uci_upgrade_enabled;
> +int uci_upgrade_preserve_partition;
> +
>  static void umount_autofs(void)
>  {
>  	system_printf("umount %s 2> /dev/null", "/tmp/run/mountd/");
> @@ -186,18 +193,28 @@ static void autofs_cleanup_handler(void)
>  static void autofs_init(void)
>  {
>  	int kproto_version;
> -	char *p;
> +	char *p, *filename = NULL;
>  	struct uci_context *ctx;
>  	signal_init(autofs_cleanup_handler);
>  	ctx = ucix_init("mountd");
>  	uci_timeout = ucix_get_option_int(ctx, "mountd", "mountd", "timeout", 60);
>  	p = ucix_get_option(ctx, "mountd", "mountd", "path");
> +	filename = ucix_get_option(ctx, "mountd", "sysupgrade", "check_filename");
> +	uci_upgrade_backup = ucix_get_option_int(ctx, "mountd", "sysupgrade", "save_config", 0);
> +	uci_upgrade_delay = ucix_get_option_int(ctx, "mountd", "sysupgrade", "delay", 0);
> +	uci_upgrade_enabled = ucix_get_option_int(ctx, "mountd", "sysupgrade", "enabled", 0);
> +	uci_upgrade_preserve_partition = ucix_get_option_int(ctx, "mountd", "sysupgrade", "preserve_part", 0);
>  	ucix_cleanup(ctx);
>  	if(p)
>  		snprintf(uci_path, 31, "%s", p);
>  	else
>  		snprintf(uci_path, 31, "/tmp/mounts/");
>  	uci_path[31] = '\0';
> +
> +	if(filename) {
> +		snprintf(uci_upgrade_filename, 255, "%s", filename);
> +	}
> +
>  	mkdir("/tmp/run/", 0555);
>  	mkdir("/tmp/mounts", 0555);
>  	system_printf("rm -rf %s*", uci_path);
> diff --git a/include/upgrade.h b/include/upgrade.h
> new file mode 100644
> index 0000000..6b5ace9
> --- /dev/null
> +++ b/include/upgrade.h
> @@ -0,0 +1,6 @@
> +#ifndef _UPGRADE_H__
> +#define _UPGRADE_H__
> +
> +int sysupgrade_scan(const char *mount_path);
> +
> +#endif
> diff --git a/mount.c b/mount.c
> index 7cbb8ff..0bec25e 100644
> --- a/mount.c
> +++ b/mount.c
> @@ -28,6 +28,7 @@
>  #include "include/ucix.h"
>  #include "include/fs.h"
>  #include "include/mount.h"
> +#include "include/upgrade.h"
>  
>  int mount_new(char *path, char *dev);
>  
> @@ -300,6 +301,7 @@ int mount_new(char *path, char *dev)
>  	{
>  		mount->mounted = 1;
>  		mount_dump_uci_state();
> +		sysupgrade_scan(tmp);
>  	} else return -1;
>  	return 0;
>  }
> diff --git a/upgrade.c b/upgrade.c
> new file mode 100644
> index 0000000..6992e65
> --- /dev/null
> +++ b/upgrade.c
> @@ -0,0 +1,162 @@
> +/*
> + *   This program is free software; you can redistribute it and/or modify
> + *   it under the terms of the GNU General Public License as published by
> + *   the Free Software Foundation; either version 2 of the License, or
> + *   (at your option) any later version.
> + *
> + *   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.
> + *
> + *   Provided by 9elements GmbH
> + *   Copyright (C) 2016 Philipp Deppenwiese <philipp.deppenwiese@9elements.com>
> + */
> +
> +#include <errno.h>
> +#include <linux/limits.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdarg.h>
> +#include <string.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <sys/wait.h>
> +#include <unistd.h>
> +
> +#include "include/upgrade.h"
> +#include "include/log.h"
> +
> +#define SYSUPGRADE_CMD "/sbin/sysupgrade"
> +
> +extern char uci_upgrade_filename[255];
> +extern int uci_upgrade_backup;
> +extern int uci_upgrade_delay;
> +extern int uci_upgrade_enabled;
> +extern int uci_upgrade_preserve_partition;
> +
> +static unsigned option_index = 0;
> +static char **parameters = NULL;
> +
> +static int execute_sysupgrade()
> +{
> +	pid_t pid;
> +	int status;
> +	char * const envp[] = { NULL };
> +
> +	if (!parameters) {
> +		return 1;
> +	}
> +
> +	if ((pid = fork()) < 0) {
> +		perror("Can't fork child process for command invocation!");
> +		return 1;
> +	} else if (pid == 0) {
> +		if (execve(SYSUPGRADE_CMD, parameters, envp) < 0) {
> +			log_printf("Error was reported on sysupgrade: %s\n", strerror(errno));
> +			return 1;
> +		}
> +	} else {
> +		waitpid(pid, &status, 0);
> +	}
> +	return 0;
> +}
> +
> +static int find_upgrade_filepath(const char *mount_path, char *update_file)
> +{
> +	struct stat file;
> +
> +	if (snprintf(update_file, PATH_MAX, "%s/%s", mount_path,
> +		     uci_upgrade_filename) < 0) {
> +		perror(NULL);
> +		return 1;
> +	}
> +
> +	if (stat(update_file, &file) < 0) {
> +		log_printf("No upgrade file found for device path: %s! \n", mount_path);
> +		return 1;
> +	}
> +	return 0;
> +}
> +
> +static void add_sysupgrade_option(const char *parameter, ...)
> +{
> +	va_list arguments;
> +	char *option = NULL;
> +
> +	parameters = realloc(parameters, ++option_index * sizeof(char*));
> +
> +	if (parameter) {
> +		option = (char*)calloc(PATH_MAX, sizeof(char));
> +
> +		va_start(arguments, parameter);
> +		vsnprintf(option, PATH_MAX, parameter, arguments);
> +		va_end(arguments);
> +	}
> +
> +	parameters[option_index - 1] = option;
> +}
> +
> +static void free_all()
> +{
> +	unsigned i;
> +
> +	if (!parameters) {
> +		return;
> +	}
> +
> +	for (i = 0; i < option_index; i++) {
> +		if (parameters[i]) {
> +			free(parameters[i]);
> +		}
> +	}
> +
> +	free(parameters);
> +	parameters = NULL;
> +	option_index = 0;
> +}
> +
> +int sysupgrade_scan(const char *mount_path)
> +{
> +	char update_file[PATH_MAX];
> +
> +	if (!uci_upgrade_enabled) {
> +		log_printf("Sysupgrade via usb device is disabled..\n");
> +		return 0;
> +	}
> +
> +	if (find_upgrade_filepath(mount_path, update_file)) {
> +		log_printf("No upgrade file could be found..\n");
> +		return 0;
> +	}
> +
> +	log_printf("Starting sysupgrade routine..\n");
> +
> +	add_sysupgrade_option("%s", SYSUPGRADE_CMD);
> +
> +	if (uci_upgrade_delay) {
> +		add_sysupgrade_option("-d %i", uci_upgrade_delay);
> +	}
> +
> +	if (uci_upgrade_backup) {
> +		add_sysupgrade_option("-c");
> +	} else {
> +		add_sysupgrade_option("-n");
> +	}
> +
> +	if (uci_upgrade_preserve_partition) {
> +		add_sysupgrade_option("-p");
> +	}
> +
> +	add_sysupgrade_option("%s", update_file);
> +	add_sysupgrade_option(NULL);
> +
> +	if (execute_sysupgrade()) {
> +		free_all();
> +		log_printf("Try to start sysupgrade but it failed! \n");
> +		return 1;
> +	}
> +
> +	free_all();
> +	return 0;
> +}
>
Bruno Randolf July 6, 2016, 10:18 a.m. UTC | #2
On 06/07/16 06:58, John Crispin wrote:
> Hi Philipp
> 
> the thing that i am worried about is this process
> 
> 1) plug a stick
> 2) mountd detects the upgrade file
> 3) mountd triggers sysupgrade
> 4) system reboots
> 5) goto 1)
> 
> there is no way to know at what point between 3 and 4 we need to unplug
> the usb stick

One possible solution is to write a file, something like
".sysupgrade-applied" or remove the upgrade file before actually doing
the upgrade, or during first boot.

bruno

> 	John
> 
> On 04/07/2016 18:56, Philipp Deppenwiese wrote:
>> Extend the mountd with the ability to apply sysupgrades from mounted devices.
>> Upgrade files are identified by filename and executed through the
>> commandline sysupgrade utility of LEDE.
>>
>> Option List:
>>
>> config mountd 'sysupgrade'
>> 	option  check_filename  firmware.bin (required)
>> 	option  save_config     1	(optional, default disabled)
>> 	option  delay           10	(optional, default disabled)
>> 	option	preserve_part	1	(optional, default disabled)
>> 	option	enabled		1
>>
>> The so called "filesearch" is done only in the root of a mounted
>> devices.
>>
>> Signed-off-by: Philipp Deppenwiese <zaolin@das-labor.org>
>> ---
>>  CMakeLists.txt    |   2 +-
>>  autofs.c          |  19 ++++++-
>>  include/upgrade.h |   6 ++
>>  mount.c           |   2 +
>>  upgrade.c         | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  5 files changed, 189 insertions(+), 2 deletions(-)
>>  create mode 100644 include/upgrade.h
>>  create mode 100644 upgrade.c
>>
>> diff --git a/CMakeLists.txt b/CMakeLists.txt
>> index 2e712cd..ed3602e 100644
>> --- a/CMakeLists.txt
>> +++ b/CMakeLists.txt
>> @@ -5,7 +5,7 @@ ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
>>  
>>  SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
>>  
>> -ADD_EXECUTABLE(mountd main.c log.c sys.c autofs.c mount.c timer.c signal.c ucix.c led.c fs.c ucix.c)
>> +ADD_EXECUTABLE(mountd main.c log.c sys.c autofs.c mount.c timer.c signal.c ucix.c led.c fs.c ucix.c upgrade.c)
>>  TARGET_LINK_LIBRARIES(mountd uci ubox)
>>  
>>  INSTALL(TARGETS mountd
>> diff --git a/autofs.c b/autofs.c
>> index 4ad782d..79a3e97 100644
>> --- a/autofs.c
>> +++ b/autofs.c
>> @@ -37,6 +37,13 @@ dev_t dev;
>>  time_t uci_timeout;
>>  char uci_path[32];
>>  
>> +// Sysupgrade uci options
>> +char uci_upgrade_filename[255];
>> +int uci_upgrade_backup;
>> +int uci_upgrade_delay;
>> +int uci_upgrade_enabled;
>> +int uci_upgrade_preserve_partition;
>> +
>>  static void umount_autofs(void)
>>  {
>>  	system_printf("umount %s 2> /dev/null", "/tmp/run/mountd/");
>> @@ -186,18 +193,28 @@ static void autofs_cleanup_handler(void)
>>  static void autofs_init(void)
>>  {
>>  	int kproto_version;
>> -	char *p;
>> +	char *p, *filename = NULL;
>>  	struct uci_context *ctx;
>>  	signal_init(autofs_cleanup_handler);
>>  	ctx = ucix_init("mountd");
>>  	uci_timeout = ucix_get_option_int(ctx, "mountd", "mountd", "timeout", 60);
>>  	p = ucix_get_option(ctx, "mountd", "mountd", "path");
>> +	filename = ucix_get_option(ctx, "mountd", "sysupgrade", "check_filename");
>> +	uci_upgrade_backup = ucix_get_option_int(ctx, "mountd", "sysupgrade", "save_config", 0);
>> +	uci_upgrade_delay = ucix_get_option_int(ctx, "mountd", "sysupgrade", "delay", 0);
>> +	uci_upgrade_enabled = ucix_get_option_int(ctx, "mountd", "sysupgrade", "enabled", 0);
>> +	uci_upgrade_preserve_partition = ucix_get_option_int(ctx, "mountd", "sysupgrade", "preserve_part", 0);
>>  	ucix_cleanup(ctx);
>>  	if(p)
>>  		snprintf(uci_path, 31, "%s", p);
>>  	else
>>  		snprintf(uci_path, 31, "/tmp/mounts/");
>>  	uci_path[31] = '\0';
>> +
>> +	if(filename) {
>> +		snprintf(uci_upgrade_filename, 255, "%s", filename);
>> +	}
>> +
>>  	mkdir("/tmp/run/", 0555);
>>  	mkdir("/tmp/mounts", 0555);
>>  	system_printf("rm -rf %s*", uci_path);
>> diff --git a/include/upgrade.h b/include/upgrade.h
>> new file mode 100644
>> index 0000000..6b5ace9
>> --- /dev/null
>> +++ b/include/upgrade.h
>> @@ -0,0 +1,6 @@
>> +#ifndef _UPGRADE_H__
>> +#define _UPGRADE_H__
>> +
>> +int sysupgrade_scan(const char *mount_path);
>> +
>> +#endif
>> diff --git a/mount.c b/mount.c
>> index 7cbb8ff..0bec25e 100644
>> --- a/mount.c
>> +++ b/mount.c
>> @@ -28,6 +28,7 @@
>>  #include "include/ucix.h"
>>  #include "include/fs.h"
>>  #include "include/mount.h"
>> +#include "include/upgrade.h"
>>  
>>  int mount_new(char *path, char *dev);
>>  
>> @@ -300,6 +301,7 @@ int mount_new(char *path, char *dev)
>>  	{
>>  		mount->mounted = 1;
>>  		mount_dump_uci_state();
>> +		sysupgrade_scan(tmp);
>>  	} else return -1;
>>  	return 0;
>>  }
>> diff --git a/upgrade.c b/upgrade.c
>> new file mode 100644
>> index 0000000..6992e65
>> --- /dev/null
>> +++ b/upgrade.c
>> @@ -0,0 +1,162 @@
>> +/*
>> + *   This program is free software; you can redistribute it and/or modify
>> + *   it under the terms of the GNU General Public License as published by
>> + *   the Free Software Foundation; either version 2 of the License, or
>> + *   (at your option) any later version.
>> + *
>> + *   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.
>> + *
>> + *   Provided by 9elements GmbH
>> + *   Copyright (C) 2016 Philipp Deppenwiese <philipp.deppenwiese@9elements.com>
>> + */
>> +
>> +#include <errno.h>
>> +#include <linux/limits.h>
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <stdarg.h>
>> +#include <string.h>
>> +#include <sys/stat.h>
>> +#include <sys/types.h>
>> +#include <sys/wait.h>
>> +#include <unistd.h>
>> +
>> +#include "include/upgrade.h"
>> +#include "include/log.h"
>> +
>> +#define SYSUPGRADE_CMD "/sbin/sysupgrade"
>> +
>> +extern char uci_upgrade_filename[255];
>> +extern int uci_upgrade_backup;
>> +extern int uci_upgrade_delay;
>> +extern int uci_upgrade_enabled;
>> +extern int uci_upgrade_preserve_partition;
>> +
>> +static unsigned option_index = 0;
>> +static char **parameters = NULL;
>> +
>> +static int execute_sysupgrade()
>> +{
>> +	pid_t pid;
>> +	int status;
>> +	char * const envp[] = { NULL };
>> +
>> +	if (!parameters) {
>> +		return 1;
>> +	}
>> +
>> +	if ((pid = fork()) < 0) {
>> +		perror("Can't fork child process for command invocation!");
>> +		return 1;
>> +	} else if (pid == 0) {
>> +		if (execve(SYSUPGRADE_CMD, parameters, envp) < 0) {
>> +			log_printf("Error was reported on sysupgrade: %s\n", strerror(errno));
>> +			return 1;
>> +		}
>> +	} else {
>> +		waitpid(pid, &status, 0);
>> +	}
>> +	return 0;
>> +}
>> +
>> +static int find_upgrade_filepath(const char *mount_path, char *update_file)
>> +{
>> +	struct stat file;
>> +
>> +	if (snprintf(update_file, PATH_MAX, "%s/%s", mount_path,
>> +		     uci_upgrade_filename) < 0) {
>> +		perror(NULL);
>> +		return 1;
>> +	}
>> +
>> +	if (stat(update_file, &file) < 0) {
>> +		log_printf("No upgrade file found for device path: %s! \n", mount_path);
>> +		return 1;
>> +	}
>> +	return 0;
>> +}
>> +
>> +static void add_sysupgrade_option(const char *parameter, ...)
>> +{
>> +	va_list arguments;
>> +	char *option = NULL;
>> +
>> +	parameters = realloc(parameters, ++option_index * sizeof(char*));
>> +
>> +	if (parameter) {
>> +		option = (char*)calloc(PATH_MAX, sizeof(char));
>> +
>> +		va_start(arguments, parameter);
>> +		vsnprintf(option, PATH_MAX, parameter, arguments);
>> +		va_end(arguments);
>> +	}
>> +
>> +	parameters[option_index - 1] = option;
>> +}
>> +
>> +static void free_all()
>> +{
>> +	unsigned i;
>> +
>> +	if (!parameters) {
>> +		return;
>> +	}
>> +
>> +	for (i = 0; i < option_index; i++) {
>> +		if (parameters[i]) {
>> +			free(parameters[i]);
>> +		}
>> +	}
>> +
>> +	free(parameters);
>> +	parameters = NULL;
>> +	option_index = 0;
>> +}
>> +
>> +int sysupgrade_scan(const char *mount_path)
>> +{
>> +	char update_file[PATH_MAX];
>> +
>> +	if (!uci_upgrade_enabled) {
>> +		log_printf("Sysupgrade via usb device is disabled..\n");
>> +		return 0;
>> +	}
>> +
>> +	if (find_upgrade_filepath(mount_path, update_file)) {
>> +		log_printf("No upgrade file could be found..\n");
>> +		return 0;
>> +	}
>> +
>> +	log_printf("Starting sysupgrade routine..\n");
>> +
>> +	add_sysupgrade_option("%s", SYSUPGRADE_CMD);
>> +
>> +	if (uci_upgrade_delay) {
>> +		add_sysupgrade_option("-d %i", uci_upgrade_delay);
>> +	}
>> +
>> +	if (uci_upgrade_backup) {
>> +		add_sysupgrade_option("-c");
>> +	} else {
>> +		add_sysupgrade_option("-n");
>> +	}
>> +
>> +	if (uci_upgrade_preserve_partition) {
>> +		add_sysupgrade_option("-p");
>> +	}
>> +
>> +	add_sysupgrade_option("%s", update_file);
>> +	add_sysupgrade_option(NULL);
>> +
>> +	if (execute_sysupgrade()) {
>> +		free_all();
>> +		log_printf("Try to start sysupgrade but it failed! \n");
>> +		return 1;
>> +	}
>> +
>> +	free_all();
>> +	return 0;
>> +}
>>
> 
> _______________________________________________
> Lede-dev mailing list
> Lede-dev@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/lede-dev
>
Rujun Wang July 6, 2016, 10:34 a.m. UTC | #3
Another possible solution is to record the HASH of system image after
system upgrade is done. Next time when we try to do the system
upgrade, we can compare the recorded HASH value and the compute value
from system upgrade file?

2016-07-06 18:18 GMT+08:00 Bruno Randolf <br1@einfach.org>:
> On 06/07/16 06:58, John Crispin wrote:
>> Hi Philipp
>>
>> the thing that i am worried about is this process
>>
>> 1) plug a stick
>> 2) mountd detects the upgrade file
>> 3) mountd triggers sysupgrade
>> 4) system reboots
>> 5) goto 1)
>>
>> there is no way to know at what point between 3 and 4 we need to unplug
>> the usb stick
>
> One possible solution is to write a file, something like
> ".sysupgrade-applied" or remove the upgrade file before actually doing
> the upgrade, or during first boot.
>
> bruno
>
>>       John
>>
>> On 04/07/2016 18:56, Philipp Deppenwiese wrote:
>>> Extend the mountd with the ability to apply sysupgrades from mounted devices.
>>> Upgrade files are identified by filename and executed through the
>>> commandline sysupgrade utility of LEDE.
>>>
>>> Option List:
>>>
>>> config mountd 'sysupgrade'
>>>      option  check_filename  firmware.bin (required)
>>>      option  save_config     1       (optional, default disabled)
>>>      option  delay           10      (optional, default disabled)
>>>      option  preserve_part   1       (optional, default disabled)
>>>      option  enabled         1
>>>
>>> The so called "filesearch" is done only in the root of a mounted
>>> devices.
>>>
>>> Signed-off-by: Philipp Deppenwiese <zaolin@das-labor.org>
>>> ---
>>>  CMakeLists.txt    |   2 +-
>>>  autofs.c          |  19 ++++++-
>>>  include/upgrade.h |   6 ++
>>>  mount.c           |   2 +
>>>  upgrade.c         | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>  5 files changed, 189 insertions(+), 2 deletions(-)
>>>  create mode 100644 include/upgrade.h
>>>  create mode 100644 upgrade.c
>>>
>>> diff --git a/CMakeLists.txt b/CMakeLists.txt
>>> index 2e712cd..ed3602e 100644
>>> --- a/CMakeLists.txt
>>> +++ b/CMakeLists.txt
>>> @@ -5,7 +5,7 @@ ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
>>>
>>>  SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
>>>
>>> -ADD_EXECUTABLE(mountd main.c log.c sys.c autofs.c mount.c timer.c signal.c ucix.c led.c fs.c ucix.c)
>>> +ADD_EXECUTABLE(mountd main.c log.c sys.c autofs.c mount.c timer.c signal.c ucix.c led.c fs.c ucix.c upgrade.c)
>>>  TARGET_LINK_LIBRARIES(mountd uci ubox)
>>>
>>>  INSTALL(TARGETS mountd
>>> diff --git a/autofs.c b/autofs.c
>>> index 4ad782d..79a3e97 100644
>>> --- a/autofs.c
>>> +++ b/autofs.c
>>> @@ -37,6 +37,13 @@ dev_t dev;
>>>  time_t uci_timeout;
>>>  char uci_path[32];
>>>
>>> +// Sysupgrade uci options
>>> +char uci_upgrade_filename[255];
>>> +int uci_upgrade_backup;
>>> +int uci_upgrade_delay;
>>> +int uci_upgrade_enabled;
>>> +int uci_upgrade_preserve_partition;
>>> +
>>>  static void umount_autofs(void)
>>>  {
>>>      system_printf("umount %s 2> /dev/null", "/tmp/run/mountd/");
>>> @@ -186,18 +193,28 @@ static void autofs_cleanup_handler(void)
>>>  static void autofs_init(void)
>>>  {
>>>      int kproto_version;
>>> -    char *p;
>>> +    char *p, *filename = NULL;
>>>      struct uci_context *ctx;
>>>      signal_init(autofs_cleanup_handler);
>>>      ctx = ucix_init("mountd");
>>>      uci_timeout = ucix_get_option_int(ctx, "mountd", "mountd", "timeout", 60);
>>>      p = ucix_get_option(ctx, "mountd", "mountd", "path");
>>> +    filename = ucix_get_option(ctx, "mountd", "sysupgrade", "check_filename");
>>> +    uci_upgrade_backup = ucix_get_option_int(ctx, "mountd", "sysupgrade", "save_config", 0);
>>> +    uci_upgrade_delay = ucix_get_option_int(ctx, "mountd", "sysupgrade", "delay", 0);
>>> +    uci_upgrade_enabled = ucix_get_option_int(ctx, "mountd", "sysupgrade", "enabled", 0);
>>> +    uci_upgrade_preserve_partition = ucix_get_option_int(ctx, "mountd", "sysupgrade", "preserve_part", 0);
>>>      ucix_cleanup(ctx);
>>>      if(p)
>>>              snprintf(uci_path, 31, "%s", p);
>>>      else
>>>              snprintf(uci_path, 31, "/tmp/mounts/");
>>>      uci_path[31] = '\0';
>>> +
>>> +    if(filename) {
>>> +            snprintf(uci_upgrade_filename, 255, "%s", filename);
>>> +    }
>>> +
>>>      mkdir("/tmp/run/", 0555);
>>>      mkdir("/tmp/mounts", 0555);
>>>      system_printf("rm -rf %s*", uci_path);
>>> diff --git a/include/upgrade.h b/include/upgrade.h
>>> new file mode 100644
>>> index 0000000..6b5ace9
>>> --- /dev/null
>>> +++ b/include/upgrade.h
>>> @@ -0,0 +1,6 @@
>>> +#ifndef _UPGRADE_H__
>>> +#define _UPGRADE_H__
>>> +
>>> +int sysupgrade_scan(const char *mount_path);
>>> +
>>> +#endif
>>> diff --git a/mount.c b/mount.c
>>> index 7cbb8ff..0bec25e 100644
>>> --- a/mount.c
>>> +++ b/mount.c
>>> @@ -28,6 +28,7 @@
>>>  #include "include/ucix.h"
>>>  #include "include/fs.h"
>>>  #include "include/mount.h"
>>> +#include "include/upgrade.h"
>>>
>>>  int mount_new(char *path, char *dev);
>>>
>>> @@ -300,6 +301,7 @@ int mount_new(char *path, char *dev)
>>>      {
>>>              mount->mounted = 1;
>>>              mount_dump_uci_state();
>>> +            sysupgrade_scan(tmp);
>>>      } else return -1;
>>>      return 0;
>>>  }
>>> diff --git a/upgrade.c b/upgrade.c
>>> new file mode 100644
>>> index 0000000..6992e65
>>> --- /dev/null
>>> +++ b/upgrade.c
>>> @@ -0,0 +1,162 @@
>>> +/*
>>> + *   This program is free software; you can redistribute it and/or modify
>>> + *   it under the terms of the GNU General Public License as published by
>>> + *   the Free Software Foundation; either version 2 of the License, or
>>> + *   (at your option) any later version.
>>> + *
>>> + *   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.
>>> + *
>>> + *   Provided by 9elements GmbH
>>> + *   Copyright (C) 2016 Philipp Deppenwiese <philipp.deppenwiese@9elements.com>
>>> + */
>>> +
>>> +#include <errno.h>
>>> +#include <linux/limits.h>
>>> +#include <stdio.h>
>>> +#include <stdlib.h>
>>> +#include <stdarg.h>
>>> +#include <string.h>
>>> +#include <sys/stat.h>
>>> +#include <sys/types.h>
>>> +#include <sys/wait.h>
>>> +#include <unistd.h>
>>> +
>>> +#include "include/upgrade.h"
>>> +#include "include/log.h"
>>> +
>>> +#define SYSUPGRADE_CMD "/sbin/sysupgrade"
>>> +
>>> +extern char uci_upgrade_filename[255];
>>> +extern int uci_upgrade_backup;
>>> +extern int uci_upgrade_delay;
>>> +extern int uci_upgrade_enabled;
>>> +extern int uci_upgrade_preserve_partition;
>>> +
>>> +static unsigned option_index = 0;
>>> +static char **parameters = NULL;
>>> +
>>> +static int execute_sysupgrade()
>>> +{
>>> +    pid_t pid;
>>> +    int status;
>>> +    char * const envp[] = { NULL };
>>> +
>>> +    if (!parameters) {
>>> +            return 1;
>>> +    }
>>> +
>>> +    if ((pid = fork()) < 0) {
>>> +            perror("Can't fork child process for command invocation!");
>>> +            return 1;
>>> +    } else if (pid == 0) {
>>> +            if (execve(SYSUPGRADE_CMD, parameters, envp) < 0) {
>>> +                    log_printf("Error was reported on sysupgrade: %s\n", strerror(errno));
>>> +                    return 1;
>>> +            }
>>> +    } else {
>>> +            waitpid(pid, &status, 0);
>>> +    }
>>> +    return 0;
>>> +}
>>> +
>>> +static int find_upgrade_filepath(const char *mount_path, char *update_file)
>>> +{
>>> +    struct stat file;
>>> +
>>> +    if (snprintf(update_file, PATH_MAX, "%s/%s", mount_path,
>>> +                 uci_upgrade_filename) < 0) {
>>> +            perror(NULL);
>>> +            return 1;
>>> +    }
>>> +
>>> +    if (stat(update_file, &file) < 0) {
>>> +            log_printf("No upgrade file found for device path: %s! \n", mount_path);
>>> +            return 1;
>>> +    }
>>> +    return 0;
>>> +}
>>> +
>>> +static void add_sysupgrade_option(const char *parameter, ...)
>>> +{
>>> +    va_list arguments;
>>> +    char *option = NULL;
>>> +
>>> +    parameters = realloc(parameters, ++option_index * sizeof(char*));
>>> +
>>> +    if (parameter) {
>>> +            option = (char*)calloc(PATH_MAX, sizeof(char));
>>> +
>>> +            va_start(arguments, parameter);
>>> +            vsnprintf(option, PATH_MAX, parameter, arguments);
>>> +            va_end(arguments);
>>> +    }
>>> +
>>> +    parameters[option_index - 1] = option;
>>> +}
>>> +
>>> +static void free_all()
>>> +{
>>> +    unsigned i;
>>> +
>>> +    if (!parameters) {
>>> +            return;
>>> +    }
>>> +
>>> +    for (i = 0; i < option_index; i++) {
>>> +            if (parameters[i]) {
>>> +                    free(parameters[i]);
>>> +            }
>>> +    }
>>> +
>>> +    free(parameters);
>>> +    parameters = NULL;
>>> +    option_index = 0;
>>> +}
>>> +
>>> +int sysupgrade_scan(const char *mount_path)
>>> +{
>>> +    char update_file[PATH_MAX];
>>> +
>>> +    if (!uci_upgrade_enabled) {
>>> +            log_printf("Sysupgrade via usb device is disabled..\n");
>>> +            return 0;
>>> +    }
>>> +
>>> +    if (find_upgrade_filepath(mount_path, update_file)) {
>>> +            log_printf("No upgrade file could be found..\n");
>>> +            return 0;
>>> +    }
>>> +
>>> +    log_printf("Starting sysupgrade routine..\n");
>>> +
>>> +    add_sysupgrade_option("%s", SYSUPGRADE_CMD);
>>> +
>>> +    if (uci_upgrade_delay) {
>>> +            add_sysupgrade_option("-d %i", uci_upgrade_delay);
>>> +    }
>>> +
>>> +    if (uci_upgrade_backup) {
>>> +            add_sysupgrade_option("-c");
>>> +    } else {
>>> +            add_sysupgrade_option("-n");
>>> +    }
>>> +
>>> +    if (uci_upgrade_preserve_partition) {
>>> +            add_sysupgrade_option("-p");
>>> +    }
>>> +
>>> +    add_sysupgrade_option("%s", update_file);
>>> +    add_sysupgrade_option(NULL);
>>> +
>>> +    if (execute_sysupgrade()) {
>>> +            free_all();
>>> +            log_printf("Try to start sysupgrade but it failed! \n");
>>> +            return 1;
>>> +    }
>>> +
>>> +    free_all();
>>> +    return 0;
>>> +}
>>>
>>
>> _______________________________________________
>> Lede-dev mailing list
>> Lede-dev@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/lede-dev
>>
>
>
> _______________________________________________
> Lede-dev mailing list
> Lede-dev@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/lede-dev
olivier.hardouin@gmail.com July 7, 2016, 5:52 a.m. UTC | #4
Though I like the idea of being able to upgrade using a usb stick, I
wonder if mountd is the correct place to implement this.
Indeed mountd is sending events via hotplug, so some other subsystems
could listen to these ones and decide whether the upgrade should be
based on some file of an attached usb disk or some other place (in the
network).
I think mountd should only do what it is good at, meaning detecting
disk, finding partition, mounting filesystem and inform other
subsystems.


On Wed, Jul 6, 2016 at 12:34 PM, Wang Linetkux <chinawrj@gmail.com> wrote:
> Another possible solution is to record the HASH of system image after
> system upgrade is done. Next time when we try to do the system
> upgrade, we can compare the recorded HASH value and the compute value
> from system upgrade file?
>
> 2016-07-06 18:18 GMT+08:00 Bruno Randolf <br1@einfach.org>:
>> On 06/07/16 06:58, John Crispin wrote:
>>> Hi Philipp
>>>
>>> the thing that i am worried about is this process
>>>
>>> 1) plug a stick
>>> 2) mountd detects the upgrade file
>>> 3) mountd triggers sysupgrade
>>> 4) system reboots
>>> 5) goto 1)
>>>
>>> there is no way to know at what point between 3 and 4 we need to unplug
>>> the usb stick
>>
>> One possible solution is to write a file, something like
>> ".sysupgrade-applied" or remove the upgrade file before actually doing
>> the upgrade, or during first boot.
>>
>> bruno
>>
>>>       John
>>>
>>> On 04/07/2016 18:56, Philipp Deppenwiese wrote:
>>>> Extend the mountd with the ability to apply sysupgrades from mounted devices.
>>>> Upgrade files are identified by filename and executed through the
>>>> commandline sysupgrade utility of LEDE.
>>>>
>>>> Option List:
>>>>
>>>> config mountd 'sysupgrade'
>>>>      option  check_filename  firmware.bin (required)
>>>>      option  save_config     1       (optional, default disabled)
>>>>      option  delay           10      (optional, default disabled)
>>>>      option  preserve_part   1       (optional, default disabled)
>>>>      option  enabled         1
>>>>
>>>> The so called "filesearch" is done only in the root of a mounted
>>>> devices.
>>>>
>>>> Signed-off-by: Philipp Deppenwiese <zaolin@das-labor.org>
>>>> ---
>>>>  CMakeLists.txt    |   2 +-
>>>>  autofs.c          |  19 ++++++-
>>>>  include/upgrade.h |   6 ++
>>>>  mount.c           |   2 +
>>>>  upgrade.c         | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>>  5 files changed, 189 insertions(+), 2 deletions(-)
>>>>  create mode 100644 include/upgrade.h
>>>>  create mode 100644 upgrade.c
>>>>
>>>> diff --git a/CMakeLists.txt b/CMakeLists.txt
>>>> index 2e712cd..ed3602e 100644
>>>> --- a/CMakeLists.txt
>>>> +++ b/CMakeLists.txt
>>>> @@ -5,7 +5,7 @@ ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
>>>>
>>>>  SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
>>>>
>>>> -ADD_EXECUTABLE(mountd main.c log.c sys.c autofs.c mount.c timer.c signal.c ucix.c led.c fs.c ucix.c)
>>>> +ADD_EXECUTABLE(mountd main.c log.c sys.c autofs.c mount.c timer.c signal.c ucix.c led.c fs.c ucix.c upgrade.c)
>>>>  TARGET_LINK_LIBRARIES(mountd uci ubox)
>>>>
>>>>  INSTALL(TARGETS mountd
>>>> diff --git a/autofs.c b/autofs.c
>>>> index 4ad782d..79a3e97 100644
>>>> --- a/autofs.c
>>>> +++ b/autofs.c
>>>> @@ -37,6 +37,13 @@ dev_t dev;
>>>>  time_t uci_timeout;
>>>>  char uci_path[32];
>>>>
>>>> +// Sysupgrade uci options
>>>> +char uci_upgrade_filename[255];
>>>> +int uci_upgrade_backup;
>>>> +int uci_upgrade_delay;
>>>> +int uci_upgrade_enabled;
>>>> +int uci_upgrade_preserve_partition;
>>>> +
>>>>  static void umount_autofs(void)
>>>>  {
>>>>      system_printf("umount %s 2> /dev/null", "/tmp/run/mountd/");
>>>> @@ -186,18 +193,28 @@ static void autofs_cleanup_handler(void)
>>>>  static void autofs_init(void)
>>>>  {
>>>>      int kproto_version;
>>>> -    char *p;
>>>> +    char *p, *filename = NULL;
>>>>      struct uci_context *ctx;
>>>>      signal_init(autofs_cleanup_handler);
>>>>      ctx = ucix_init("mountd");
>>>>      uci_timeout = ucix_get_option_int(ctx, "mountd", "mountd", "timeout", 60);
>>>>      p = ucix_get_option(ctx, "mountd", "mountd", "path");
>>>> +    filename = ucix_get_option(ctx, "mountd", "sysupgrade", "check_filename");
>>>> +    uci_upgrade_backup = ucix_get_option_int(ctx, "mountd", "sysupgrade", "save_config", 0);
>>>> +    uci_upgrade_delay = ucix_get_option_int(ctx, "mountd", "sysupgrade", "delay", 0);
>>>> +    uci_upgrade_enabled = ucix_get_option_int(ctx, "mountd", "sysupgrade", "enabled", 0);
>>>> +    uci_upgrade_preserve_partition = ucix_get_option_int(ctx, "mountd", "sysupgrade", "preserve_part", 0);
>>>>      ucix_cleanup(ctx);
>>>>      if(p)
>>>>              snprintf(uci_path, 31, "%s", p);
>>>>      else
>>>>              snprintf(uci_path, 31, "/tmp/mounts/");
>>>>      uci_path[31] = '\0';
>>>> +
>>>> +    if(filename) {
>>>> +            snprintf(uci_upgrade_filename, 255, "%s", filename);
>>>> +    }
>>>> +
>>>>      mkdir("/tmp/run/", 0555);
>>>>      mkdir("/tmp/mounts", 0555);
>>>>      system_printf("rm -rf %s*", uci_path);
>>>> diff --git a/include/upgrade.h b/include/upgrade.h
>>>> new file mode 100644
>>>> index 0000000..6b5ace9
>>>> --- /dev/null
>>>> +++ b/include/upgrade.h
>>>> @@ -0,0 +1,6 @@
>>>> +#ifndef _UPGRADE_H__
>>>> +#define _UPGRADE_H__
>>>> +
>>>> +int sysupgrade_scan(const char *mount_path);
>>>> +
>>>> +#endif
>>>> diff --git a/mount.c b/mount.c
>>>> index 7cbb8ff..0bec25e 100644
>>>> --- a/mount.c
>>>> +++ b/mount.c
>>>> @@ -28,6 +28,7 @@
>>>>  #include "include/ucix.h"
>>>>  #include "include/fs.h"
>>>>  #include "include/mount.h"
>>>> +#include "include/upgrade.h"
>>>>
>>>>  int mount_new(char *path, char *dev);
>>>>
>>>> @@ -300,6 +301,7 @@ int mount_new(char *path, char *dev)
>>>>      {
>>>>              mount->mounted = 1;
>>>>              mount_dump_uci_state();
>>>> +            sysupgrade_scan(tmp);
>>>>      } else return -1;
>>>>      return 0;
>>>>  }
>>>> diff --git a/upgrade.c b/upgrade.c
>>>> new file mode 100644
>>>> index 0000000..6992e65
>>>> --- /dev/null
>>>> +++ b/upgrade.c
>>>> @@ -0,0 +1,162 @@
>>>> +/*
>>>> + *   This program is free software; you can redistribute it and/or modify
>>>> + *   it under the terms of the GNU General Public License as published by
>>>> + *   the Free Software Foundation; either version 2 of the License, or
>>>> + *   (at your option) any later version.
>>>> + *
>>>> + *   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.
>>>> + *
>>>> + *   Provided by 9elements GmbH
>>>> + *   Copyright (C) 2016 Philipp Deppenwiese <philipp.deppenwiese@9elements.com>
>>>> + */
>>>> +
>>>> +#include <errno.h>
>>>> +#include <linux/limits.h>
>>>> +#include <stdio.h>
>>>> +#include <stdlib.h>
>>>> +#include <stdarg.h>
>>>> +#include <string.h>
>>>> +#include <sys/stat.h>
>>>> +#include <sys/types.h>
>>>> +#include <sys/wait.h>
>>>> +#include <unistd.h>
>>>> +
>>>> +#include "include/upgrade.h"
>>>> +#include "include/log.h"
>>>> +
>>>> +#define SYSUPGRADE_CMD "/sbin/sysupgrade"
>>>> +
>>>> +extern char uci_upgrade_filename[255];
>>>> +extern int uci_upgrade_backup;
>>>> +extern int uci_upgrade_delay;
>>>> +extern int uci_upgrade_enabled;
>>>> +extern int uci_upgrade_preserve_partition;
>>>> +
>>>> +static unsigned option_index = 0;
>>>> +static char **parameters = NULL;
>>>> +
>>>> +static int execute_sysupgrade()
>>>> +{
>>>> +    pid_t pid;
>>>> +    int status;
>>>> +    char * const envp[] = { NULL };
>>>> +
>>>> +    if (!parameters) {
>>>> +            return 1;
>>>> +    }
>>>> +
>>>> +    if ((pid = fork()) < 0) {
>>>> +            perror("Can't fork child process for command invocation!");
>>>> +            return 1;
>>>> +    } else if (pid == 0) {
>>>> +            if (execve(SYSUPGRADE_CMD, parameters, envp) < 0) {
>>>> +                    log_printf("Error was reported on sysupgrade: %s\n", strerror(errno));
>>>> +                    return 1;
>>>> +            }
>>>> +    } else {
>>>> +            waitpid(pid, &status, 0);
>>>> +    }
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int find_upgrade_filepath(const char *mount_path, char *update_file)
>>>> +{
>>>> +    struct stat file;
>>>> +
>>>> +    if (snprintf(update_file, PATH_MAX, "%s/%s", mount_path,
>>>> +                 uci_upgrade_filename) < 0) {
>>>> +            perror(NULL);
>>>> +            return 1;
>>>> +    }
>>>> +
>>>> +    if (stat(update_file, &file) < 0) {
>>>> +            log_printf("No upgrade file found for device path: %s! \n", mount_path);
>>>> +            return 1;
>>>> +    }
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static void add_sysupgrade_option(const char *parameter, ...)
>>>> +{
>>>> +    va_list arguments;
>>>> +    char *option = NULL;
>>>> +
>>>> +    parameters = realloc(parameters, ++option_index * sizeof(char*));
>>>> +
>>>> +    if (parameter) {
>>>> +            option = (char*)calloc(PATH_MAX, sizeof(char));
>>>> +
>>>> +            va_start(arguments, parameter);
>>>> +            vsnprintf(option, PATH_MAX, parameter, arguments);
>>>> +            va_end(arguments);
>>>> +    }
>>>> +
>>>> +    parameters[option_index - 1] = option;
>>>> +}
>>>> +
>>>> +static void free_all()
>>>> +{
>>>> +    unsigned i;
>>>> +
>>>> +    if (!parameters) {
>>>> +            return;
>>>> +    }
>>>> +
>>>> +    for (i = 0; i < option_index; i++) {
>>>> +            if (parameters[i]) {
>>>> +                    free(parameters[i]);
>>>> +            }
>>>> +    }
>>>> +
>>>> +    free(parameters);
>>>> +    parameters = NULL;
>>>> +    option_index = 0;
>>>> +}
>>>> +
>>>> +int sysupgrade_scan(const char *mount_path)
>>>> +{
>>>> +    char update_file[PATH_MAX];
>>>> +
>>>> +    if (!uci_upgrade_enabled) {
>>>> +            log_printf("Sysupgrade via usb device is disabled..\n");
>>>> +            return 0;
>>>> +    }
>>>> +
>>>> +    if (find_upgrade_filepath(mount_path, update_file)) {
>>>> +            log_printf("No upgrade file could be found..\n");
>>>> +            return 0;
>>>> +    }
>>>> +
>>>> +    log_printf("Starting sysupgrade routine..\n");
>>>> +
>>>> +    add_sysupgrade_option("%s", SYSUPGRADE_CMD);
>>>> +
>>>> +    if (uci_upgrade_delay) {
>>>> +            add_sysupgrade_option("-d %i", uci_upgrade_delay);
>>>> +    }
>>>> +
>>>> +    if (uci_upgrade_backup) {
>>>> +            add_sysupgrade_option("-c");
>>>> +    } else {
>>>> +            add_sysupgrade_option("-n");
>>>> +    }
>>>> +
>>>> +    if (uci_upgrade_preserve_partition) {
>>>> +            add_sysupgrade_option("-p");
>>>> +    }
>>>> +
>>>> +    add_sysupgrade_option("%s", update_file);
>>>> +    add_sysupgrade_option(NULL);
>>>> +
>>>> +    if (execute_sysupgrade()) {
>>>> +            free_all();
>>>> +            log_printf("Try to start sysupgrade but it failed! \n");
>>>> +            return 1;
>>>> +    }
>>>> +
>>>> +    free_all();
>>>> +    return 0;
>>>> +}
>>>>
>>>
>>> _______________________________________________
>>> Lede-dev mailing list
>>> Lede-dev@lists.infradead.org
>>> http://lists.infradead.org/mailman/listinfo/lede-dev
>>>
>>
>>
>> _______________________________________________
>> Lede-dev mailing list
>> Lede-dev@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/lede-dev
>
> _______________________________________________
> Lede-dev mailing list
> Lede-dev@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/lede-dev
Philipp Deppenwiese July 7, 2016, 11:40 a.m. UTC | #5
Hey,

thanks for all your answers!

On 07/07/2016 07:52 AM, Olivier Hardouin wrote:
> Though I like the idea of being able to upgrade using a usb stick, I
> wonder if mountd is the correct place to implement this.
> Indeed mountd is sending events via hotplug, so some other subsystems
> could listen to these ones and decide whether the upgrade should be
> based on some file of an attached usb disk or some other place (in the
> network).
> I think mountd should only do what it is good at, meaning detecting
> disk, finding partition, mounting filesystem and inform other
> subsystems.
Yes, good point. But then we should rewrite/redesign the whole update
process
including the sysupgrade binary in my opinion. For example writing a
small library and a daemon
which does the updates for us. A daemon which executes a sysupgrade tool
doesn't
make more sense for me as the mountd solution.
>
> On Wed, Jul 6, 2016 at 12:34 PM, Wang Linetkux <chinawrj@gmail.com> wrote:
>> Another possible solution is to record the HASH of system image after
>> system upgrade is done. Next time when we try to do the system
>> upgrade, we can compare the recorded HASH value and the compute value
>> from system upgrade file?
Also a good idea but normally this should be done by the sysupgrade util
itself.
Building upgrade logic into the mountd is not the way we should go. See
above.
>>
>> 2016-07-06 18:18 GMT+08:00 Bruno Randolf <br1@einfach.org>:
>>> On 06/07/16 06:58, John Crispin wrote:
>>>> Hi Philipp
>>>>
>>>> the thing that i am worried about is this process
>>>>
>>>> 1) plug a stick
>>>> 2) mountd detects the upgrade file
>>>> 3) mountd triggers sysupgrade
>>>> 4) system reboots
>>>> 5) goto 1)
>>>>
>>>> there is no way to know at what point between 3 and 4 we need to unplug
>>>> the usb stick
Yep, I totally agree. A better solution based on this patch would be
copying the update to tmp
and execute it after the storage device is removed.
>>> One possible solution is to write a file, something like
>>> ".sysupgrade-applied" or remove the upgrade file before actually doing
>>> the upgrade, or during first boot.
>>>
>>> bruno
I think this solution is lacking the feature "batch update" on for
example 20 devices in row.
>>>>       John
>>>>
>>>> On 04/07/2016 18:56, Philipp Deppenwiese wrote:
>>>>> Extend the mountd with the ability to apply sysupgrades from mounted devices.
>>>>> Upgrade files are identified by filename and executed through the
>>>>> commandline sysupgrade utility of LEDE.
>>>>>
>>>>> Option List:
>>>>>
>>>>> config mountd 'sysupgrade'
>>>>>      option  check_filename  firmware.bin (required)
>>>>>      option  save_config     1       (optional, default disabled)
>>>>>      option  delay           10      (optional, default disabled)
>>>>>      option  preserve_part   1       (optional, default disabled)
>>>>>      option  enabled         1
>>>>>
>>>>> The so called "filesearch" is done only in the root of a mounted
>>>>> devices.
>>>>>
>>>>> Signed-off-by: Philipp Deppenwiese <zaolin@das-labor.org>
>>>>> ---
>>>>>  CMakeLists.txt    |   2 +-
>>>>>  autofs.c          |  19 ++++++-
>>>>>  include/upgrade.h |   6 ++
>>>>>  mount.c           |   2 +
>>>>>  upgrade.c         | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>>>  5 files changed, 189 insertions(+), 2 deletions(-)
>>>>>  create mode 100644 include/upgrade.h
>>>>>  create mode 100644 upgrade.c
>>>>>
>>>>> diff --git a/CMakeLists.txt b/CMakeLists.txt
>>>>> index 2e712cd..ed3602e 100644
>>>>> --- a/CMakeLists.txt
>>>>> +++ b/CMakeLists.txt
>>>>> @@ -5,7 +5,7 @@ ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
>>>>>
>>>>>  SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
>>>>>
>>>>> -ADD_EXECUTABLE(mountd main.c log.c sys.c autofs.c mount.c timer.c signal.c ucix.c led.c fs.c ucix.c)
>>>>> +ADD_EXECUTABLE(mountd main.c log.c sys.c autofs.c mount.c timer.c signal.c ucix.c led.c fs.c ucix.c upgrade.c)
>>>>>  TARGET_LINK_LIBRARIES(mountd uci ubox)
>>>>>
>>>>>  INSTALL(TARGETS mountd
>>>>> diff --git a/autofs.c b/autofs.c
>>>>> index 4ad782d..79a3e97 100644
>>>>> --- a/autofs.c
>>>>> +++ b/autofs.c
>>>>> @@ -37,6 +37,13 @@ dev_t dev;
>>>>>  time_t uci_timeout;
>>>>>  char uci_path[32];
>>>>>
>>>>> +// Sysupgrade uci options
>>>>> +char uci_upgrade_filename[255];
>>>>> +int uci_upgrade_backup;
>>>>> +int uci_upgrade_delay;
>>>>> +int uci_upgrade_enabled;
>>>>> +int uci_upgrade_preserve_partition;
>>>>> +
>>>>>  static void umount_autofs(void)
>>>>>  {
>>>>>      system_printf("umount %s 2> /dev/null", "/tmp/run/mountd/");
>>>>> @@ -186,18 +193,28 @@ static void autofs_cleanup_handler(void)
>>>>>  static void autofs_init(void)
>>>>>  {
>>>>>      int kproto_version;
>>>>> -    char *p;
>>>>> +    char *p, *filename = NULL;
>>>>>      struct uci_context *ctx;
>>>>>      signal_init(autofs_cleanup_handler);
>>>>>      ctx = ucix_init("mountd");
>>>>>      uci_timeout = ucix_get_option_int(ctx, "mountd", "mountd", "timeout", 60);
>>>>>      p = ucix_get_option(ctx, "mountd", "mountd", "path");
>>>>> +    filename = ucix_get_option(ctx, "mountd", "sysupgrade", "check_filename");
>>>>> +    uci_upgrade_backup = ucix_get_option_int(ctx, "mountd", "sysupgrade", "save_config", 0);
>>>>> +    uci_upgrade_delay = ucix_get_option_int(ctx, "mountd", "sysupgrade", "delay", 0);
>>>>> +    uci_upgrade_enabled = ucix_get_option_int(ctx, "mountd", "sysupgrade", "enabled", 0);
>>>>> +    uci_upgrade_preserve_partition = ucix_get_option_int(ctx, "mountd", "sysupgrade", "preserve_part", 0);
>>>>>      ucix_cleanup(ctx);
>>>>>      if(p)
>>>>>              snprintf(uci_path, 31, "%s", p);
>>>>>      else
>>>>>              snprintf(uci_path, 31, "/tmp/mounts/");
>>>>>      uci_path[31] = '\0';
>>>>> +
>>>>> +    if(filename) {
>>>>> +            snprintf(uci_upgrade_filename, 255, "%s", filename);
>>>>> +    }
>>>>> +
>>>>>      mkdir("/tmp/run/", 0555);
>>>>>      mkdir("/tmp/mounts", 0555);
>>>>>      system_printf("rm -rf %s*", uci_path);
>>>>> diff --git a/include/upgrade.h b/include/upgrade.h
>>>>> new file mode 100644
>>>>> index 0000000..6b5ace9
>>>>> --- /dev/null
>>>>> +++ b/include/upgrade.h
>>>>> @@ -0,0 +1,6 @@
>>>>> +#ifndef _UPGRADE_H__
>>>>> +#define _UPGRADE_H__
>>>>> +
>>>>> +int sysupgrade_scan(const char *mount_path);
>>>>> +
>>>>> +#endif
>>>>> diff --git a/mount.c b/mount.c
>>>>> index 7cbb8ff..0bec25e 100644
>>>>> --- a/mount.c
>>>>> +++ b/mount.c
>>>>> @@ -28,6 +28,7 @@
>>>>>  #include "include/ucix.h"
>>>>>  #include "include/fs.h"
>>>>>  #include "include/mount.h"
>>>>> +#include "include/upgrade.h"
>>>>>
>>>>>  int mount_new(char *path, char *dev);
>>>>>
>>>>> @@ -300,6 +301,7 @@ int mount_new(char *path, char *dev)
>>>>>      {
>>>>>              mount->mounted = 1;
>>>>>              mount_dump_uci_state();
>>>>> +            sysupgrade_scan(tmp);
>>>>>      } else return -1;
>>>>>      return 0;
>>>>>  }
>>>>> diff --git a/upgrade.c b/upgrade.c
>>>>> new file mode 100644
>>>>> index 0000000..6992e65
>>>>> --- /dev/null
>>>>> +++ b/upgrade.c
>>>>> @@ -0,0 +1,162 @@
>>>>> +/*
>>>>> + *   This program is free software; you can redistribute it and/or modify
>>>>> + *   it under the terms of the GNU General Public License as published by
>>>>> + *   the Free Software Foundation; either version 2 of the License, or
>>>>> + *   (at your option) any later version.
>>>>> + *
>>>>> + *   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.
>>>>> + *
>>>>> + *   Provided by 9elements GmbH
>>>>> + *   Copyright (C) 2016 Philipp Deppenwiese <philipp.deppenwiese@9elements.com>
>>>>> + */
>>>>> +
>>>>> +#include <errno.h>
>>>>> +#include <linux/limits.h>
>>>>> +#include <stdio.h>
>>>>> +#include <stdlib.h>
>>>>> +#include <stdarg.h>
>>>>> +#include <string.h>
>>>>> +#include <sys/stat.h>
>>>>> +#include <sys/types.h>
>>>>> +#include <sys/wait.h>
>>>>> +#include <unistd.h>
>>>>> +
>>>>> +#include "include/upgrade.h"
>>>>> +#include "include/log.h"
>>>>> +
>>>>> +#define SYSUPGRADE_CMD "/sbin/sysupgrade"
>>>>> +
>>>>> +extern char uci_upgrade_filename[255];
>>>>> +extern int uci_upgrade_backup;
>>>>> +extern int uci_upgrade_delay;
>>>>> +extern int uci_upgrade_enabled;
>>>>> +extern int uci_upgrade_preserve_partition;
>>>>> +
>>>>> +static unsigned option_index = 0;
>>>>> +static char **parameters = NULL;
>>>>> +
>>>>> +static int execute_sysupgrade()
>>>>> +{
>>>>> +    pid_t pid;
>>>>> +    int status;
>>>>> +    char * const envp[] = { NULL };
>>>>> +
>>>>> +    if (!parameters) {
>>>>> +            return 1;
>>>>> +    }
>>>>> +
>>>>> +    if ((pid = fork()) < 0) {
>>>>> +            perror("Can't fork child process for command invocation!");
>>>>> +            return 1;
>>>>> +    } else if (pid == 0) {
>>>>> +            if (execve(SYSUPGRADE_CMD, parameters, envp) < 0) {
>>>>> +                    log_printf("Error was reported on sysupgrade: %s\n", strerror(errno));
>>>>> +                    return 1;
>>>>> +            }
>>>>> +    } else {
>>>>> +            waitpid(pid, &status, 0);
>>>>> +    }
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>> +static int find_upgrade_filepath(const char *mount_path, char *update_file)
>>>>> +{
>>>>> +    struct stat file;
>>>>> +
>>>>> +    if (snprintf(update_file, PATH_MAX, "%s/%s", mount_path,
>>>>> +                 uci_upgrade_filename) < 0) {
>>>>> +            perror(NULL);
>>>>> +            return 1;
>>>>> +    }
>>>>> +
>>>>> +    if (stat(update_file, &file) < 0) {
>>>>> +            log_printf("No upgrade file found for device path: %s! \n", mount_path);
>>>>> +            return 1;
>>>>> +    }
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>> +static void add_sysupgrade_option(const char *parameter, ...)
>>>>> +{
>>>>> +    va_list arguments;
>>>>> +    char *option = NULL;
>>>>> +
>>>>> +    parameters = realloc(parameters, ++option_index * sizeof(char*));
>>>>> +
>>>>> +    if (parameter) {
>>>>> +            option = (char*)calloc(PATH_MAX, sizeof(char));
>>>>> +
>>>>> +            va_start(arguments, parameter);
>>>>> +            vsnprintf(option, PATH_MAX, parameter, arguments);
>>>>> +            va_end(arguments);
>>>>> +    }
>>>>> +
>>>>> +    parameters[option_index - 1] = option;
>>>>> +}
>>>>> +
>>>>> +static void free_all()
>>>>> +{
>>>>> +    unsigned i;
>>>>> +
>>>>> +    if (!parameters) {
>>>>> +            return;
>>>>> +    }
>>>>> +
>>>>> +    for (i = 0; i < option_index; i++) {
>>>>> +            if (parameters[i]) {
>>>>> +                    free(parameters[i]);
>>>>> +            }
>>>>> +    }
>>>>> +
>>>>> +    free(parameters);
>>>>> +    parameters = NULL;
>>>>> +    option_index = 0;
>>>>> +}
>>>>> +
>>>>> +int sysupgrade_scan(const char *mount_path)
>>>>> +{
>>>>> +    char update_file[PATH_MAX];
>>>>> +
>>>>> +    if (!uci_upgrade_enabled) {
>>>>> +            log_printf("Sysupgrade via usb device is disabled..\n");
>>>>> +            return 0;
>>>>> +    }
>>>>> +
>>>>> +    if (find_upgrade_filepath(mount_path, update_file)) {
>>>>> +            log_printf("No upgrade file could be found..\n");
>>>>> +            return 0;
>>>>> +    }
>>>>> +
>>>>> +    log_printf("Starting sysupgrade routine..\n");
>>>>> +
>>>>> +    add_sysupgrade_option("%s", SYSUPGRADE_CMD);
>>>>> +
>>>>> +    if (uci_upgrade_delay) {
>>>>> +            add_sysupgrade_option("-d %i", uci_upgrade_delay);
>>>>> +    }
>>>>> +
>>>>> +    if (uci_upgrade_backup) {
>>>>> +            add_sysupgrade_option("-c");
>>>>> +    } else {
>>>>> +            add_sysupgrade_option("-n");
>>>>> +    }
>>>>> +
>>>>> +    if (uci_upgrade_preserve_partition) {
>>>>> +            add_sysupgrade_option("-p");
>>>>> +    }
>>>>> +
>>>>> +    add_sysupgrade_option("%s", update_file);
>>>>> +    add_sysupgrade_option(NULL);
>>>>> +
>>>>> +    if (execute_sysupgrade()) {
>>>>> +            free_all();
>>>>> +            log_printf("Try to start sysupgrade but it failed! \n");
>>>>> +            return 1;
>>>>> +    }
>>>>> +
>>>>> +    free_all();
>>>>> +    return 0;
>>>>> +}
>>>>>
>>>> _______________________________________________
>>>> Lede-dev mailing list
>>>> Lede-dev@lists.infradead.org
>>>> http://lists.infradead.org/mailman/listinfo/lede-dev
>>>>
>>>
>>> _______________________________________________
>>> Lede-dev mailing list
>>> Lede-dev@lists.infradead.org
>>> http://lists.infradead.org/mailman/listinfo/lede-dev
>> _______________________________________________
>> Lede-dev mailing list
>> Lede-dev@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/lede-dev
> _______________________________________________
> Lede-dev mailing list
> Lede-dev@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/lede-dev
olivier.hardouin@gmail.com July 8, 2016, 8:21 a.m. UTC | #6
Hi Philipp,

> Yes, good point. But then we should rewrite/redesign the whole update
> process
> including the sysupgrade binary in my opinion. For example writing a
> small library and a daemon
> which does the updates for us. A daemon which executes a sysupgrade tool
> doesn't
> make more sense for me as the mountd solution.

I'm not sure why a complete re-design of the sysupgrade would be
needed in this particular case.
At the end you only want to trigger the upgrade when a stick is
inserted, and hotplug can do that.
If the following (untested and unfinished) script is stored in
/etc/hotplug.d/mount/99-autoupgrade, it would perform the upgrade.
#/bin/sh
if [ "${ACTION}" == "add" ]; then
        if [[ "$(uci get -q autoupgrade.usbdisk.enabled)" == "1" ]]; then
                filename="$(uci get -q
mountd.mountd.path)${NAME}/$(uci get -q autoupgrade.usbdisk.filename)"
                [[ ! -f "$filename" ]] && exit
                uci get -q autoupgrade.usbdisk.delay >/dev/null &&
delay="-d $(uci get -q autoupgrade.usbdisk.delay)"
                [[ "$(uci get -q autoupgrade.usbdisk.save_config)" ==
"1" ]] && backup="-c" || backup="-n"
                [[ "$(uci get -q autoupgrade.usbdisk.preserve_part)" =
"1" ]] && preserve_partition="-p"
                logger -t hotplug " Starting upgrade with
/sbin/sysupgrade $delay $backup $preserve_partition $filename"
                /sbin/sysupgrade $delay $backup $preserve_partition $filename
                # TODO find a good way to not execute this at every boot ...
        fi
fi
The uci config is the same as what you described in your patch (just
replace mountd by autoupgrade).
This is a proof of concept. You may argue about doing the upgrade from
within a hotplug script. I indeed would be cleaner to have a separate
daemon, especially if they are several sources for the upgrade (and
not only the usb).

Olivier
diff mbox

Patch

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2e712cd..ed3602e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,7 +5,7 @@  ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
 
 SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
 
-ADD_EXECUTABLE(mountd main.c log.c sys.c autofs.c mount.c timer.c signal.c ucix.c led.c fs.c ucix.c)
+ADD_EXECUTABLE(mountd main.c log.c sys.c autofs.c mount.c timer.c signal.c ucix.c led.c fs.c ucix.c upgrade.c)
 TARGET_LINK_LIBRARIES(mountd uci ubox)
 
 INSTALL(TARGETS mountd
diff --git a/autofs.c b/autofs.c
index 4ad782d..79a3e97 100644
--- a/autofs.c
+++ b/autofs.c
@@ -37,6 +37,13 @@  dev_t dev;
 time_t uci_timeout;
 char uci_path[32];
 
+// Sysupgrade uci options
+char uci_upgrade_filename[255];
+int uci_upgrade_backup;
+int uci_upgrade_delay;
+int uci_upgrade_enabled;
+int uci_upgrade_preserve_partition;
+
 static void umount_autofs(void)
 {
 	system_printf("umount %s 2> /dev/null", "/tmp/run/mountd/");
@@ -186,18 +193,28 @@  static void autofs_cleanup_handler(void)
 static void autofs_init(void)
 {
 	int kproto_version;
-	char *p;
+	char *p, *filename = NULL;
 	struct uci_context *ctx;
 	signal_init(autofs_cleanup_handler);
 	ctx = ucix_init("mountd");
 	uci_timeout = ucix_get_option_int(ctx, "mountd", "mountd", "timeout", 60);
 	p = ucix_get_option(ctx, "mountd", "mountd", "path");
+	filename = ucix_get_option(ctx, "mountd", "sysupgrade", "check_filename");
+	uci_upgrade_backup = ucix_get_option_int(ctx, "mountd", "sysupgrade", "save_config", 0);
+	uci_upgrade_delay = ucix_get_option_int(ctx, "mountd", "sysupgrade", "delay", 0);
+	uci_upgrade_enabled = ucix_get_option_int(ctx, "mountd", "sysupgrade", "enabled", 0);
+	uci_upgrade_preserve_partition = ucix_get_option_int(ctx, "mountd", "sysupgrade", "preserve_part", 0);
 	ucix_cleanup(ctx);
 	if(p)
 		snprintf(uci_path, 31, "%s", p);
 	else
 		snprintf(uci_path, 31, "/tmp/mounts/");
 	uci_path[31] = '\0';
+
+	if(filename) {
+		snprintf(uci_upgrade_filename, 255, "%s", filename);
+	}
+
 	mkdir("/tmp/run/", 0555);
 	mkdir("/tmp/mounts", 0555);
 	system_printf("rm -rf %s*", uci_path);
diff --git a/include/upgrade.h b/include/upgrade.h
new file mode 100644
index 0000000..6b5ace9
--- /dev/null
+++ b/include/upgrade.h
@@ -0,0 +1,6 @@ 
+#ifndef _UPGRADE_H__
+#define _UPGRADE_H__
+
+int sysupgrade_scan(const char *mount_path);
+
+#endif
diff --git a/mount.c b/mount.c
index 7cbb8ff..0bec25e 100644
--- a/mount.c
+++ b/mount.c
@@ -28,6 +28,7 @@ 
 #include "include/ucix.h"
 #include "include/fs.h"
 #include "include/mount.h"
+#include "include/upgrade.h"
 
 int mount_new(char *path, char *dev);
 
@@ -300,6 +301,7 @@  int mount_new(char *path, char *dev)
 	{
 		mount->mounted = 1;
 		mount_dump_uci_state();
+		sysupgrade_scan(tmp);
 	} else return -1;
 	return 0;
 }
diff --git a/upgrade.c b/upgrade.c
new file mode 100644
index 0000000..6992e65
--- /dev/null
+++ b/upgrade.c
@@ -0,0 +1,162 @@ 
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   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.
+ *
+ *   Provided by 9elements GmbH
+ *   Copyright (C) 2016 Philipp Deppenwiese <philipp.deppenwiese@9elements.com>
+ */
+
+#include <errno.h>
+#include <linux/limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "include/upgrade.h"
+#include "include/log.h"
+
+#define SYSUPGRADE_CMD "/sbin/sysupgrade"
+
+extern char uci_upgrade_filename[255];
+extern int uci_upgrade_backup;
+extern int uci_upgrade_delay;
+extern int uci_upgrade_enabled;
+extern int uci_upgrade_preserve_partition;
+
+static unsigned option_index = 0;
+static char **parameters = NULL;
+
+static int execute_sysupgrade()
+{
+	pid_t pid;
+	int status;
+	char * const envp[] = { NULL };
+
+	if (!parameters) {
+		return 1;
+	}
+
+	if ((pid = fork()) < 0) {
+		perror("Can't fork child process for command invocation!");
+		return 1;
+	} else if (pid == 0) {
+		if (execve(SYSUPGRADE_CMD, parameters, envp) < 0) {
+			log_printf("Error was reported on sysupgrade: %s\n", strerror(errno));
+			return 1;
+		}
+	} else {
+		waitpid(pid, &status, 0);
+	}
+	return 0;
+}
+
+static int find_upgrade_filepath(const char *mount_path, char *update_file)
+{
+	struct stat file;
+
+	if (snprintf(update_file, PATH_MAX, "%s/%s", mount_path,
+		     uci_upgrade_filename) < 0) {
+		perror(NULL);
+		return 1;
+	}
+
+	if (stat(update_file, &file) < 0) {
+		log_printf("No upgrade file found for device path: %s! \n", mount_path);
+		return 1;
+	}
+	return 0;
+}
+
+static void add_sysupgrade_option(const char *parameter, ...)
+{
+	va_list arguments;
+	char *option = NULL;
+
+	parameters = realloc(parameters, ++option_index * sizeof(char*));
+
+	if (parameter) {
+		option = (char*)calloc(PATH_MAX, sizeof(char));
+
+		va_start(arguments, parameter);
+		vsnprintf(option, PATH_MAX, parameter, arguments);
+		va_end(arguments);
+	}
+
+	parameters[option_index - 1] = option;
+}
+
+static void free_all()
+{
+	unsigned i;
+
+	if (!parameters) {
+		return;
+	}
+
+	for (i = 0; i < option_index; i++) {
+		if (parameters[i]) {
+			free(parameters[i]);
+		}
+	}
+
+	free(parameters);
+	parameters = NULL;
+	option_index = 0;
+}
+
+int sysupgrade_scan(const char *mount_path)
+{
+	char update_file[PATH_MAX];
+
+	if (!uci_upgrade_enabled) {
+		log_printf("Sysupgrade via usb device is disabled..\n");
+		return 0;
+	}
+
+	if (find_upgrade_filepath(mount_path, update_file)) {
+		log_printf("No upgrade file could be found..\n");
+		return 0;
+	}
+
+	log_printf("Starting sysupgrade routine..\n");
+
+	add_sysupgrade_option("%s", SYSUPGRADE_CMD);
+
+	if (uci_upgrade_delay) {
+		add_sysupgrade_option("-d %i", uci_upgrade_delay);
+	}
+
+	if (uci_upgrade_backup) {
+		add_sysupgrade_option("-c");
+	} else {
+		add_sysupgrade_option("-n");
+	}
+
+	if (uci_upgrade_preserve_partition) {
+		add_sysupgrade_option("-p");
+	}
+
+	add_sysupgrade_option("%s", update_file);
+	add_sysupgrade_option(NULL);
+
+	if (execute_sysupgrade()) {
+		free_all();
+		log_printf("Try to start sysupgrade but it failed! \n");
+		return 1;
+	}
+
+	free_all();
+	return 0;
+}