diff --git a/src/config.c b/src/config.c
index 3afdfbf6..9f445ed8 100644
--- a/src/config.c
+++ b/src/config.c
@@ -16,13 +16,15 @@
* along with this program. If not, see .
*/
+#include
+#include
+
#include "tvheadend.h"
#include "settings.h"
#include "config.h"
#include "uuid.h"
-
-#include
-#include
+#include "htsbuf.h"
+#include "spawn.h"
/* *************************************************************************
* Global data
@@ -956,6 +958,70 @@ config_migrate_v11 ( void )
htsmsg_destroy(dvr_config);
}
+/*
+ * Perform backup
+ */
+static void
+dobackup(const char *oldver)
+{
+ char outfile[PATH_MAX], cwd[PATH_MAX];
+ const char *argv[] = {
+ "/usr/bin/tar", "cjf", outfile, "--exclude", "backup", ".", NULL
+ };
+ const char *root = hts_settings_get_root();
+ char errtxt[128];
+ const char **arg;
+ int code;
+
+ tvhinfo("config", "backup: migrating config from %s (running %s)",
+ oldver, tvheadend_version);
+
+ getcwd(cwd, sizeof(cwd));
+
+ snprintf(outfile, sizeof(outfile), "%s/backup", root);
+ if (makedirs(outfile, 0700))
+ goto fatal;
+ if (chdir(root)) {
+ tvherror("config", "unable to find directory '%s'", root);
+ goto fatal;
+ }
+
+ snprintf(outfile, sizeof(outfile), "%s/backup/%s.tar.bz2",
+ root, oldver);
+ tvhinfo("config", "backup: running, output file %s", outfile);
+
+ spawnv(argv[0], (void *)argv);
+
+ while ((code = spawn_reap(errtxt, sizeof(errtxt))) == -EAGAIN)
+ usleep(20000);
+
+ if (code) {
+ htsbuf_queue_t q;
+ char *s;
+ htsbuf_queue_init(&q, 0);
+ for (arg = argv; *arg; arg++) {
+ htsbuf_append(&q, *arg, strlen(*arg));
+ if (arg[1])
+ htsbuf_append(&q, " ", 1);
+ }
+ s = htsbuf_to_string(&q);
+ tvherror("config", "command '%s' returned error code %d", s, code);
+ free(s);
+ htsbuf_queue_flush(&q);
+ goto fatal;
+ }
+
+ if (chdir(cwd)) {
+ tvherror("config", "unable to change directory to '%s'", cwd);
+ goto fatal;
+ }
+ return;
+
+fatal:
+ tvherror("config", "backup: fatal error");
+ exit(EXIT_FAILURE);
+}
+
/*
* Migration table
*/
@@ -977,12 +1043,19 @@ static const config_migrate_t config_migrate_table[] = {
* Perform migrations (if required)
*/
static int
-config_migrate ( void )
+config_migrate ( int backup )
{
uint32_t v;
+ const char *s;
/* Get the current version */
v = htsmsg_get_u32_or_default(config, "version", 0);
+ s = htsmsg_get_str(config, "fullversion") ?: "unknown";
+
+ if (backup && strcmp(s, tvheadend_version))
+ dobackup(s);
+ else
+ backup = 0;
/* Attempt to auto-detect versions prior to v2 */
if (!v) {
@@ -993,8 +1066,11 @@ config_migrate ( void )
}
/* No changes required */
- if (v == ARRAY_SIZE(config_migrate_table))
+ if (v == ARRAY_SIZE(config_migrate_table)) {
+ if (backup)
+ goto update;
return 0;
+ }
/* Run migrations */
for ( ; v < ARRAY_SIZE(config_migrate_table); v++) {
@@ -1003,7 +1079,9 @@ config_migrate ( void )
}
/* Update */
+update:
htsmsg_set_u32(config, "version", v);
+ htsmsg_set_str(config, "fullversion", tvheadend_version);
config_save();
return 1;
}
@@ -1050,7 +1128,7 @@ config_check ( void )
* *************************************************************************/
void
-config_init ( const char *path )
+config_init ( const char *path, int backup )
{
struct stat st;
char buf[1024];
@@ -1095,11 +1173,12 @@ config_init ( const char *path )
/* Store version number */
if (new) {
htsmsg_set_u32(config, "version", ARRAY_SIZE(config_migrate_table));
+ htsmsg_set_str(config, "fullversion", tvheadend_version);
config_save();
/* Perform migrations */
} else {
- if (config_migrate())
+ if (config_migrate(backup))
config_check();
}
}
diff --git a/src/config.h b/src/config.h
index 4473947d..9f08a672 100644
--- a/src/config.h
+++ b/src/config.h
@@ -23,7 +23,7 @@
#include "htsmsg.h"
-void config_init ( const char *path );
+void config_init ( const char *path, int backup );
void config_done ( void );
void config_save ( void );
diff --git a/src/main.c b/src/main.c
index 0a2ae7a5..9bc86856 100644
--- a/src/main.c
+++ b/src/main.c
@@ -484,7 +484,8 @@ main(int argc, char **argv)
opt_dump = 0,
opt_xspf = 0,
opt_dbus = 0,
- opt_dbus_session = 0;
+ opt_dbus_session = 0,
+ opt_nobackup = 0;
const char *opt_config = NULL,
*opt_user = NULL,
*opt_group = NULL,
@@ -507,6 +508,7 @@ main(int argc, char **argv)
{ 0, NULL, "Service Configuration", OPT_BOOL, NULL },
{ 'c', "config", "Alternate config path", OPT_STR, &opt_config },
+ { 'B', "nobackup", "Do not backup config tree at upgrade", OPT_BOOL, &opt_nobackup },
{ 'f', "fork", "Fork and run as daemon", OPT_BOOL, &opt_fork },
{ 'u', "user", "Run as user", OPT_STR, &opt_user },
{ 'g', "group", "Run as group", OPT_STR, &opt_group },
@@ -787,7 +789,7 @@ main(int argc, char **argv)
/* Initialise configuration */
uuid_init();
idnode_init();
- config_init(opt_config);
+ config_init(opt_config, opt_nobackup == 0);
/**
* Initialize subsystems
diff --git a/src/settings.c b/src/settings.c
index 5fea51c5..cf31d893 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -184,21 +184,22 @@ hts_settings_save(htsmsg_t *record, const char *pathfmt, ...)
static htsmsg_t *
hts_settings_load_one(const char *filename)
{
- ssize_t n;
+ ssize_t n, size;
char *mem;
fb_file *fp;
htsmsg_t *r = NULL;
/* Open */
if (!(fp = fb_open(filename, 1, 0))) return NULL;
+ size = fb_size(fp);
/* Load data */
- mem = malloc(fb_size(fp)+1);
- n = fb_read(fp, mem, fb_size(fp));
+ mem = malloc(size+1);
+ n = fb_read(fp, mem, size);
if (n >= 0) mem[n] = 0;
/* Decode */
- if(n == fb_size(fp))
+ if(n == size)
r = htsmsg_json_deserialize(mem);
/* Close */
diff --git a/src/spawn.c b/src/spawn.c
index b1bdacbf..b7d8bacc 100644
--- a/src/spawn.c
+++ b/src/spawn.c
@@ -84,50 +84,56 @@ find_exec ( const char *name, char *out, size_t len )
}
/**
- * The reaper is called once a second to finish of any pending spawns
+ * Reap one child
*/
-void
-spawn_reaper(void)
+int
+spawn_reap(char *stxt, size_t stxtlen)
{
pid_t pid;
- int status;
- char txt[100];
+ int status, res;
spawn_t *s;
- while(1) {
- pid = waitpid(-1, &status, WNOHANG);
- if(pid < 1)
+ pid = waitpid(-1, &status, WNOHANG);
+ if(pid < 1)
+ return -EAGAIN;
+
+ pthread_mutex_lock(&spawn_mutex);
+ LIST_FOREACH(s, &spawns, link)
+ if(s->pid == pid)
break;
- pthread_mutex_lock(&spawn_mutex);
- LIST_FOREACH(s, &spawns, link)
- if(s->pid == pid)
- break;
-
- if (WIFEXITED(status)) {
- snprintf(txt, sizeof(txt),
- "exited, status=%d", WEXITSTATUS(status));
- } else if (WIFSIGNALED(status)) {
- snprintf(txt, sizeof(txt),
- "killed by signal %d", WTERMSIG(status));
- } else if (WIFSTOPPED(status)) {
- snprintf(txt, sizeof(txt),
- "stopped by signal %d", WSTOPSIG(status));
- } else if (WIFCONTINUED(status)) {
- snprintf(txt, sizeof(txt),
- "continued");
- } else {
- snprintf(txt, sizeof(txt),
- "unknown status");
- }
-
- if(s != NULL) {
- LIST_REMOVE(s, link);
- free((void *)s->name);
- free(s);
- }
- pthread_mutex_unlock(&spawn_mutex);
+ res = -EIO;
+ if (WIFEXITED(status)) {
+ res = WEXITSTATUS(status);
+ if (stxt)
+ snprintf(stxt, stxtlen, "exited, status=%d", WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ snprintf(stxt, stxtlen, "killed by signal %d, "
+ "stopped by signal %d",
+ WTERMSIG(status),
+ WSTOPSIG(status));
+ } else if (WIFCONTINUED(status)) {
+ snprintf(stxt, stxtlen, "continued");
+ } else {
+ snprintf(stxt, stxtlen, "unknown status");
}
+
+ if(s != NULL) {
+ LIST_REMOVE(s, link);
+ free((void *)s->name);
+ free(s);
+ }
+ pthread_mutex_unlock(&spawn_mutex);
+ return res;
+}
+
+/**
+ * The reaper is called once a second to finish of any pending spawns
+ */
+void
+spawn_reaper(void)
+{
+ while (spawn_reap(NULL, 0) != -EAGAIN) ;
}
diff --git a/src/spawn.h b/src/spawn.h
index 5f89426b..64e734bf 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -25,6 +25,8 @@ int spawn_and_store_stdout(const char *prog, char *argv[], char **outp);
int spawnv(const char *prog, char *argv[]);
+int spawn_reap(char *stxt, size_t stxtlen);
+
void spawn_reaper(void);
#endif /* SPAWN_H */