mosquitto/src/persist.c

871 lines
22 KiB
C
Raw Normal View History

2014-05-07 22:27:00 +00:00
/*
Copyright (c) 2010-2015 Roger Light <roger@atchoo.org>
2014-05-07 22:27:00 +00:00
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
2015-04-29 20:37:47 +00:00
#include "config.h"
2014-05-07 22:27:00 +00:00
#ifdef WITH_PERSISTENCE
#ifndef WIN32
#include <arpa/inet.h>
#endif
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
2014-09-14 17:08:09 +00:00
#include <time.h>
2014-05-07 22:27:00 +00:00
2015-04-29 20:37:47 +00:00
#include "mosquitto_broker.h"
#include "memory_mosq.h"
#include "persist.h"
#include "time_mosq.h"
2014-05-07 22:27:00 +00:00
#include "util_mosq.h"
static uint32_t db_version;
2015-05-16 14:24:24 +00:00
static int persist__restore_sub(struct mosquitto_db *db, const char *client_id, const char *sub, int qos);
2014-05-07 22:27:00 +00:00
2015-05-16 14:24:24 +00:00
static struct mosquitto *persist__find_or_add_context(struct mosquitto_db *db, const char *client_id, uint16_t last_mid)
2014-05-07 22:27:00 +00:00
{
struct mosquitto *context;
context = NULL;
HASH_FIND(hh_id, db->contexts_by_id, client_id, strlen(client_id), context);
2014-05-07 22:27:00 +00:00
if(!context){
2015-05-16 14:24:24 +00:00
context = context__init(db, -1);
2014-05-07 22:27:00 +00:00
if(!context) return NULL;
context->id = mosquitto__strdup(client_id);
2014-06-29 22:16:10 +00:00
if(!context->id){
mosquitto__free(context);
return NULL;
}
2014-05-07 22:27:00 +00:00
context->clean_session = false;
HASH_ADD_KEYPTR(hh_id, db->contexts_by_id, context->id, strlen(context->id), context);
2014-05-07 22:27:00 +00:00
}
if(last_mid){
context->last_mid = last_mid;
}
return context;
}
static int persist__client_messages_write(struct mosquitto_db *db, FILE *db_fptr, struct mosquitto *context, struct mosquitto_client_msg *queue)
2014-05-07 22:27:00 +00:00
{
uint32_t length;
dbid_t i64temp;
uint16_t i16temp, slen;
uint8_t i8temp;
struct mosquitto_client_msg *cmsg;
assert(db);
assert(db_fptr);
assert(context);
cmsg = queue;
2014-05-07 22:27:00 +00:00
while(cmsg){
slen = strlen(context->id);
length = htonl(sizeof(dbid_t) + sizeof(uint16_t) + sizeof(uint8_t) +
sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint8_t) +
sizeof(uint8_t) + 2+slen);
i16temp = htons(DB_CHUNK_CLIENT_MSG);
write_e(db_fptr, &i16temp, sizeof(uint16_t));
write_e(db_fptr, &length, sizeof(uint32_t));
i16temp = htons(slen);
write_e(db_fptr, &i16temp, sizeof(uint16_t));
write_e(db_fptr, context->id, slen);
i64temp = cmsg->store->db_id;
write_e(db_fptr, &i64temp, sizeof(dbid_t));
i16temp = htons(cmsg->mid);
write_e(db_fptr, &i16temp, sizeof(uint16_t));
i8temp = (uint8_t )cmsg->qos;
write_e(db_fptr, &i8temp, sizeof(uint8_t));
i8temp = (uint8_t )cmsg->retain;
write_e(db_fptr, &i8temp, sizeof(uint8_t));
i8temp = (uint8_t )cmsg->direction;
write_e(db_fptr, &i8temp, sizeof(uint8_t));
i8temp = (uint8_t )cmsg->state;
write_e(db_fptr, &i8temp, sizeof(uint8_t));
i8temp = (uint8_t )cmsg->dup;
write_e(db_fptr, &i8temp, sizeof(uint8_t));
cmsg = cmsg->next;
}
return MOSQ_ERR_SUCCESS;
error:
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno));
2014-05-07 22:27:00 +00:00
return 1;
}
2015-05-16 14:24:24 +00:00
static int persist__message_store_write(struct mosquitto_db *db, FILE *db_fptr)
2014-05-07 22:27:00 +00:00
{
uint32_t length;
dbid_t i64temp;
uint32_t i32temp;
uint16_t i16temp, slen, tlen;
2014-05-07 22:27:00 +00:00
uint8_t i8temp;
struct mosquitto_msg_store *stored;
2014-05-07 22:27:00 +00:00
bool force_no_retain;
assert(db);
assert(db_fptr);
stored = db->msg_store;
while(stored){
if(stored->topic && !strncmp(stored->topic, "$SYS", 4)){
if(stored->ref_count == 1 && stored->dest_id_count == 0){
/* $SYS messages that are only retained shouldn't be persisted. */
stored = stored->next;
continue;
}
2014-05-07 22:27:00 +00:00
/* Don't save $SYS messages as retained otherwise they can give
* misleading information when reloaded. They should still be saved
* because a disconnected durable client may have them in their
* queue. */
force_no_retain = true;
}else{
force_no_retain = false;
}
if(stored->topic){
tlen = strlen(stored->topic);
}else{
tlen = 0;
}
2014-05-07 22:27:00 +00:00
length = htonl(sizeof(dbid_t) + 2+strlen(stored->source_id) +
sizeof(uint16_t) + sizeof(uint16_t) +
2+tlen + sizeof(uint32_t) +
stored->payloadlen + sizeof(uint8_t) + sizeof(uint8_t));
2014-05-07 22:27:00 +00:00
i16temp = htons(DB_CHUNK_MSG_STORE);
write_e(db_fptr, &i16temp, sizeof(uint16_t));
write_e(db_fptr, &length, sizeof(uint32_t));
i64temp = stored->db_id;
write_e(db_fptr, &i64temp, sizeof(dbid_t));
slen = strlen(stored->source_id);
i16temp = htons(slen);
write_e(db_fptr, &i16temp, sizeof(uint16_t));
if(slen){
write_e(db_fptr, stored->source_id, slen);
}
i16temp = htons(stored->source_mid);
write_e(db_fptr, &i16temp, sizeof(uint16_t));
i16temp = htons(stored->mid);
2014-05-07 22:27:00 +00:00
write_e(db_fptr, &i16temp, sizeof(uint16_t));
i16temp = htons(tlen);
2014-05-07 22:27:00 +00:00
write_e(db_fptr, &i16temp, sizeof(uint16_t));
if(tlen){
write_e(db_fptr, stored->topic, tlen);
}
2014-05-07 22:27:00 +00:00
i8temp = (uint8_t )stored->qos;
2014-05-07 22:27:00 +00:00
write_e(db_fptr, &i8temp, sizeof(uint8_t));
if(force_no_retain == false){
i8temp = (uint8_t )stored->retain;
2014-05-07 22:27:00 +00:00
}else{
i8temp = 0;
}
write_e(db_fptr, &i8temp, sizeof(uint8_t));
i32temp = htonl(stored->payloadlen);
2014-05-07 22:27:00 +00:00
write_e(db_fptr, &i32temp, sizeof(uint32_t));
if(stored->payloadlen){
2015-04-05 09:02:16 +00:00
write_e(db_fptr, UHPA_ACCESS_PAYLOAD(stored), (unsigned int)stored->payloadlen);
2014-05-07 22:27:00 +00:00
}
stored = stored->next;
2014-05-07 22:27:00 +00:00
}
return MOSQ_ERR_SUCCESS;
error:
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno));
2014-05-07 22:27:00 +00:00
return 1;
}
2015-05-16 14:24:24 +00:00
static int persist__client_write(struct mosquitto_db *db, FILE *db_fptr)
2014-05-07 22:27:00 +00:00
{
struct mosquitto *context, *ctxt_tmp;
2014-05-07 22:27:00 +00:00
uint16_t i16temp, slen;
uint32_t length;
time_t disconnect_t;
2014-05-07 22:27:00 +00:00
assert(db);
assert(db_fptr);
HASH_ITER(hh_id, db->contexts_by_id, context, ctxt_tmp){
2014-05-07 22:27:00 +00:00
if(context && context->clean_session == false){
length = htonl(2+strlen(context->id) + sizeof(uint16_t) + sizeof(time_t));
i16temp = htons(DB_CHUNK_CLIENT);
write_e(db_fptr, &i16temp, sizeof(uint16_t));
write_e(db_fptr, &length, sizeof(uint32_t));
slen = strlen(context->id);
i16temp = htons(slen);
write_e(db_fptr, &i16temp, sizeof(uint16_t));
write_e(db_fptr, context->id, slen);
i16temp = htons(context->last_mid);
write_e(db_fptr, &i16temp, sizeof(uint16_t));
if(context->disconnect_t){
disconnect_t = context->disconnect_t;
}else{
disconnect_t = time(NULL);
}
write_e(db_fptr, &disconnect_t, sizeof(time_t));
2014-05-07 22:27:00 +00:00
if(persist__client_messages_write(db, db_fptr, context, context->inflight_msgs)) return 1;
if(persist__client_messages_write(db, db_fptr, context, context->queued_msgs)) return 1;
2014-05-07 22:27:00 +00:00
}
}
return MOSQ_ERR_SUCCESS;
error:
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno));
2014-05-07 22:27:00 +00:00
return 1;
}
2016-06-21 22:33:58 +00:00
static int persist__subs_retain_write(struct mosquitto_db *db, FILE *db_fptr, struct mosquitto__subhier *node, const char *topic, int level)
2014-05-07 22:27:00 +00:00
{
struct mosquitto__subhier *subhier;
struct mosquitto__subleaf *sub;
2014-05-07 22:27:00 +00:00
char *thistopic;
uint32_t length;
uint16_t i16temp;
dbid_t i64temp;
size_t slen;
2015-04-04 20:15:27 +00:00
slen = strlen(topic) + node->topic_len + 2;
thistopic = mosquitto__malloc(sizeof(char)*slen);
2014-05-07 22:27:00 +00:00
if(!thistopic) return MOSQ_ERR_NOMEM;
if(level > 1 || strlen(topic)){
2015-04-04 20:15:27 +00:00
snprintf(thistopic, slen, "%s/%s", topic, UHPA_ACCESS_TOPIC(node));
2014-05-07 22:27:00 +00:00
}else{
2015-04-04 20:15:27 +00:00
snprintf(thistopic, slen, "%s", UHPA_ACCESS_TOPIC(node));
2014-05-07 22:27:00 +00:00
}
sub = node->subs;
while(sub){
if(sub->context->clean_session == false){
length = htonl(2+strlen(sub->context->id) + 2+strlen(thistopic) + sizeof(uint8_t));
i16temp = htons(DB_CHUNK_SUB);
write_e(db_fptr, &i16temp, sizeof(uint16_t));
write_e(db_fptr, &length, sizeof(uint32_t));
slen = strlen(sub->context->id);
i16temp = htons(slen);
write_e(db_fptr, &i16temp, sizeof(uint16_t));
write_e(db_fptr, sub->context->id, slen);
slen = strlen(thistopic);
i16temp = htons(slen);
write_e(db_fptr, &i16temp, sizeof(uint16_t));
write_e(db_fptr, thistopic, slen);
write_e(db_fptr, &sub->qos, sizeof(uint8_t));
}
sub = sub->next;
}
if(node->retained){
if(strncmp(node->retained->topic, "$SYS", 4)){
2014-05-07 22:27:00 +00:00
/* Don't save $SYS messages. */
length = htonl(sizeof(dbid_t));
i16temp = htons(DB_CHUNK_RETAIN);
write_e(db_fptr, &i16temp, sizeof(uint16_t));
write_e(db_fptr, &length, sizeof(uint32_t));
i64temp = node->retained->db_id;
write_e(db_fptr, &i64temp, sizeof(dbid_t));
}
}
subhier = node->children;
while(subhier){
2016-06-21 22:33:58 +00:00
persist__subs_retain_write(db, db_fptr, subhier, thistopic, level+1);
2014-05-07 22:27:00 +00:00
subhier = subhier->next;
}
mosquitto__free(thistopic);
2014-05-07 22:27:00 +00:00
return MOSQ_ERR_SUCCESS;
error:
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno));
2014-05-07 22:27:00 +00:00
return 1;
}
2015-05-16 14:24:24 +00:00
static int persist__subs_retain_write_all(struct mosquitto_db *db, FILE *db_fptr)
2014-05-07 22:27:00 +00:00
{
struct mosquitto__subhier *subhier;
2014-05-07 22:27:00 +00:00
subhier = db->subs.children;
while(subhier){
if(subhier->children){
2016-06-21 22:33:58 +00:00
persist__subs_retain_write(db, db_fptr, subhier->children, "", 0);
}
2014-05-07 22:27:00 +00:00
subhier = subhier->next;
}
return MOSQ_ERR_SUCCESS;
}
2015-05-16 14:24:24 +00:00
int persist__backup(struct mosquitto_db *db, bool shutdown)
2014-05-07 22:27:00 +00:00
{
int rc = 0;
FILE *db_fptr = NULL;
uint32_t db_version_w = htonl(MOSQ_DB_VERSION);
uint32_t crc = htonl(0);
dbid_t i64temp;
uint32_t i32temp;
uint16_t i16temp;
uint8_t i8temp;
char err[256];
char *outfile = NULL;
int len;
if(!db || !db->config || !db->config->persistence_filepath) return MOSQ_ERR_INVAL;
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_INFO, "Saving in-memory database to %s.", db->config->persistence_filepath);
2014-05-07 22:27:00 +00:00
len = strlen(db->config->persistence_filepath)+5;
outfile = mosquitto__malloc(len+1);
2014-05-07 22:27:00 +00:00
if(!outfile){
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_INFO, "Error saving in-memory database, out of memory.");
2014-05-07 22:27:00 +00:00
return MOSQ_ERR_NOMEM;
}
snprintf(outfile, len, "%s.new", db->config->persistence_filepath);
2015-01-14 21:43:39 +00:00
outfile[len] = '\0';
db_fptr = mosquitto__fopen(outfile, "wb");
2014-05-07 22:27:00 +00:00
if(db_fptr == NULL){
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_INFO, "Error saving in-memory database, unable to open %s for writing.", outfile);
2014-05-07 22:27:00 +00:00
goto error;
}
/* Header */
write_e(db_fptr, magic, 15);
write_e(db_fptr, &crc, sizeof(uint32_t));
write_e(db_fptr, &db_version_w, sizeof(uint32_t));
/* DB config */
i16temp = htons(DB_CHUNK_CFG);
write_e(db_fptr, &i16temp, sizeof(uint16_t));
/* chunk length */
i32temp = htonl(sizeof(dbid_t) + sizeof(uint8_t) + sizeof(uint8_t));
write_e(db_fptr, &i32temp, sizeof(uint32_t));
/* db written at broker shutdown or not */
i8temp = shutdown;
write_e(db_fptr, &i8temp, sizeof(uint8_t));
i8temp = sizeof(dbid_t);
write_e(db_fptr, &i8temp, sizeof(uint8_t));
/* last db mid */
i64temp = db->last_db_id;
write_e(db_fptr, &i64temp, sizeof(dbid_t));
2015-05-16 14:24:24 +00:00
if(persist__message_store_write(db, db_fptr)){
2014-05-07 22:27:00 +00:00
goto error;
}
2015-05-16 14:24:24 +00:00
persist__client_write(db, db_fptr);
persist__subs_retain_write_all(db, db_fptr);
2014-05-07 22:27:00 +00:00
fclose(db_fptr);
#ifdef WIN32
if(remove(db->config->persistence_filepath) != 0){
if(errno != ENOENT){
goto error;
}
}
#endif
2014-05-07 22:27:00 +00:00
if(rename(outfile, db->config->persistence_filepath) != 0){
goto error;
}
mosquitto__free(outfile);
2014-05-07 22:27:00 +00:00
outfile = NULL;
return rc;
error:
mosquitto__free(outfile);
2014-05-07 22:27:00 +00:00
strerror_r(errno, err, 256);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err);
2014-05-07 22:27:00 +00:00
if(db_fptr) fclose(db_fptr);
return 1;
}
2015-05-16 14:24:24 +00:00
static int persist__client_msg_restore(struct mosquitto_db *db, const char *client_id, uint16_t mid, uint8_t qos, uint8_t retain, uint8_t direction, uint8_t state, uint8_t dup, uint64_t store_id)
2014-05-07 22:27:00 +00:00
{
struct mosquitto_client_msg *cmsg;
struct mosquitto_client_msg **msgs, **last_msg;
struct mosquitto_msg_store_load *load;
2014-05-07 22:27:00 +00:00
struct mosquitto *context;
cmsg = mosquitto__malloc(sizeof(struct mosquitto_client_msg));
2014-05-07 22:27:00 +00:00
if(!cmsg){
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
2014-05-07 22:27:00 +00:00
return MOSQ_ERR_NOMEM;
}
2014-11-17 21:58:53 +00:00
cmsg->next = NULL;
2014-05-07 22:27:00 +00:00
cmsg->store = NULL;
cmsg->mid = mid;
cmsg->qos = qos;
cmsg->retain = retain;
2014-11-17 21:58:53 +00:00
cmsg->timestamp = 0;
2014-05-07 22:27:00 +00:00
cmsg->direction = direction;
cmsg->state = state;
cmsg->dup = dup;
HASH_FIND(hh, db->msg_store_load, &store_id, sizeof(dbid_t), load);
if(!load){
mosquitto__free(cmsg);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error restoring persistent database, message store corrupt.");
2014-05-07 22:27:00 +00:00
return 1;
}
cmsg->store = load->store;
cmsg->store->ref_count++;
2015-05-16 14:24:24 +00:00
context = persist__find_or_add_context(db, client_id, 0);
2014-05-07 22:27:00 +00:00
if(!context){
mosquitto__free(cmsg);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error restoring persistent database, message store corrupt.");
2014-05-07 22:27:00 +00:00
return 1;
}
if (state == mosq_ms_queued){
msgs = &(context->queued_msgs);
last_msg = &(context->last_queued_msg);
}else{
msgs = &(context->inflight_msgs);
last_msg = &(context->last_inflight_msg);
}
if(*msgs){
(*last_msg)->next = cmsg;
2014-05-07 22:27:00 +00:00
}else{
*msgs = cmsg;
2014-05-07 22:27:00 +00:00
}
*last_msg = cmsg;
2014-05-07 22:27:00 +00:00
return MOSQ_ERR_SUCCESS;
}
2015-05-16 14:24:24 +00:00
static int persist__client_chunk_restore(struct mosquitto_db *db, FILE *db_fptr)
2014-05-07 22:27:00 +00:00
{
uint16_t i16temp, slen, last_mid;
char *client_id = NULL;
int rc = 0;
struct mosquitto *context;
time_t disconnect_t;
read_e(db_fptr, &i16temp, sizeof(uint16_t));
slen = ntohs(i16temp);
if(!slen){
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Corrupt persistent database.");
2014-05-07 22:27:00 +00:00
fclose(db_fptr);
return 1;
}
client_id = mosquitto__malloc(slen+1);
2014-05-07 22:27:00 +00:00
if(!client_id){
fclose(db_fptr);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
2014-05-07 22:27:00 +00:00
return MOSQ_ERR_NOMEM;
}
read_e(db_fptr, client_id, slen);
2014-11-17 21:58:53 +00:00
client_id[slen] = '\0';
2014-05-07 22:27:00 +00:00
read_e(db_fptr, &i16temp, sizeof(uint16_t));
last_mid = ntohs(i16temp);
if(db_version == 2){
disconnect_t = time(NULL);
2014-05-07 22:27:00 +00:00
}else{
2015-02-17 23:23:13 +00:00
read_e(db_fptr, &disconnect_t, sizeof(time_t));
2014-05-07 22:27:00 +00:00
}
2015-05-16 14:24:24 +00:00
context = persist__find_or_add_context(db, client_id, last_mid);
2014-05-07 22:27:00 +00:00
if(context){
context->disconnect_t = disconnect_t;
}else{
rc = 1;
}
mosquitto__free(client_id);
2014-05-07 22:27:00 +00:00
return rc;
error:
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno));
2014-05-07 22:27:00 +00:00
fclose(db_fptr);
mosquitto__free(client_id);
2014-05-07 22:27:00 +00:00
return 1;
}
2015-05-16 14:24:24 +00:00
static int persist__client_msg_chunk_restore(struct mosquitto_db *db, FILE *db_fptr)
2014-05-07 22:27:00 +00:00
{
dbid_t i64temp, store_id;
uint16_t i16temp, slen, mid;
uint8_t qos, retain, direction, state, dup;
char *client_id = NULL;
int rc;
char err[256];
read_e(db_fptr, &i16temp, sizeof(uint16_t));
slen = ntohs(i16temp);
if(!slen){
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Corrupt persistent database.");
2014-05-07 22:27:00 +00:00
fclose(db_fptr);
return 1;
}
client_id = mosquitto__malloc(slen+1);
2014-05-07 22:27:00 +00:00
if(!client_id){
fclose(db_fptr);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
2014-05-07 22:27:00 +00:00
return MOSQ_ERR_NOMEM;
}
read_e(db_fptr, client_id, slen);
2014-11-17 21:58:53 +00:00
client_id[slen] = '\0';
2014-05-07 22:27:00 +00:00
read_e(db_fptr, &i64temp, sizeof(dbid_t));
store_id = i64temp;
read_e(db_fptr, &i16temp, sizeof(uint16_t));
mid = ntohs(i16temp);
read_e(db_fptr, &qos, sizeof(uint8_t));
read_e(db_fptr, &retain, sizeof(uint8_t));
read_e(db_fptr, &direction, sizeof(uint8_t));
read_e(db_fptr, &state, sizeof(uint8_t));
read_e(db_fptr, &dup, sizeof(uint8_t));
2015-05-16 14:24:24 +00:00
rc = persist__client_msg_restore(db, client_id, mid, qos, retain, direction, state, dup, store_id);
mosquitto__free(client_id);
2014-05-07 22:27:00 +00:00
return rc;
error:
strerror_r(errno, err, 256);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err);
2014-05-07 22:27:00 +00:00
fclose(db_fptr);
mosquitto__free(client_id);
2014-05-07 22:27:00 +00:00
return 1;
}
2015-05-16 14:24:24 +00:00
static int persist__msg_store_chunk_restore(struct mosquitto_db *db, FILE *db_fptr)
2014-05-07 22:27:00 +00:00
{
dbid_t i64temp, store_id;
uint32_t i32temp, payloadlen = 0;
2014-05-07 22:27:00 +00:00
uint16_t i16temp, slen, source_mid;
uint8_t qos, retain;
mosquitto__payload_uhpa payload;
2014-05-07 22:27:00 +00:00
char *source_id = NULL;
char *topic = NULL;
int rc = 0;
struct mosquitto_msg_store *stored = NULL;
struct mosquitto_msg_store_load *load;
2014-05-07 22:27:00 +00:00
char err[256];
payload.ptr = NULL;
load = mosquitto__malloc(sizeof(struct mosquitto_msg_store_load));
if(!load){
fclose(db_fptr);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
2014-05-07 22:27:00 +00:00
read_e(db_fptr, &i64temp, sizeof(dbid_t));
store_id = i64temp;
read_e(db_fptr, &i16temp, sizeof(uint16_t));
slen = ntohs(i16temp);
if(slen){
source_id = mosquitto__malloc(slen+1);
2014-05-07 22:27:00 +00:00
if(!source_id){
mosquitto__free(load);
2014-05-07 22:27:00 +00:00
fclose(db_fptr);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
2014-05-07 22:27:00 +00:00
return MOSQ_ERR_NOMEM;
}
read_e(db_fptr, source_id, slen);
2014-11-17 21:58:53 +00:00
source_id[slen] = '\0';
2014-05-07 22:27:00 +00:00
}
read_e(db_fptr, &i16temp, sizeof(uint16_t));
source_mid = ntohs(i16temp);
/* This is the mid - don't need it */
read_e(db_fptr, &i16temp, sizeof(uint16_t));
read_e(db_fptr, &i16temp, sizeof(uint16_t));
slen = ntohs(i16temp);
if(slen){
topic = mosquitto__malloc(slen+1);
2014-05-07 22:27:00 +00:00
if(!topic){
mosquitto__free(load);
2014-05-07 22:27:00 +00:00
fclose(db_fptr);
mosquitto__free(source_id);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
2014-05-07 22:27:00 +00:00
return MOSQ_ERR_NOMEM;
}
read_e(db_fptr, topic, slen);
2014-11-17 21:58:53 +00:00
topic[slen] = '\0';
2014-05-07 22:27:00 +00:00
}else{
topic = NULL;
2014-05-07 22:27:00 +00:00
}
read_e(db_fptr, &qos, sizeof(uint8_t));
read_e(db_fptr, &retain, sizeof(uint8_t));
read_e(db_fptr, &i32temp, sizeof(uint32_t));
payloadlen = ntohl(i32temp);
if(payloadlen){
if(UHPA_ALLOC(payload, payloadlen) == 0){
mosquitto__free(load);
2014-05-07 22:27:00 +00:00
fclose(db_fptr);
mosquitto__free(source_id);
mosquitto__free(topic);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
2014-05-07 22:27:00 +00:00
return MOSQ_ERR_NOMEM;
}
read_e(db_fptr, UHPA_ACCESS(payload, payloadlen), payloadlen);
2014-05-07 22:27:00 +00:00
}
rc = db__message_store(db, source_id, source_mid, topic, qos, payloadlen, &payload, retain, &stored, store_id);
mosquitto__free(source_id);
if(rc == MOSQ_ERR_SUCCESS){
load->db_id = stored->db_id;
load->store = stored;
HASH_ADD(hh, db->msg_store_load, db_id, sizeof(dbid_t), load);
return MOSQ_ERR_SUCCESS;
}else{
mosquitto__free(load);
fclose(db_fptr);
mosquitto__free(topic);
UHPA_FREE(payload, payloadlen);
return rc;
}
2014-05-07 22:27:00 +00:00
error:
strerror_r(errno, err, 256);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err);
2014-05-07 22:27:00 +00:00
fclose(db_fptr);
mosquitto__free(source_id);
mosquitto__free(topic);
UHPA_FREE(payload, payloadlen);
2014-05-07 22:27:00 +00:00
return 1;
}
2015-05-16 14:24:24 +00:00
static int persist__retain_chunk_restore(struct mosquitto_db *db, FILE *db_fptr)
2014-05-07 22:27:00 +00:00
{
dbid_t i64temp, store_id;
struct mosquitto_msg_store_load *load;
2014-05-07 22:27:00 +00:00
char err[256];
if(fread(&i64temp, sizeof(dbid_t), 1, db_fptr) != 1){
strerror_r(errno, err, 256);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err);
2014-05-07 22:27:00 +00:00
fclose(db_fptr);
return 1;
}
store_id = i64temp;
HASH_FIND(hh, db->msg_store_load, &store_id, sizeof(dbid_t), load);
if(load){
2015-05-18 08:29:22 +00:00
sub__messages_queue(db, NULL, load->store->topic, load->store->qos, load->store->retain, &load->store);
}else{
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Corrupt database whilst restoring a retained message.");
return MOSQ_ERR_INVAL;
2014-05-07 22:27:00 +00:00
}
return MOSQ_ERR_SUCCESS;
}
2015-05-16 14:24:24 +00:00
static int persist__sub_chunk_restore(struct mosquitto_db *db, FILE *db_fptr)
2014-05-07 22:27:00 +00:00
{
uint16_t i16temp, slen;
uint8_t qos;
char *client_id;
char *topic;
int rc = 0;
char err[256];
read_e(db_fptr, &i16temp, sizeof(uint16_t));
slen = ntohs(i16temp);
client_id = mosquitto__malloc(slen+1);
2014-05-07 22:27:00 +00:00
if(!client_id){
fclose(db_fptr);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
2014-05-07 22:27:00 +00:00
return MOSQ_ERR_NOMEM;
}
read_e(db_fptr, client_id, slen);
2014-11-17 21:58:53 +00:00
client_id[slen] = '\0';
2014-05-07 22:27:00 +00:00
read_e(db_fptr, &i16temp, sizeof(uint16_t));
slen = ntohs(i16temp);
topic = mosquitto__malloc(slen+1);
2014-05-07 22:27:00 +00:00
if(!topic){
fclose(db_fptr);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
mosquitto__free(client_id);
2014-05-07 22:27:00 +00:00
return MOSQ_ERR_NOMEM;
}
read_e(db_fptr, topic, slen);
2014-11-17 21:58:53 +00:00
topic[slen] = '\0';
2014-05-07 22:27:00 +00:00
read_e(db_fptr, &qos, sizeof(uint8_t));
2015-05-16 14:24:24 +00:00
if(persist__restore_sub(db, client_id, topic, qos)){
2014-05-07 22:27:00 +00:00
rc = 1;
}
mosquitto__free(client_id);
mosquitto__free(topic);
2014-05-07 22:27:00 +00:00
return rc;
error:
strerror_r(errno, err, 256);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err);
2014-05-07 22:27:00 +00:00
fclose(db_fptr);
return 1;
}
2015-05-16 14:24:24 +00:00
int persist__restore(struct mosquitto_db *db)
2014-05-07 22:27:00 +00:00
{
FILE *fptr;
char header[15];
int rc = 0;
uint32_t crc;
dbid_t i64temp;
uint32_t i32temp, length;
uint16_t i16temp, chunk;
uint8_t i8temp;
ssize_t rlen;
char err[256];
struct mosquitto_msg_store_load *load, *load_tmp;
2014-05-07 22:27:00 +00:00
assert(db);
assert(db->config);
assert(db->config->persistence_filepath);
db->msg_store_load = NULL;
fptr = mosquitto__fopen(db->config->persistence_filepath, "rb");
2014-05-07 22:27:00 +00:00
if(fptr == NULL) return MOSQ_ERR_SUCCESS;
read_e(fptr, &header, 15);
if(!memcmp(header, magic, 15)){
// Restore DB as normal
read_e(fptr, &crc, sizeof(uint32_t));
read_e(fptr, &i32temp, sizeof(uint32_t));
db_version = ntohl(i32temp);
/* IMPORTANT - this is where compatibility checks are made.
* Is your DB change still compatible with previous versions?
*/
if(db_version > MOSQ_DB_VERSION && db_version != 0){
if(db_version == 2){
/* Addition of disconnect_t to client chunk in v3. */
}else{
fclose(fptr);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unsupported persistent database format version %d (need version %d).", db_version, MOSQ_DB_VERSION);
2014-05-07 22:27:00 +00:00
return 1;
}
}
while(rlen = fread(&i16temp, sizeof(uint16_t), 1, fptr), rlen == 1){
chunk = ntohs(i16temp);
read_e(fptr, &i32temp, sizeof(uint32_t));
length = ntohl(i32temp);
switch(chunk){
case DB_CHUNK_CFG:
read_e(fptr, &i8temp, sizeof(uint8_t)); // shutdown
read_e(fptr, &i8temp, sizeof(uint8_t)); // sizeof(dbid_t)
if(i8temp != sizeof(dbid_t)){
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Incompatible database configuration (dbid size is %d bytes, expected %lu)",
2014-05-07 22:27:00 +00:00
i8temp, (unsigned long)sizeof(dbid_t));
fclose(fptr);
return 1;
}
read_e(fptr, &i64temp, sizeof(dbid_t));
db->last_db_id = i64temp;
break;
case DB_CHUNK_MSG_STORE:
2015-05-16 14:24:24 +00:00
if(persist__msg_store_chunk_restore(db, fptr)) return 1;
2014-05-07 22:27:00 +00:00
break;
case DB_CHUNK_CLIENT_MSG:
2015-05-16 14:24:24 +00:00
if(persist__client_msg_chunk_restore(db, fptr)) return 1;
2014-05-07 22:27:00 +00:00
break;
case DB_CHUNK_RETAIN:
2015-05-16 14:24:24 +00:00
if(persist__retain_chunk_restore(db, fptr)) return 1;
2014-05-07 22:27:00 +00:00
break;
case DB_CHUNK_SUB:
2015-05-16 14:24:24 +00:00
if(persist__sub_chunk_restore(db, fptr)) return 1;
2014-05-07 22:27:00 +00:00
break;
case DB_CHUNK_CLIENT:
2015-05-16 14:24:24 +00:00
if(persist__client_chunk_restore(db, fptr)) return 1;
2014-05-07 22:27:00 +00:00
break;
default:
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Unsupported chunk \"%d\" in persistent database file. Ignoring.", chunk);
2014-05-07 22:27:00 +00:00
fseek(fptr, length, SEEK_CUR);
break;
}
}
if(rlen < 0) goto error;
}else{
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to restore persistent database. Unrecognised file format.");
2014-05-07 22:27:00 +00:00
rc = 1;
}
fclose(fptr);
HASH_ITER(hh, db->msg_store_load, load, load_tmp){
HASH_DELETE(hh, db->msg_store_load, load);
mosquitto__free(load);
}
2014-05-07 22:27:00 +00:00
return rc;
error:
strerror_r(errno, err, 256);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err);
2014-05-07 22:27:00 +00:00
if(fptr) fclose(fptr);
return 1;
}
2015-05-16 14:24:24 +00:00
static int persist__restore_sub(struct mosquitto_db *db, const char *client_id, const char *sub, int qos)
2014-05-07 22:27:00 +00:00
{
struct mosquitto *context;
assert(db);
assert(client_id);
assert(sub);
2015-05-16 14:24:24 +00:00
context = persist__find_or_add_context(db, client_id, 0);
2014-05-07 22:27:00 +00:00
if(!context) return 1;
2015-05-18 08:29:22 +00:00
return sub__add(db, context, sub, qos, &db->subs);
2014-05-07 22:27:00 +00:00
}
#endif