summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2019-10-27 22:33:11 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2019-10-27 23:10:41 (GMT)
commit609c184c3edb350a0da7fe29bf449a7189080c92 (patch)
tree4ddd6320ee58a6169cad377f9889a08298fbec47 /src
parentd0547bc36bd6ccb84eff128fc6e4f2df034a705a (diff)
Implemented snapshot related management features.
Diffstat (limited to 'src')
-rw-r--r--src/analysis/db/cdb.c287
-rw-r--r--src/analysis/db/client.c86
-rw-r--r--src/analysis/db/client.h2
-rw-r--r--src/analysis/db/protocol.h30
-rw-r--r--src/analysis/db/snapshot.c419
-rw-r--r--src/analysis/db/snapshot.h11
-rw-r--r--src/common/packed.c8
-rw-r--r--src/common/sqlite.c60
-rw-r--r--src/common/sqlite.h3
-rw-r--r--src/gui/dialogs/snapshots.c15
10 files changed, 824 insertions, 97 deletions
diff --git a/src/analysis/db/cdb.c b/src/analysis/db/cdb.c
index 125f69d..92624f6 100644
--- a/src/analysis/db/cdb.c
+++ b/src/analysis/db/cdb.c
@@ -147,6 +147,15 @@ static void on_collection_extended(GDbCollection *, GDbItem *, GCdbArchive *);
/* Assure le traitement des requêtes de clients. */
static void *g_cdb_archive_process(GCdbArchive *);
+/* Envoie un paquet de données constitué à tous les clients. */
+static void g_cdb_archive_send_reply_to_all_clients(GCdbArchive *, packed_buffer *);
+
+/* Envoie à tous les clients la nouvelle liste d'instantanés. */
+static bool g_cdb_archive_send_snapshot_update(GCdbArchive *, packed_buffer *);
+
+/* Envoie à tous les clients le nouvel instantané courant. */
+static bool g_cdb_archive_send_snapshot_change(GCdbArchive *, packed_buffer *);
+
/* Dissocie un utilisateur de l'archive. */
static void g_cdb_archive_remove_client(GCdbArchive *, size_t);
@@ -255,12 +264,18 @@ static void g_cdb_archive_dispose(GCdbArchive *archive)
static void g_cdb_archive_finalize(GCdbArchive *archive)
{
+#ifndef NDEBUG
int ret; /* Bilan d'un appel */
+#endif
if (archive->db != NULL)
{
+#ifndef NDEBUG
ret = sqlite3_close(archive->db);
assert(ret == SQLITE_OK);
+#else
+ sqlite3_close(archive->db);
+#endif
}
if (archive->xml_desc != NULL)
@@ -299,10 +314,6 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rl
GCdbArchive *result; /* Adresse à retourner */
int ret; /* Retour d'un appel */
struct stat finfo; /* Information sur l'archive */
- snapshot_id_t id; /* Identifiant d'instantané */
-#ifndef NDEBUG
- bool status; /* Validité de l'identifiant */
-#endif
result = g_object_new(G_TYPE_CDB_ARCHIVE, NULL);
@@ -348,14 +359,7 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rl
/* Récupération de la base courante */
-#ifndef NDEBUG
- status = g_db_snapshot_get_current_id(result->snapshot, &id);
- assert(status);
-#else
- g_db_snapshot_get_current_id(result->snapshot, &id);
-#endif
-
- result->db = g_db_snapshot_get_database(result->snapshot, &id);
+ result->db = g_db_snapshot_get_database(result->snapshot);
if (result->db == NULL)
{
@@ -381,14 +385,7 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rl
/* Récupération de la base courante */
-#ifndef NDEBUG
- status = g_db_snapshot_get_current_id(result->snapshot, &id);
- assert(status);
-#else
- g_db_snapshot_get_current_id(result->snapshot, &id);
-#endif
-
- result->db = g_db_snapshot_get_database(result->snapshot, &id);
+ result->db = g_db_snapshot_get_database(result->snapshot);
if (result->db == NULL)
{
@@ -843,7 +840,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
DBError error; /* Bilan d'une opération */
packed_buffer out_pbuf; /* Tampon d'émission */
GDbCollection *collec; /* Collection visée au final */
- snapshot_id_t id; /* Identifiant d'instantané */
+ bool reload; /* Besoin de rechargement */
char *msg; /* Erreur à faire remonter */
void interrupt_poll_with_sigusr1(int sig) { };
@@ -1007,19 +1004,8 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
case DBC_GET_SNAPSHOTS:
- force_snapshots_update:
-
- init_packed_buffer(&out_pbuf);
-
- status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SNAPSHOTS_UPDATED },
- sizeof(uint32_t), true);
- if (!status) goto gcap_bad_reply;
-
- status = g_db_snapshot_pack_all(archive->snapshot, &out_pbuf);
- if (!status) goto gcap_bad_reply;
-
- status = extend_packed_buffer(&out_pbuf, SNAPSHOT_END_MARK, SNAP_ID_HEX_SZ, false);
- if (!status) goto gcap_bad_reply;
+ if (!g_cdb_archive_send_snapshot_update(archive, &out_pbuf))
+ goto critical_error;
status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].ssl_fd);
if (!status) goto gcap_bad_reply;
@@ -1030,21 +1016,8 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
case DBC_GET_CUR_SNAPSHOT:
- init_packed_buffer(&out_pbuf);
-
- status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_CUR_SNAPSHOT_UPDATED },
- sizeof(uint32_t), true);
- if (!status) goto gcap_bad_reply;
-
-#ifndef NDEBUG
- status = g_db_snapshot_get_current_id(archive->snapshot, &id);
- assert(status);
-#else
- g_db_snapshot_get_current_id(archive->snapshot, &id);
-#endif
-
- status = pack_snapshot_id(&id, &out_pbuf);
- if (!status) goto gcap_bad_reply;
+ if (!g_cdb_archive_send_snapshot_change(archive, &out_pbuf))
+ goto critical_error;
status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].ssl_fd);
if (!status) goto gcap_bad_reply;
@@ -1061,8 +1034,34 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
case DBC_SET_CUR_SNAPSHOT:
+ error = g_db_snapshot_restore(archive->snapshot, &in_pbuf, &reload);
+ if (error == DBE_NONE)
+ {
+#ifndef NDEBUG
+ ret = sqlite3_close(archive->db);
+ assert(ret == SQLITE_OK);
+#else
+ sqlite3_close(archive->db);
+#endif
+
+ archive->db = g_db_snapshot_get_database(archive->snapshot);
+
+ if (archive->db == NULL)
+ {
+ error = DBE_SNAPSHOT_RESTORE_FAILURE;
+ }
+ else
+ {
+ if (!g_cdb_archive_send_snapshot_change(archive, NULL))
+ goto critical_error;
+ }
+
+ }
+
+ else if (error == DBE_BAD_EXCHANGE)
+ goto gcap_bad_exchange;
break;
@@ -1071,7 +1070,10 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
error = g_db_snapshot_set_name(archive->snapshot, &in_pbuf);
if (error == DBE_NONE)
- goto force_snapshots_update;
+ {
+ if (!g_cdb_archive_send_snapshot_update(archive, NULL))
+ goto critical_error;
+ }
else if (error == DBE_BAD_EXCHANGE)
goto gcap_bad_exchange;
@@ -1083,7 +1085,47 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
error = g_db_snapshot_set_desc(archive->snapshot, &in_pbuf);
if (error == DBE_NONE)
- goto force_snapshots_update;
+ {
+ if (!g_cdb_archive_send_snapshot_update(archive, NULL))
+ goto critical_error;
+ }
+
+ else if (error == DBE_BAD_EXCHANGE)
+ goto gcap_bad_exchange;
+
+ break;
+
+ case DBC_CREATE_SNAPSHOT:
+
+ error = g_db_snapshot_create(archive->snapshot, archive->db);
+
+ if (error == DBE_NONE)
+ {
+ if (!g_cdb_archive_send_snapshot_update(archive, NULL))
+ goto critical_error;
+ }
+
+ else if (error == DBE_BAD_EXCHANGE)
+ goto gcap_bad_exchange;
+
+ break;
+
+ case DBC_REMOVE_SNAPSHOT:
+
+ error = g_db_snapshot_remove(archive->snapshot, &in_pbuf, &reload);
+
+ if (error == DBE_NONE)
+ {
+ if (!g_cdb_archive_send_snapshot_update(archive, NULL))
+ goto critical_error;
+
+ if (reload)
+ {
+ if (!g_cdb_archive_send_snapshot_change(archive, NULL))
+ goto critical_error;
+ }
+
+ }
else if (error == DBE_BAD_EXCHANGE)
goto gcap_bad_exchange;
@@ -1122,6 +1164,14 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
g_cdb_archive_remove_client(archive, i);
+ continue;
+
+ critical_error:
+
+ LOG_ERROR(LMT_ERROR, _("Internal critical error"));
+
+ assert(0);
+
}
}
@@ -1150,6 +1200,141 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
/******************************************************************************
* *
* Paramètres : archive = archive à connecter avec un utilisateur. *
+* pbuf = paquet de données à émettre. *
+* *
+* Description : Envoie un paquet de données constitué à tous les clients. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_cdb_archive_send_reply_to_all_clients(GCdbArchive *archive, packed_buffer *pbuf)
+{
+ size_t i; /* Boucle de parcours */
+ bool status; /* Bilan d'une émission */
+
+ for (i = 0; i < archive->count; i++)
+ {
+ status = ssl_send_packed_buffer(pbuf, archive->clients[i].ssl_fd);
+ if (!status)
+ {
+ log_variadic_message(LMT_ERROR, _("Error while replying to client %zu"), i);
+
+ g_cdb_archive_remove_client(archive, i);
+ i--;
+
+ }
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : archive = archive à connecter avec un utilisateur. *
+* pbuf = paquet à consituer pour un envoi unique. [OUT] *
+* *
+* Description : Envoie à tous les clients la nouvelle liste d'instantanés. *
+* *
+* Retour : Bilan de constitution de la réponse. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_cdb_archive_send_snapshot_update(GCdbArchive *archive, packed_buffer *pbuf)
+{
+ bool result; /* Bilan à retourner */
+ bool do_send; /* Réalisation de l'émission */
+ packed_buffer out_pbuf; /* Tampon d'émission */
+
+ do_send = (pbuf == NULL);
+
+ if (pbuf == NULL)
+ pbuf = &out_pbuf;
+
+ init_packed_buffer(pbuf);
+
+ result = extend_packed_buffer(pbuf, (uint32_t []) { DBC_SNAPSHOTS_UPDATED },
+ sizeof(uint32_t), true);
+ if (!result) goto bad_reply;
+
+ result = g_db_snapshot_pack_all(archive->snapshot, pbuf);
+ if (!result) goto bad_reply;
+
+ result = extend_packed_buffer(pbuf, SNAPSHOT_END_MARK, SNAP_ID_HEX_SZ, false);
+ if (!result) goto bad_reply;
+
+ if (do_send)
+ g_cdb_archive_send_reply_to_all_clients(archive, pbuf);
+
+ bad_reply:
+
+ if (do_send || !result)
+ exit_packed_buffer(pbuf);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : archive = archive à connecter avec un utilisateur. *
+* pbuf = paquet à consituer pour un envoi unique. [OUT] *
+* *
+* Description : Envoie à tous les clients le nouvel instantané courant. *
+* *
+* Retour : Bilan de constitution de la réponse. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_cdb_archive_send_snapshot_change(GCdbArchive *archive, packed_buffer *pbuf)
+{
+ bool result; /* Bilan à retourner */
+ bool do_send; /* Réalisation de l'émission */
+ packed_buffer out_pbuf; /* Tampon d'émission */
+ snapshot_id_t id; /* Identifiant d'instantané */
+
+ do_send = (pbuf == NULL);
+
+ if (pbuf == NULL)
+ pbuf = &out_pbuf;
+
+ init_packed_buffer(pbuf);
+
+ result = extend_packed_buffer(pbuf, (uint32_t []) { DBC_CUR_SNAPSHOT_UPDATED },
+ sizeof(uint32_t), true);
+ if (!result) goto bad_reply;
+
+ result = g_db_snapshot_get_current_id(archive->snapshot, &id);
+ assert(result);
+ if (!result) goto bad_reply;
+
+ result = pack_snapshot_id(&id, pbuf);
+ if (!result) goto bad_reply;
+
+ if (do_send)
+ g_cdb_archive_send_reply_to_all_clients(archive, pbuf);
+
+ bad_reply:
+
+ if (do_send || !result)
+ exit_packed_buffer(pbuf);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : archive = archive à connecter avec un utilisateur. *
* fd = canal de communication réseau ouvert. *
* *
* Description : Associe un nouvel utilisateur à l'archive. *
diff --git a/src/analysis/db/client.c b/src/analysis/db/client.c
index b4e856f..c1bcd28 100644
--- a/src/analysis/db/client.c
+++ b/src/analysis/db/client.c
@@ -908,6 +908,8 @@ static void *g_hub_client_update(GHubClient *client)
case DBC_SET_CUR_SNAPSHOT:
case DBC_SET_SNAPSHOT_NAME:
case DBC_SET_SNAPSHOT_DESC:
+ case DBC_CREATE_SNAPSHOT:
+ case DBC_REMOVE_SNAPSHOT:
log_variadic_message(LMT_INFO,
_("This command is not available on this side: 0x%08x"), command);
goto gdcu_bad_exchange;
@@ -1586,9 +1588,32 @@ bool g_hub_client_set_snapshot_desc(GHubClient *client, const snapshot_id_t *id,
bool g_hub_client_restore_snapshot(GHubClient *client, const snapshot_id_t *id)
{
- bool result; /* Bilan à retourner */
+ bool result; /* Bilan partiel à remonter */
+ packed_buffer out_pbuf; /* Tampon d'émission */
+ SSL *tls_fd; /* Canal de communication SSL */
+
+ init_packed_buffer(&out_pbuf);
+
+ tls_fd = g_hub_client_get_ssl_fd(client);
+
+ if (tls_fd == NULL)
+ result = false;
- result = false;
+ else
+ {
+ result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SET_CUR_SNAPSHOT }, sizeof(uint32_t), true);
+
+ if (result)
+ result = pack_snapshot_id(id, &out_pbuf);
+
+ if (result)
+ result = ssl_send_packed_buffer(&out_pbuf, tls_fd);
+
+ g_hub_client_put_ssl_fd(client, tls_fd);
+
+ }
+
+ exit_packed_buffer(&out_pbuf);
return result;
@@ -1598,7 +1623,6 @@ bool g_hub_client_restore_snapshot(GHubClient *client, const snapshot_id_t *id)
/******************************************************************************
* *
* Paramètres : client = client pour les accès distants à manipuler. *
-* id = identifiant d'instantané à traiter. *
* *
* Description : Crée un nouvel instantané à partir d'un autre. *
* *
@@ -1608,11 +1632,31 @@ bool g_hub_client_restore_snapshot(GHubClient *client, const snapshot_id_t *id)
* *
******************************************************************************/
-bool g_hub_client_create_snapshot(GHubClient *client, const snapshot_id_t *id)
+bool g_hub_client_create_snapshot(GHubClient *client)
{
- bool result; /* Bilan à retourner */
+ bool result; /* Bilan partiel à remonter */
+ packed_buffer out_pbuf; /* Tampon d'émission */
+ SSL *tls_fd; /* Canal de communication SSL */
- result = false;
+ init_packed_buffer(&out_pbuf);
+
+ tls_fd = g_hub_client_get_ssl_fd(client);
+
+ if (tls_fd == NULL)
+ result = false;
+
+ else
+ {
+ result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_CREATE_SNAPSHOT }, sizeof(uint32_t), true);
+
+ if (result)
+ result = ssl_send_packed_buffer(&out_pbuf, tls_fd);
+
+ g_hub_client_put_ssl_fd(client, tls_fd);
+
+ }
+
+ exit_packed_buffer(&out_pbuf);
return result;
@@ -1635,9 +1679,35 @@ bool g_hub_client_create_snapshot(GHubClient *client, const snapshot_id_t *id)
bool g_hub_client_remove_snapshot(GHubClient *client, const snapshot_id_t *id, bool rec)
{
- bool result; /* Bilan à retourner */
+ bool result; /* Bilan partiel à remonter */
+ packed_buffer out_pbuf; /* Tampon d'émission */
+ SSL *tls_fd; /* Canal de communication SSL */
+
+ init_packed_buffer(&out_pbuf);
+
+ tls_fd = g_hub_client_get_ssl_fd(client);
+
+ if (tls_fd == NULL)
+ result = false;
+
+ else
+ {
+ result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_REMOVE_SNAPSHOT }, sizeof(uint32_t), true);
+
+ if (result)
+ result = pack_snapshot_id(id, &out_pbuf);
+
+ if (result)
+ result = extend_packed_buffer(&out_pbuf, (uint8_t []) { rec ? 0x1 : 0x0 }, sizeof(uint8_t), false);
- result = false;
+ if (result)
+ result = ssl_send_packed_buffer(&out_pbuf, tls_fd);
+
+ g_hub_client_put_ssl_fd(client, tls_fd);
+
+ }
+
+ exit_packed_buffer(&out_pbuf);
return result;
diff --git a/src/analysis/db/client.h b/src/analysis/db/client.h
index af47b5c..0323349 100644
--- a/src/analysis/db/client.h
+++ b/src/analysis/db/client.h
@@ -93,7 +93,7 @@ bool g_hub_client_set_snapshot_desc(GHubClient *, const snapshot_id_t *, const c
bool g_hub_client_restore_snapshot(GHubClient *, const snapshot_id_t *);
/* Crée un nouvel instantané à partir d'un autre. */
-bool g_hub_client_create_snapshot(GHubClient *, const snapshot_id_t *);
+bool g_hub_client_create_snapshot(GHubClient *);
/* Supprime un ancien instantané. */
bool g_hub_client_remove_snapshot(GHubClient *, const snapshot_id_t *, bool);
diff --git a/src/analysis/db/protocol.h b/src/analysis/db/protocol.h
index b57c25c..f66d8fc 100644
--- a/src/analysis/db/protocol.h
+++ b/src/analysis/db/protocol.h
@@ -277,6 +277,34 @@ typedef enum _DBCommand
DBC_SET_SNAPSHOT_DESC, /* Description de l'instantané */
+ /**
+ * Gestion de la commande 'DBC_CREATE_SNAPSHOT'.
+ *
+ * Le client connecté envoie un paquet de la forme suivante :
+ *
+ * [ Gestion d'instantané : DBC_CREATE_SNAPSHOT ]
+ *
+ * Le serveur renvoie ensuite automatiquement un paquet
+ * de type 'DBC_SNAPSHOTS_UPDATED'.
+ */
+
+ DBC_CREATE_SNAPSHOT, /* Création d'instantané */
+
+ /**
+ * Gestion de la commande 'DBC_REMOVE_SNAPSHOT'.
+ *
+ * Le client connecté envoie un paquet de la forme suivante :
+ *
+ * [ Gestion d'instantané : DBC_REMOVE_SNAPSHOT ]
+ * [ <identifiant d'instantané> ]
+ * [ indicateur de récursivité : octet 0x1 ou 0x0 ]
+ *
+ * Le serveur renvoie ensuite automatiquement un paquet
+ * de type 'DBC_SNAPSHOTS_UPDATED'.
+ */
+
+ DBC_REMOVE_SNAPSHOT, /* Suppression d'instantané */
+
DBC_COUNT
} DBCommand;
@@ -303,6 +331,8 @@ typedef enum _DBError
DBE_XML_ERROR, /* Erreur lors d'une définition*/
DBE_SNAPSHOT_NOT_FOUND, /* Instantané non trouvé */
+ DBE_SNAPSHOT_RESTORE_FAILURE, /* Echec d'une restauration */
+ DBE_SNAPSHOT_ROOT_REMOVAL, /* Tentative de suppression */
DBE_COUNT
diff --git a/src/analysis/db/snapshot.c b/src/analysis/db/snapshot.c
index 2cd50f6..e07129e 100644
--- a/src/analysis/db/snapshot.c
+++ b/src/analysis/db/snapshot.c
@@ -38,6 +38,7 @@
#include "../../common/compression.h"
#include "../../common/extstr.h"
#include "../../common/io.h"
+#include "../../common/sqlite.h"
#include "../../common/xml.h"
#include "../../core/logs.h"
@@ -79,9 +80,15 @@ static DBError save_snapshot_node(const snapshot_node_t *, xmlDocPtr, xmlXPathCo
/* Recherche le noeud d'instantané lié à un identifiant. */
static snapshot_node_t *find_snapshot_node(snapshot_node_t *, const snapshot_id_t *);
+/* Détermine si un instantané est compris dans une branche. */
+static bool contain_snapshot_node(const snapshot_node_t *, const snapshot_node_t *);
+
/* Ajoute un instantané comme prolongement d'un instantané. */
static void add_snapshot_node(snapshot_node_t *, snapshot_node_t *);
+/* Fait disparaître un instantané dans une arborescence. */
+static void remove_snapshot_node(snapshot_node_t *, bool);
+
/* Collecte les descriptions d'une arborescence d'instantanés. */
static bool pack_snapshot_node(const snapshot_node_t *, packed_buffer *);
@@ -101,6 +108,8 @@ struct _GDbSnapshot
snapshot_node_t *nodes; /* Instantanés présents */
snapshot_node_t *current; /* Instantané courant */
+ char *current_db; /* Base de données SQLite */
+
};
/* Gestionnaire d'instantanés de bases de données (classe) */
@@ -487,6 +496,34 @@ static snapshot_node_t *find_snapshot_node(snapshot_node_t *node, const snapshot
/******************************************************************************
* *
+* Paramètres : node = départ du parcours de recherche. *
+* target = instantané recherché. *
+* *
+* Description : Détermine si un instantané est compris dans une branche. *
+* *
+* Retour : Noeud trouvé ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool contain_snapshot_node(const snapshot_node_t *node, const snapshot_node_t *target)
+{
+ bool result; /* Bilan à faire remonter */
+ size_t i; /* Boucle de parcours */
+
+ result = (node == target);
+
+ for (i = 0; i < node->count && !result; i++)
+ result = contain_snapshot_node(node->children[i], target);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
* Paramètres : node = instantané de rattachement. *
* child = instantané à attacher. *
* *
@@ -507,6 +544,8 @@ static void add_snapshot_node(snapshot_node_t *node, snapshot_node_t *child)
node->children[node->count - 1] = child;
+ child->parent = node;
+
src = get_snapshot_info_id(&node->info);
dest = get_snapshot_info_parent_id(&child->info);
@@ -517,6 +556,78 @@ static void add_snapshot_node(snapshot_node_t *node, snapshot_node_t *child)
/******************************************************************************
* *
+* Paramètres : node = instantané à traiter. *
+* rec = précise si les enfants sont à rattacher au parent. *
+* *
+* Description : Fait disparaître un instantané dans une arborescence. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void remove_snapshot_node(snapshot_node_t *node, bool rec)
+{
+ snapshot_node_t *parent; /* Accès direct */
+ size_t i; /* Boucle de parcours */
+
+ parent = node->parent;
+
+ assert(parent != NULL);
+
+ /* Coupe de la branche */
+
+ assert(parent->count > 0);
+
+ if (parent->count == 1)
+ {
+ free(parent->children);
+
+ parent->children = NULL;
+ parent->count = 0;
+
+ }
+
+ else
+ {
+ for (i = 0; i < parent->count; i++)
+ if (parent->children[i] == node)
+ break;
+
+ assert(i < parent->count);
+
+ if ((i + 1) < parent->count)
+ memmove(&parent->children[i], &parent->children[i + 1],
+ (parent->count - i - 1) * sizeof(snapshot_node_t *));
+
+ parent->children = realloc(parent->children, --parent->count * sizeof(snapshot_node_t *));
+
+ }
+
+ /* Rattachement des enfants ? */
+
+ if (!rec)
+ {
+ for (i = 0; i < node->count; i++)
+ add_snapshot_node(parent, node->children[i]);
+
+ free(node->children);
+
+ node->children = NULL;
+ node->count = 0;
+
+ }
+
+ /* Suppression */
+
+ destroy_snapshot_node(node);
+
+}
+
+
+/******************************************************************************
+* *
* Paramètres : node = définition d'instantané à consulter. *
* pbuf = paquet de données où venir inscrire des infos. *
* *
@@ -595,9 +706,10 @@ static void g_db_snapshot_init(GDbSnapshot *snap)
snap->hash = NULL;
snap->nodes = NULL;
-
snap->current = NULL;
+ snap->current_db = NULL;
+
}
@@ -643,6 +755,9 @@ static void g_db_snapshot_finalize(GDbSnapshot *snap)
if (snap->nodes != NULL)
destroy_snapshot_node(snap->nodes);
+ if (snap->current_db != NULL)
+ free(snap->current_db);
+
G_OBJECT_CLASS(g_db_snapshot_parent_class)->finalize(G_OBJECT(snap));
}
@@ -664,12 +779,30 @@ static void g_db_snapshot_finalize(GDbSnapshot *snap)
static GDbSnapshot *g_db_snapshot_new(const char *tmpdir, const char *hash)
{
GDbSnapshot *result; /* Adresse à retourner */
+ int ret; /* Bilan d'une génération */
+ bool status; /* Bilan de la création */
result = g_object_new(G_TYPE_DB_SNAPSHOT, NULL);
result->tmpdir = strdup(tmpdir);
result->hash = strdup(hash);
+ ret = asprintf(&result->current_db, "%s" G_DIR_SEPARATOR_S "%s_current_db.sql", tmpdir, hash);
+
+ status = (ret > 0);
+
+ if (status)
+ {
+ ret = ensure_path_exists(result->current_db);
+ status = (ret != -1);
+ }
+
+ if (!status)
+ {
+ g_object_unref(G_OBJECT(result));
+ result = NULL;
+ }
+
return result;
}
@@ -699,6 +832,7 @@ GDbSnapshot *g_db_snapshot_new_empty(const char *tmpdir, const char *hash, GList
GDbCollection *collec; /* Collection visée manipulée */
result = g_db_snapshot_new(tmpdir, hash);
+ if (result == NULL) goto exit;
result->nodes = create_snapshot_node(NULL, 0, NULL, NULL);
@@ -728,6 +862,11 @@ GDbSnapshot *g_db_snapshot_new_empty(const char *tmpdir, const char *hash, GList
sqlite3_close(db);
+ status = copy_file(result->current_db, result->nodes->path);
+
+ if (!status)
+ goto error;
+
return result;
error_db:
@@ -738,6 +877,8 @@ GDbSnapshot *g_db_snapshot_new_empty(const char *tmpdir, const char *hash, GList
g_object_unref(G_OBJECT(result));
+ exit:
+
return NULL;
}
@@ -776,6 +917,7 @@ GDbSnapshot *g_db_snapshot_new_from_xml(const char *tmpdir, const char *hash, xm
snapshot_id_t node_id; /* Identifiant de noeud courant*/
result = g_db_snapshot_new(tmpdir, hash);
+ if (result == NULL) goto exit;
/* Chargement de l'ensemble des instantanés */
@@ -890,6 +1032,8 @@ GDbSnapshot *g_db_snapshot_new_from_xml(const char *tmpdir, const char *hash, xm
g_object_unref(G_OBJECT(result));
+ exit:
+
return NULL;
}
@@ -931,6 +1075,15 @@ bool g_db_snapshot_fill(GDbSnapshot *snap, struct archive *archive)
if (!_endswith(path, ".db", &dot))
continue;
+ if (strcmp(path, "current.db") == 0)
+ {
+ if (!dump_archive_entry_into_file(archive, entry, snap->current_db))
+ break;
+
+ continue;
+
+ }
+
raw_id = strndup(path, dot - path);
status = init_snapshot_id_from_text(&node_id, raw_id);
@@ -988,6 +1141,7 @@ DBError g_db_snapshot_save(const GDbSnapshot *snap, xmlDocPtr xdoc, xmlXPathCont
DBError result; /* Conclusion à retourner */
const snapshot_id_t *id; /* Identifiant attribué */
bool status; /* Bilan d'un ajout XML */
+ CPError ret; /* Bilan d'une compression */
assert(snap->current != NULL);
@@ -999,8 +1153,32 @@ DBError g_db_snapshot_save(const GDbSnapshot *snap, xmlDocPtr xdoc, xmlXPathCont
result = DBE_XML_ERROR;
else
+ {
+ ret = add_file_into_archive(archive, snap->current_db, "current.db");
+
+ switch (ret)
+ {
+ case CPE_NO_ERROR:
+ break;
+
+ case CPE_SYSTEM_ERROR:
+ result = DBE_SYS_ERROR;
+ goto exit;
+ break;
+
+ case CPE_ARCHIVE_ERROR:
+ result = DBE_ARCHIVE_ERROR;
+ goto exit;
+ break;
+
+ }
+
result = save_snapshot_node(snap->nodes, xdoc, context, archive);
+ }
+
+ exit:
+
return result;
}
@@ -1042,7 +1220,6 @@ bool g_db_snapshot_get_current_id(const GDbSnapshot *snap, snapshot_id_t *id)
/******************************************************************************
* *
* Paramètres : snap = gestionnaire d'instantanés à consulter. *
-* id = identifiant de l'instantané visé. *
* *
* Description : Fournit la base de données correspondant à instanné donné. *
* *
@@ -1052,32 +1229,19 @@ bool g_db_snapshot_get_current_id(const GDbSnapshot *snap, snapshot_id_t *id)
* *
******************************************************************************/
-sqlite3 *g_db_snapshot_get_database(const GDbSnapshot *snap, const snapshot_id_t *id)
+sqlite3 *g_db_snapshot_get_database(const GDbSnapshot *snap)
{
sqlite3 *result; /* Base SQLite à retourner */
- snapshot_node_t *node; /* Instantané trouvé */
int ret; /* Bilan d'un appel */
- node = find_snapshot_node(snap->nodes, id);
-
- if (node == NULL)
- {
- log_variadic_message(LMT_ERROR, _("Snapshot not found for id '%s'"), snapshot_id_as_string(id));
- result = NULL;
- }
+ ret = sqlite3_open(snap->current_db, &result);
- else
+ if (ret != SQLITE_OK)
{
- ret = sqlite3_open(node->path, &result);
-
- if (ret != SQLITE_OK)
- {
- if (result != NULL)
- sqlite3_close(result);
-
- result = NULL;
+ if (result != NULL)
+ sqlite3_close(result);
- }
+ result = NULL;
}
@@ -1112,7 +1276,7 @@ bool g_db_snapshot_pack_all(const GDbSnapshot *snap, packed_buffer *pbuf)
/******************************************************************************
* *
-* Paramètres : snap = gestionnaire d'instantanés à consulter. *
+* Paramètres : snap = gestionnaire d'instantanés à consulter. *
* pbuf = paquet de données où venir puiser les infos. *
* *
* Description : Actualise la désignation d'un instantané donné. *
@@ -1177,7 +1341,7 @@ DBError g_db_snapshot_set_name(const GDbSnapshot *snap, packed_buffer *pbuf)
/******************************************************************************
* *
-* Paramètres : snap = gestionnaire d'instantanés à consulter. *
+* Paramètres : snap = gestionnaire d'instantanés à consulter. *
* pbuf = paquet de données où venir puiser les infos. *
* *
* Description : Actualise la description d'un instantané donné. *
@@ -1238,3 +1402,212 @@ DBError g_db_snapshot_set_desc(const GDbSnapshot *snap, packed_buffer *pbuf)
return result;
}
+
+
+/******************************************************************************
+* *
+* Paramètres : snap = gestionnaire d'instantanés à consulter. *
+* pbuf = paquet de données où venir puiser les infos. *
+* reload = indique un besoin de rechargement de la base. [OUT] *
+* *
+* Description : Restaure un instantané de l'arborescence. *
+* *
+* Retour : Bilan de l'opération sous forme de code d'erreur. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+DBError g_db_snapshot_restore(GDbSnapshot *snap, packed_buffer *pbuf, bool *reload)
+{
+ DBError result; /* Conclusion à retourner */
+ snapshot_id_t id; /* Identifiant d'instantané */
+ bool status; /* Bilan d'une récupération */
+ snapshot_node_t *node; /* Instantané trouvé */
+
+ result = DBE_NONE;
+
+ /* Lecture des arguments */
+
+ setup_empty_snapshot_id(&id);
+
+ status = unpack_snapshot_id(&id, pbuf);
+ if (!status)
+ {
+ result = DBE_BAD_EXCHANGE;
+ goto bad_exchange;
+ }
+
+ /* Traitement */
+
+ node = find_snapshot_node(snap->nodes, &id);
+
+ if (node == NULL)
+ {
+ log_variadic_message(LMT_ERROR, _("Snapshot not found for id '%s'"), snapshot_id_as_string(&id));
+ result = DBE_SNAPSHOT_NOT_FOUND;
+ }
+
+ else if (node == snap->current)
+ {
+ log_simple_message(LMT_WARNING, _("No need to restore the current snapshot"));
+ *reload = false;
+ }
+
+ else
+ {
+ status = copy_file(snap->current_db, node->path);
+
+ if (!status)
+ {
+ log_variadic_message(LMT_ERROR, _("Failed to restore snapshot from '%s' to '%s'"),
+ node->path, snap->current_db);
+ result = DBE_SNAPSHOT_RESTORE_FAILURE;
+ }
+
+ else
+ {
+ snap->current = node;
+ *reload = true;
+ }
+
+ }
+
+ bad_exchange:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : snap = gestionnaire d'instantanés à consulter. *
+* db = base de données courante. *
+* *
+* Description : Crée un nouvel instantanés dans l'arborescence. *
+* *
+* Retour : Bilan de l'opération sous forme de code d'erreur. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+DBError g_db_snapshot_create(GDbSnapshot *snap, sqlite3 *db)
+{
+ DBError result; /* Conclusion à retourner */
+ snapshot_node_t *new; /* Nouvel instantané */
+ bool status; /* Bilan d'une récupération */
+
+ result = DBE_NONE;
+
+ new = create_snapshot_node(NULL, 0, NULL, NULL);
+
+ status = setup_snapshot_node_db_path(new, snap->tmpdir, snap->hash);
+ if (!status)
+ {
+ result = DBE_SYS_ERROR;
+ destroy_snapshot_node(new);
+ goto error;
+ }
+
+ status = backup_db(db, new->path);
+ if (!status)
+ {
+ result = DBE_SYS_ERROR;
+ destroy_snapshot_node(new);
+ goto error;
+ }
+
+ add_snapshot_node(snap->current, new);
+
+ snap->current = new;
+
+ error:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : snap = gestionnaire d'instantanés à consulter. *
+* pbuf = paquet de données où venir puiser les infos. *
+* changed = indique si l'instantané courant a bougé. [OUT] *
+* *
+* Description : Supprime un instantané dans l'arborescence. *
+* *
+* Retour : Bilan de l'opération sous forme de code d'erreur. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+DBError g_db_snapshot_remove(GDbSnapshot *snap, packed_buffer *pbuf, bool *changed)
+{
+ DBError result; /* Conclusion à retourner */
+ snapshot_id_t id; /* Identifiant d'instantané */
+ bool status; /* Bilan d'une récupération */
+ uint8_t tmp; /* Stockage temporaire */
+ bool rec; /* Indicateur de récursivité */
+ snapshot_node_t *node; /* Instantané trouvé */
+
+ result = DBE_NONE;
+
+ *changed = false;
+
+ /* Lecture des arguments */
+
+ setup_empty_snapshot_id(&id);
+
+ status = unpack_snapshot_id(&id, pbuf);
+ if (!status)
+ {
+ result = DBE_BAD_EXCHANGE;
+ goto bad_exchange;
+ }
+
+ status = extract_packed_buffer(pbuf, &tmp, sizeof(uint8_t), false);
+ if (!status)
+ {
+ result = DBE_BAD_EXCHANGE;
+ goto bad_exchange;
+ }
+
+ rec = (tmp == 0x1);
+
+ /* Traitement */
+
+ node = find_snapshot_node(snap->nodes, &id);
+
+ if (node == NULL)
+ {
+ log_variadic_message(LMT_ERROR, _("Snapshot not found for id '%s'"), snapshot_id_as_string(&id));
+ result = DBE_SNAPSHOT_NOT_FOUND;
+ }
+
+ else if (node == snap->nodes)
+ {
+ log_simple_message(LMT_ERROR, _("Root snapshot can not be removed"));
+ result = DBE_SNAPSHOT_ROOT_REMOVAL;
+ }
+
+ else
+ {
+ /* Réassignation éventuelle */
+ if ((rec && contain_snapshot_node(node, snap->current)) || (!rec && node == snap->current))
+ {
+ snap->current = node->parent;
+ *changed = true;
+ }
+
+ remove_snapshot_node(node, rec);
+
+ }
+
+ bad_exchange:
+
+ return result;
+
+}
diff --git a/src/analysis/db/snapshot.h b/src/analysis/db/snapshot.h
index 457e8c2..8737f8c 100644
--- a/src/analysis/db/snapshot.h
+++ b/src/analysis/db/snapshot.h
@@ -72,7 +72,7 @@ DBError g_db_snapshot_save(const GDbSnapshot *, xmlDocPtr, xmlXPathContextPtr, s
bool g_db_snapshot_get_current_id(const GDbSnapshot *, snapshot_id_t *);
/* Fournit la base de données correspondant à instanné donné. */
-sqlite3 *g_db_snapshot_get_database(const GDbSnapshot *, const snapshot_id_t *);
+sqlite3 *g_db_snapshot_get_database(const GDbSnapshot *);
/* Collecte les descriptions de l'ensemble des instantanés. */
bool g_db_snapshot_pack_all(const GDbSnapshot *, packed_buffer *);
@@ -83,6 +83,15 @@ DBError g_db_snapshot_set_name(const GDbSnapshot *, packed_buffer *);
/* Actualise la description d'un instantané donné. */
DBError g_db_snapshot_set_desc(const GDbSnapshot *, packed_buffer *);
+/* Restaure un instantané de l'arborescence. */
+DBError g_db_snapshot_restore(GDbSnapshot *, packed_buffer *, bool *);
+
+/* Crée un nouvel instantanés dans l'arborescence. */
+DBError g_db_snapshot_create(GDbSnapshot *, sqlite3 *);
+
+/* Supprime un instantané dans l'arborescence. */
+DBError g_db_snapshot_remove(GDbSnapshot *, packed_buffer *, bool *);
+
#endif /* _ANALYSIS_DB_SNAPSHOT_H */
diff --git a/src/common/packed.c b/src/common/packed.c
index a10155d..0c4a222 100644
--- a/src/common/packed.c
+++ b/src/common/packed.c
@@ -97,8 +97,16 @@ void reset_packed_buffer(packed_buffer *pbuf)
void exit_packed_buffer(packed_buffer *pbuf)
{
+#ifndef NDEBUG
+ assert(pbuf->data != NULL);
+#endif
+
free(pbuf->data);
+#ifndef NDEBUG
+ pbuf->data = NULL;
+#endif
+
}
diff --git a/src/common/sqlite.c b/src/common/sqlite.c
index 3a94623..41f99ac 100644
--- a/src/common/sqlite.c
+++ b/src/common/sqlite.c
@@ -583,3 +583,63 @@ bool update_db_values(sqlite3 *db, const char *table, const bound_value *values,
return result;
}
+
+
+/******************************************************************************
+* *
+* Paramètres : db = base de données à sauvegarder. *
+* filename = fichier de destination pour la sauvegarde. *
+* *
+* Description : Effectue une copie d'une base de données en cours d'usage. *
+* *
+* Retour : Bilan de l'exécution de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool backup_db(sqlite3 *db, const char *filename)
+{
+ bool result; /* Conclusion à faire remonter */
+ sqlite3 *copy; /* Copie de la base de données */
+ int ret; /* Bilan d'un appel à SQLite */
+ sqlite3_backup *backup; /* Gestionnaire de sauvegarde */
+
+ /**
+ * Cf. https://www.sqlite.org/backup.html
+ */
+
+ ret = sqlite3_open(filename, &copy);
+
+ if (ret != SQLITE_OK)
+ {
+ if (copy != NULL)
+ sqlite3_close(copy);
+
+ result = false;
+
+ goto exit;
+
+ }
+
+ backup = sqlite3_backup_init(copy, "main", db, "main");
+
+ if (backup == NULL)
+ result = false;
+
+ else
+ {
+ sqlite3_backup_step(backup, -1);
+ sqlite3_backup_finish(backup);
+
+ ret = sqlite3_errcode(copy);
+
+ result = (ret == SQLITE_OK);
+
+ }
+
+ exit:
+
+ return result;
+
+}
diff --git a/src/common/sqlite.h b/src/common/sqlite.h
index 99de12d..addc16d 100644
--- a/src/common/sqlite.h
+++ b/src/common/sqlite.h
@@ -87,6 +87,9 @@ bool store_db_values(sqlite3 *, const char *, const bound_value *, size_t);
/* Met à jour une série de valeurs dans une base de données. */
bool update_db_values(sqlite3 *, const char *, const bound_value *, size_t, const bound_value *, size_t);
+/* Effectue une copie d'une base de données en cours d'usage. */
+bool backup_db(sqlite3 *, const char *);
+
#endif /* _COMMON_SQLITE_H */
diff --git a/src/gui/dialogs/snapshots.c b/src/gui/dialogs/snapshots.c
index 1fcea67..9f256c6 100644
--- a/src/gui/dialogs/snapshots.c
+++ b/src/gui/dialogs/snapshots.c
@@ -720,22 +720,11 @@ static void restore_old_snapshot(GtkToolButton *button, GtkBuilder *builder)
static void create_new_snapshot(GtkToolButton *button, GtkBuilder *builder)
{
- const char *raw; /* Identifiant brut */
- snapshot_id_t id; /* Identifiant utilisable */
- bool status; /* Bilan d'une conversion */
GHubClient *client; /* Cible des interactions */
- raw = g_object_get_data(G_OBJECT(builder), "selected");
-
- status = init_snapshot_id_from_text(&id, raw);
-
- if (status)
- {
- client = G_HUB_CLIENT(g_object_get_data(G_OBJECT(builder), "client"));
-
- g_hub_client_create_snapshot(client, &id);
+ client = G_HUB_CLIENT(g_object_get_data(G_OBJECT(builder), "client"));
- }
+ g_hub_client_create_snapshot(client);
}