config: add automatic backup at upgrade using 'tar cvjf' to <CFG>/backup directory
This commit is contained in:
parent
fe478893fd
commit
95c4abe53e
6 changed files with 140 additions and 50 deletions
93
src/config.c
93
src/config.c
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
78
src/spawn.c
78
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) ;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Add table
Reference in a new issue