summaryrefslogtreecommitdiff
path: root/accel-pppd/backup
diff options
context:
space:
mode:
authorKozlov Dmitry <xeb@mail.ru>2012-07-10 18:58:53 +0400
committerKozlov Dmitry <xeb@mail.ru>2012-07-10 18:58:53 +0400
commit80c32d237e01b1c05663ccfa34003d2f49aa7eee (patch)
treea49da21b5219e4bc89d9794918f054f3620cb140 /accel-pppd/backup
parent64b5b693764c4f36870fd988ccbb53bcb188e74d (diff)
downloadaccel-ppp-80c32d237e01b1c05663ccfa34003d2f49aa7eee.tar.gz
accel-ppp-80c32d237e01b1c05663ccfa34003d2f49aa7eee.zip
initial session backup implementation
Diffstat (limited to 'accel-pppd/backup')
-rw-r--r--accel-pppd/backup/CMakeLists.txt4
-rw-r--r--accel-pppd/backup/backup.c245
-rw-r--r--accel-pppd/backup/backup.h94
-rw-r--r--accel-pppd/backup/backup_file.c351
4 files changed, 694 insertions, 0 deletions
diff --git a/accel-pppd/backup/CMakeLists.txt b/accel-pppd/backup/CMakeLists.txt
new file mode 100644
index 0000000..77fefab
--- /dev/null
+++ b/accel-pppd/backup/CMakeLists.txt
@@ -0,0 +1,4 @@
+ADD_LIBRARY(backup_file SHARED backup_file.c)
+
+INSTALL(TARGETS backup_file LIBRARY DESTINATION lib/accel-ppp)
+
diff --git a/accel-pppd/backup/backup.c b/accel-pppd/backup/backup.c
new file mode 100644
index 0000000..d045f0c
--- /dev/null
+++ b/accel-pppd/backup/backup.c
@@ -0,0 +1,245 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "triton.h"
+#include "events.h"
+#include "ap_session.h"
+#include "backup.h"
+
+#ifdef USE_BACKUP
+
+static LIST_HEAD(storage_list);
+static LIST_HEAD(module_list);
+
+struct backup_tag __export *backup_add_tag(struct backup_mod *m, uint8_t id, int internal, const void *data, size_t size)
+{
+ struct backup_tag *t;
+
+ t = m->data->storage->alloc_tag(m->data, size);
+ if (!t)
+ return NULL;
+
+ t->id = id;
+ t->internal = internal;
+ t->size = size;
+ memcpy(t->data, data, size);
+
+ list_add_tail(&t->entry, &m->tag_list);
+
+ return t;
+}
+
+void backup_add_fd(struct backup_mod *m, int fd)
+{
+ if (m->data->storage->add_fd)
+ m->data->storage->add_fd(m->data, fd);
+}
+
+struct backup_mod __export *backup_find_mod(struct backup_data *d, uint8_t mod_id)
+{
+ struct backup_mod *m;
+
+ list_for_each_entry(m, &d->mod_list, entry) {
+ if (m->id == mod_id)
+ return m;
+ }
+
+ return NULL;
+}
+
+struct backup_tag __export *backup_find_tag(struct backup_data *d, uint8_t mod_id, uint8_t tag_id, int internal)
+{
+ struct backup_mod *m = backup_find_mod(d, mod_id);
+ struct backup_tag *t;
+
+ if (!m)
+ return NULL;
+
+ list_for_each_entry(t, &m->tag_list, entry) {
+ if (t->id == tag_id && t->internal == internal)
+ return t;
+ }
+
+ return NULL;
+}
+
+void __export backup_free(struct backup_data *data)
+{
+ struct backup_mod *m;
+ struct backup_tag *t;
+
+ while (!list_empty(&data->mod_list)) {
+ m = list_entry(data->mod_list.next, typeof(*m), entry);
+ while (!list_empty(&m->tag_list)) {
+ t = list_entry(m->tag_list.next, typeof(*t), entry);
+ list_del(&t->entry);
+ data->storage->free_tag(data, t);
+ }
+ list_del(&m->entry);
+ data->storage->free_mod(m);
+ }
+ data->storage->free(data);
+}
+
+int __export backup_save_session(struct ap_session *ses)
+{
+ struct backup_storage *storage;
+ struct backup_module *module;
+ struct backup_data *d;
+ struct backup_mod *m;
+ int r, f1 = 0, f2;
+
+ list_for_each_entry(storage, &storage_list, entry) {
+ d = storage->create(ses);
+ if (!d)
+ continue;
+
+ //d->ses = ses;
+
+ f2 = 0;
+
+ list_for_each_entry(module, &module_list, entry) {
+ if (!module->save)
+ continue;
+
+ m = storage->alloc_mod(d);
+ if (!m) {
+ f2 = 1;
+ break;
+ }
+
+ m->data = d;
+ m->id = module->id;
+ r = module->save(ses, m);
+ if (r == -2) {
+ storage->free_mod(m);
+ continue;
+ }
+
+ list_add_tail(&m->entry, &d->mod_list);
+
+ if (r == -1) {
+ f2 = 1;
+ break;
+ }
+ }
+
+ if (f2)
+ backup_free(d);
+ else {
+ f1 = 1;
+ if (storage->commit)
+ storage->commit(d);
+ ses->backup = d;
+ }
+ }
+
+ return !f1;
+}
+
+/*int backup_restore_internal(void)
+{
+ struct backup_storage *storage;
+
+ list_for_each_entry(storage, &storage_list, entry) {
+ if (storage->restore_internal) {
+ if (storage->check_integrity())
+ continue;
+ storage->restore_internal();
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+void backup_restore_external(void)
+{
+ struct backup_storage *storage;
+
+ list_for_each_entry(storage, &storage_list, entry) {
+ if (storage->restore_external) {
+ if (storage->check_integrity())
+ continue;
+ storage->restore_external();
+ return;
+ }
+ }
+}*/
+
+static void __restore_session(struct ap_session *ses)
+{
+ struct backup_module *module;
+ struct backup_mod *m;
+ struct backup_module *ctrl = NULL;
+
+ list_for_each_entry(module, &module_list, entry) {
+ if (module->ctrl_start)
+ ctrl = module;
+ if (module->restore) {
+ m = backup_find_mod(ses->backup, module->id);
+ if (!m)
+ continue;
+ module->restore(ses, m);
+ }
+ }
+
+ if (ctrl)
+ ctrl->ctrl_start(ses);
+ else {
+ triton_event_fire(EV_CTRL_STARTING, ses);
+ triton_event_fire(EV_CTRL_STARTED, ses);
+
+ ap_session_starting(ses);
+ ap_session_activate(ses);
+ }
+}
+
+void __export backup_restore_session(struct backup_data *d)
+{
+ struct backup_module *module;
+ struct backup_mod *m;
+ struct ap_session *ses;
+
+ list_for_each_entry(module, &module_list, entry) {
+ if (module->ctrl_restore) {
+ m = backup_find_mod(d, module->id);
+ if (!m)
+ continue;
+ ses = module->ctrl_restore(m);
+ ses->backup = d;
+ d->ses = ses;
+ ses->state = AP_STATE_RESTORE;
+ triton_context_call(ses->ctrl->ctx, (triton_event_func)__restore_session, ses);
+ break;
+ }
+ }
+}
+
+
+void __export backup_register_module(struct backup_module *m)
+{
+ list_add_tail(&m->entry, &module_list);
+}
+
+void __export backup_register_storage(struct backup_storage *s)
+{
+ list_add_tail(&s->entry, &storage_list);
+}
+
+void backup_restore_fd()
+{
+
+}
+
+void backup_restore(int internal)
+{
+ struct backup_storage *storage;
+
+ list_for_each_entry(storage, &storage_list, entry) {
+ if (storage->restore)
+ storage->restore(internal);
+ }
+}
+
+#endif
diff --git a/accel-pppd/backup/backup.h b/accel-pppd/backup/backup.h
new file mode 100644
index 0000000..39c4ed7
--- /dev/null
+++ b/accel-pppd/backup/backup.h
@@ -0,0 +1,94 @@
+#ifndef __BACKUP_H
+#define __BACKUP_H
+
+#include <stdint.h>
+#include "list.h"
+
+#define MODID_COMMON 1
+#define MODID_RADIUS 2
+#define MODID_PPPOE 3
+#define MODID_IPOE 4
+#define MODID_PPTP 5
+#define MODID_L2TP 6
+#define MODID_IPPOOL 7
+
+
+struct ap_session;
+struct backup_storage;
+struct backup_data;
+
+struct backup_tag
+{
+ struct list_head entry;
+ uint16_t internal:1;
+ uint8_t id;
+ uint16_t size;
+ uint8_t *data;
+};
+
+struct backup_mod
+{
+ struct backup_data *data;
+ struct list_head entry;
+ int id;
+ struct list_head tag_list;
+};
+
+struct backup_data
+{
+ struct ap_session *ses;
+ struct backup_storage *storage;
+ struct list_head mod_list;
+ int internal:1;
+};
+
+struct backup_module
+{
+ struct list_head entry;
+ int id;
+
+ int (*save)(struct ap_session *, struct backup_mod *);
+ int (*restore)(struct ap_session *, struct backup_mod *);
+
+ struct ap_session *(*ctrl_restore)(struct backup_mod *);
+ void (*ctrl_start)(struct ap_session *ses);
+};
+
+struct backup_storage
+{
+ struct list_head entry;
+
+ /*int (*check_integrity)(void);
+ int (*restore)(int internal);*/
+
+ void (*restore)(int internal);
+
+ struct backup_data *(*create)(struct ap_session *);
+ int (*commit)(struct backup_data *);
+ void (*free)(struct backup_data *);
+
+ struct backup_mod *(*alloc_mod)(struct backup_data *);
+ void (*free_mod)(struct backup_mod *);
+
+ void (*add_fd)(struct backup_data *, int fd);
+
+ struct backup_tag *(*alloc_tag)(struct backup_data *, int size);
+ void (*free_tag)(struct backup_data *, struct backup_tag *);
+};
+
+void backup_register_module(struct backup_module *);
+void backup_register_storage(struct backup_storage *);
+
+int backup_save_session(struct ap_session *ses);
+void backup_restore_session(struct backup_data *d);
+
+struct backup_mod *backup_find_mod(struct backup_data *d, uint8_t mod_id);
+struct backup_tag *backup_find_tag(struct backup_data *d, uint8_t mod_id, uint8_t tag_id, int internal);
+struct backup_tag *backup_add_tag(struct backup_mod *m, uint8_t id, int internal, const void *data, size_t size);
+void backup_add_fd(struct backup_mod *m, int fd);
+
+void backup_restore(int internal);
+void backup_restore_fd();
+
+#endif
+
diff --git a/accel-pppd/backup/backup_file.c b/accel-pppd/backup/backup_file.c
new file mode 100644
index 0000000..b9d2646
--- /dev/null
+++ b/accel-pppd/backup/backup_file.c
@@ -0,0 +1,351 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/mman.h>
+
+#include "triton.h"
+#include "log.h"
+#include "ap_session.h"
+#include "backup.h"
+#include "crypto.h"
+#include "memdebug.h"
+
+#define VERSION 1
+
+struct fs_backup_data
+{
+ struct list_head fd_list;
+ int fd;
+ void *map_addr;
+ int map_len;
+ char sessionid[AP_SESSIONID_LEN];
+ struct backup_data data;
+};
+
+static char *conf_path;
+
+static struct backup_storage file_storage;
+
+static struct backup_data *fs_create(struct ap_session *ses)
+{
+ struct fs_backup_data *d = _malloc(sizeof(*d));
+
+ if (!d)
+ return NULL;
+
+ memset(d, 0, sizeof(*d));
+ d->fd = -1;
+ INIT_LIST_HEAD(&d->fd_list);
+ INIT_LIST_HEAD(&d->data.mod_list);
+ d->data.ses = ses;
+ d->data.storage = &file_storage;
+
+ return &d->data;
+}
+
+static int fs_commit(struct backup_data *d)
+{
+ char fname[PATH_MAX];
+ int fd;
+ struct backup_mod *mod;
+ struct backup_tag *tag;
+ struct iovec iov[IOV_MAX];
+ int i, len, n;
+ MD5_CTX md5;
+ unsigned char md5_buf[16];
+ uint8_t end[4] = {0, 0, 0, 0};
+ uint8_t version = VERSION;
+ uint8_t *ptr;
+
+ if (!conf_path)
+ return -1;
+
+ sprintf(fname, "%s/%s", conf_path, d->ses->sessionid);
+
+ fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, S_IREAD | S_IWRITE);
+
+ if (fd < 0) {
+ log_error("backup: can not create file '%s': %s\n", fname, strerror(errno));
+ return -1;
+ }
+
+ MD5_Init(&md5);
+ MD5_Update(&md5, &version, 1);
+
+ iov[0].iov_base = &version;
+ iov[0].iov_len = 1;
+ i = 1;
+ len = 1;
+
+ list_for_each_entry(mod, &d->mod_list, entry) {
+ MD5_Update(&md5, &mod->id, 1);
+ iov[i].iov_base = &mod->id;
+ iov[i].iov_len = 1;
+ i++;
+ len++;
+
+ list_for_each_entry(tag, &mod->tag_list, entry) {
+ ptr = (uint8_t *)(tag + 1);
+ *ptr = tag->id; ptr++;
+ *ptr = tag->internal ? 1 : 0; ptr++;
+ *(uint16_t *)ptr = tag->size;
+ MD5_Update(&md5, tag + 1, 4 + tag->size);
+ iov[i].iov_base = tag + 1;
+ iov[i].iov_len = 4 + tag->size;
+ i++;
+ len += 4 + tag->size;
+ if (i == IOV_MAX - 2) {
+ n = writev(fd, iov, i);
+ if (n < len) {
+ log_error("backup: short write %i/%i\n", n, len);
+ goto out_err;
+ }
+ i = 0;
+ len = 0;
+ }
+ }
+
+ MD5_Update(&md5, end, 4);
+ iov[i].iov_base = end;
+ iov[i].iov_len = 4;
+ i++;
+ len += 4;
+ }
+
+ MD5_Final(md5_buf, &md5);
+
+ iov[i].iov_base = md5_buf;
+ iov[i].iov_len = 16;
+ len += 16;
+
+ n = writev(fd, iov, i + 1);
+ if (n < len) {
+ log_error("backup: short write %i/%i\n", n, len);
+ goto out_err;
+ }
+
+ close(fd);
+
+ while (!list_empty(&d->mod_list)) {
+ mod = list_entry(d->mod_list.next, typeof(*mod), entry);
+ list_del(&mod->entry);
+ while (!list_empty(&mod->tag_list)) {
+ tag = list_entry(mod->tag_list.next, typeof(*tag), entry);
+ list_del(&tag->entry);
+ _free(tag);
+ }
+ _free(mod);
+ }
+
+ return 0;
+
+out_err:
+ close(fd);
+ unlink(fname);
+ return -1;
+}
+
+static void fs_free(struct backup_data *d)
+{
+ struct fs_backup_data *fsd = container_of(d, typeof(*fsd), data);
+ char fname[PATH_MAX];
+
+ if (fsd->map_addr)
+ munmap(fsd->map_addr, fsd->map_len);
+
+ if (fsd->fd != -1)
+ close(fsd->fd);
+
+ sprintf(fname, "%s/%s", conf_path, d->ses->sessionid);
+ unlink(fname);
+
+ _free(fsd);
+}
+
+static struct backup_mod *fs_alloc_mod(struct backup_data *d)
+{
+ struct backup_mod *m = _malloc(sizeof(struct backup_mod));
+
+ if (!m)
+ return NULL;
+
+ memset(m, 0, sizeof(*m));
+ INIT_LIST_HEAD(&m->tag_list);
+
+ return m;
+}
+
+static void fs_free_mod(struct backup_mod *mod)
+{
+ _free(mod);
+}
+
+static struct backup_tag *fs_alloc_tag(struct backup_data *d, int size)
+{
+ struct backup_tag *t = _malloc(sizeof(struct backup_tag) + 4 + size);
+
+ if (!t)
+ return NULL;
+
+ memset(t, 0, sizeof(*t));
+
+ t->data = (uint8_t *)(t + 1) + 4;
+
+ return t;
+}
+
+static void fs_free_tag(struct backup_data *d, struct backup_tag *tag)
+{
+ _free(tag);
+}
+
+static void fs_add_fd(struct backup_data *d, int fd)
+{
+
+}
+
+static void restore_session(const char *fn, int internal)
+{
+ char fname[PATH_MAX];
+ int fd;
+ struct stat st;
+ uint8_t *ptr, *endptr;
+ MD5_CTX md5;
+ unsigned char md5_buf[16];
+ struct backup_data *d;
+ struct fs_backup_data *fsd;
+ struct backup_mod *mod;
+ struct backup_tag *tag;
+
+ sprintf(fname, "%s/%s", conf_path, fn);
+
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
+ log_emerg("backup_file: open '%s': %s\n", fname, strerror(errno));
+ return;
+ }
+
+ fstat(fd, &st);
+
+ ptr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (ptr == MAP_FAILED) {
+ log_emerg("backup_file: mmap '%s': %s\n", fname, strerror(errno));
+ close(fd);
+ return;
+ }
+
+ if (*ptr != VERSION)
+ goto out;
+
+ MD5_Init(&md5);
+ MD5_Update(&md5, ptr, st.st_size - 16);
+ MD5_Final(md5_buf, &md5);
+
+ if (memcmp(md5_buf, ptr + st.st_size - 16, 16))
+ goto out;
+
+ d = fs_create(NULL);
+ if (!d)
+ goto out;
+
+ d->internal = internal;
+
+ fsd = container_of(d, typeof(*fsd), data);
+ fsd->fd = fd;
+ fsd->map_addr = ptr;
+ fsd->map_len = st.st_size;
+
+ endptr = ptr + st.st_size - 16;
+ ptr++;
+
+ while (ptr < endptr) {
+ mod = fs_alloc_mod(d);
+ list_add_tail(&mod->entry, &d->mod_list);
+ mod->data = d;
+ mod->id = *ptr; ptr++;
+ while (ptr < endptr) {
+ if (*(uint8_t *)ptr == 0) {
+ ptr += 4;
+ break;
+ }
+
+ if (!internal && ptr[1]) {
+ ptr += 4 + *(uint16_t *)(ptr + 2);
+ continue;
+ }
+
+ tag = fs_alloc_tag(d, 0);
+ tag->id = *ptr; ptr++;
+ tag->internal = (*ptr & 0x01) ? 1 : 0; ptr ++;
+ tag->size = *(uint16_t *)ptr; ptr += 2;
+ tag->data = ptr; ptr += tag->size;
+
+ list_add_tail(&tag->entry, &mod->tag_list);
+ }
+ }
+
+ backup_restore_session(d);
+
+ return;
+
+out:
+ munmap(ptr, st.st_size);
+ close(fd);
+}
+
+static void fs_restore(int internal)
+{
+ DIR *dirp;
+ struct dirent ent, *res;
+
+ if (!conf_path)
+ return;
+
+ dirp = opendir(conf_path);
+ if (!dirp) {
+ log_emerg("backup_file: opendir: %s\n", strerror(errno));
+ return;
+ }
+
+ while (1) {
+ if (readdir_r(dirp, &ent, &res)) {
+ log_emerg("backup_file: readdir: %s\n", strerror(errno));
+ break;
+ }
+ if (!res)
+ break;
+ if (strcmp(ent.d_name, ".") == 0 || strcmp(ent.d_name, "..") == 0)
+ continue;
+ restore_session(ent.d_name, internal);
+ }
+
+ closedir(dirp);
+}
+
+static struct backup_storage file_storage = {
+ .create = fs_create,
+ .commit = fs_commit,
+ .free = fs_free,
+ .alloc_mod = fs_alloc_mod,
+ .free_mod = fs_free_mod,
+ .add_fd = fs_add_fd,
+ .alloc_tag = fs_alloc_tag,
+ .free_tag = fs_free_tag,
+ .restore = fs_restore,
+};
+
+static void init(void)
+{
+ conf_path = conf_get_opt("backup", "path");
+
+ backup_register_storage(&file_storage);
+}
+
+DEFINE_INIT(1000, init);