config: add automatic backup at upgrade using 'tar cvjf' to <CFG>/backup directory

This commit is contained in:
Jaroslav Kysela 2014-09-05 10:24:45 +02:00
parent fe478893fd
commit 95c4abe53e
6 changed files with 140 additions and 50 deletions

View file

@ -16,13 +16,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <sys/stat.h>
#include "tvheadend.h"
#include "settings.h"
#include "config.h"
#include "uuid.h"
#include <string.h>
#include <sys/stat.h>
#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();
}
}

View file

@ -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 );

View file

@ -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

View file

@ -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 */

View file

@ -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) ;
}

View file

@ -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 */