int mqtt3_db_restore(mosquitto_db *db) { FILE *fptr; char header[15]; int rc = 0; uint32_t crc, db_version; dbid_t i64temp; uint32_t i32temp, length; uint16_t i16temp, chunk; uint8_t i8temp; ssize_t rlen; assert(db); assert(db->config); assert(db->config->persistence_filepath); fptr = fopen(db->config->persistence_filepath, "rb"); 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){ fclose(fptr); _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Unsupported persistent database format version %d (need version %d).", db_version, MOSQ_DB_VERSION); return 1; } while(rlen = fread(&i16temp, 1, sizeof(uint16_t), fptr), rlen == sizeof(uint16_t)){ 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)){ _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Incompatible database configuration (dbid size is %d bytes, expected %d)", i8temp, 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: if(_db_msg_store_chunk_restore(db, fptr)) return 1; break; case DB_CHUNK_CLIENT_MSG: if(_db_client_msg_chunk_restore(db, fptr)) return 1; break; case DB_CHUNK_RETAIN: if(_db_retain_chunk_restore(db, fptr)) return 1; break; case DB_CHUNK_SUB: if(_db_sub_chunk_restore(db, fptr)) return 1; break; case DB_CHUNK_CLIENT: if(_db_client_chunk_restore(db, fptr)) return 1; break; default: _mosquitto_log_printf(NULL, MOSQ_LOG_WARNING, "Warning: Unsupported chunk \"%d\" in persistent database file. Ignoring.", chunk); fseek(fptr, length, SEEK_CUR); break; } } if(rlen < 0) goto error; }else{ _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Unable to restore persistent database. Unrecognised file format."); rc = 1; } fclose(fptr); return rc; error: _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno)); if(fptr) fclose(fptr); return 1; }
int main(int argc, char *argv[]) { FILE *fd; 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; struct mosquitto_db db; char *filename; long cfg_count = 0; long msg_store_count = 0; long client_msg_count = 0; long retain_count = 0; long sub_count = 0; long client_count = 0; if(argc == 2){ filename = argv[1]; }else if(argc == 3 && !strcmp(argv[1], "--stats")){ stats = 1; filename = argv[2]; }else{ fprintf(stderr, "Usage: db_dump [--stats] <mosquitto db filename>\n"); return 1; } memset(&db, 0, sizeof(struct mosquitto_db)); fd = fopen(filename, "rb"); if(!fd) return 0; read_e(fd, &header, 15); if(!memcmp(header, magic, 15)){ if(!stats) printf("Mosquitto DB dump\n"); // Restore DB as normal read_e(fd, &crc, sizeof(uint32_t)); if(!stats) printf("CRC: %d\n", crc); read_e(fd, &i32temp, sizeof(uint32_t)); db_version = ntohl(i32temp); if(!stats) printf("DB version: %d\n", db_version); while(rlen = fread(&i16temp, sizeof(uint16_t), 1, fd), rlen == 1){ chunk = ntohs(i16temp); read_e(fd, &i32temp, sizeof(uint32_t)); length = ntohl(i32temp); switch(chunk){ case DB_CHUNK_CFG: cfg_count++; if(!stats) printf("DB_CHUNK_CFG:\n"); if(!stats) printf("\tLength: %d\n", length); read_e(fd, &i8temp, sizeof(uint8_t)); // shutdown if(!stats) printf("\tShutdown: %d\n", i8temp); read_e(fd, &i8temp, sizeof(uint8_t)); // sizeof(dbid_t) if(!stats) printf("\tDB ID size: %d\n", i8temp); if(i8temp != sizeof(dbid_t)){ fprintf(stderr, "Error: Incompatible database configuration (dbid size is %d bytes, expected %ld)", i8temp, sizeof(dbid_t)); fclose(fd); return 1; } read_e(fd, &i64temp, sizeof(dbid_t)); if(!stats) printf("\tLast DB ID: %ld\n", (long)i64temp); break; case DB_CHUNK_MSG_STORE: msg_store_count++; if(!stats) printf("DB_CHUNK_MSG_STORE:\n"); if(!stats) printf("\tLength: %d\n", length); if(_db_msg_store_chunk_restore(&db, fd)) return 1; break; case DB_CHUNK_CLIENT_MSG: client_msg_count++; if(!stats) printf("DB_CHUNK_CLIENT_MSG:\n"); if(!stats) printf("\tLength: %d\n", length); if(_db_client_msg_chunk_restore(&db, fd)) return 1; break; case DB_CHUNK_RETAIN: retain_count++; if(!stats) printf("DB_CHUNK_RETAIN:\n"); if(!stats) printf("\tLength: %d\n", length); if(_db_retain_chunk_restore(&db, fd)) return 1; break; case DB_CHUNK_SUB: sub_count++; if(!stats) printf("DB_CHUNK_SUB:\n"); if(!stats) printf("\tLength: %d\n", length); if(_db_sub_chunk_restore(&db, fd)) return 1; break; case DB_CHUNK_CLIENT: client_count++; if(!stats) printf("DB_CHUNK_CLIENT:\n"); if(!stats) printf("\tLength: %d\n", length); if(_db_client_chunk_restore(&db, fd)) return 1; break; default: fprintf(stderr, "Warning: Unsupported chunk \"%d\" in persistent database file. Ignoring.", chunk); fseek(fd, length, SEEK_CUR); break; } } if(rlen < 0) goto error; }else{ fprintf(stderr, "Error: Unrecognised file format."); rc = 1; } fclose(fd); if(stats){ printf("DB_CHUNK_CFG: %ld\n", cfg_count); printf("DB_CHUNK_MSG_STORE: %ld\n", msg_store_count); printf("DB_CHUNK_CLIENT_MSG: %ld\n", client_msg_count); printf("DB_CHUNK_RETAIN: %ld\n", retain_count); printf("DB_CHUNK_SUB: %ld\n", sub_count); printf("DB_CHUNK_CLIENT: %ld\n", client_count); } return rc; error: fprintf(stderr, "Error: %s.", strerror(errno)); if(fd >= 0) fclose(fd); return 1; }