]> git.ipfire.org Git - people/ms/libloc.git/blame - src/writer.c
Write networks to the database
[people/ms/libloc.git] / src / writer.c
CommitLineData
c182393f
MT
1/*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2017 IPFire Development Team <info@ipfire.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15*/
16
c182393f
MT
17#include <endian.h>
18#include <errno.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <time.h>
23
24#include <loc/format.h>
3b5f4af2 25#include <loc/network.h>
c182393f
MT
26#include <loc/writer.h>
27
28#include "libloc-private.h"
29#include "as.h"
30
31struct loc_writer {
32 struct loc_ctx* ctx;
33 int refcount;
34
35 struct loc_stringpool* pool;
36 off_t vendor;
37 off_t description;
38
39 struct loc_as** as;
40 size_t as_count;
3b5f4af2
MT
41
42 struct loc_network_tree* networks;
c182393f
MT
43};
44
45LOC_EXPORT int loc_writer_new(struct loc_ctx* ctx, struct loc_writer** writer) {
46 struct loc_writer* w = calloc(1, sizeof(*w));
47 if (!w)
48 return -ENOMEM;
49
50 w->ctx = loc_ref(ctx);
51 w->refcount = 1;
52
0e974d4b 53 int r = loc_stringpool_new(ctx, &w->pool);
c182393f
MT
54 if (r) {
55 loc_writer_unref(w);
56 return r;
57 }
58
3b5f4af2
MT
59 // Initialize the network tree
60 r = loc_network_tree_new(ctx, &w->networks);
61 if (r) {
62 loc_writer_unref(w);
63 return r;
64 }
65
c182393f
MT
66 *writer = w;
67 return 0;
68}
69
70LOC_EXPORT struct loc_writer* loc_writer_ref(struct loc_writer* writer) {
71 writer->refcount++;
72
73 return writer;
74}
75
76static void loc_writer_free(struct loc_writer* writer) {
77 DEBUG(writer->ctx, "Releasing writer at %p\n", writer);
78
79 // Unref all AS
80 for (unsigned int i = 0; i < writer->as_count; i++) {
81 loc_as_unref(writer->as[i]);
82 }
83
3b5f4af2
MT
84 // Release network tree
85 if (writer->networks)
86 loc_network_tree_unref(writer->networks);
87
c182393f
MT
88 // Unref the string pool
89 loc_stringpool_unref(writer->pool);
90
91 loc_unref(writer->ctx);
92 free(writer);
93}
94
95LOC_EXPORT struct loc_writer* loc_writer_unref(struct loc_writer* writer) {
96 if (--writer->refcount > 0)
97 return writer;
98
99 loc_writer_free(writer);
100
101 return NULL;
102}
103
104LOC_EXPORT const char* loc_writer_get_vendor(struct loc_writer* writer) {
105 return loc_stringpool_get(writer->pool, writer->vendor);
106}
107
108LOC_EXPORT int loc_writer_set_vendor(struct loc_writer* writer, const char* vendor) {
109 // Add the string to the string pool
110 off_t offset = loc_stringpool_add(writer->pool, vendor);
111 if (offset < 0)
112 return offset;
113
114 writer->vendor = offset;
115 return 0;
116}
117
118LOC_EXPORT const char* loc_writer_get_description(struct loc_writer* writer) {
119 return loc_stringpool_get(writer->pool, writer->description);
120}
121
122LOC_EXPORT int loc_writer_set_description(struct loc_writer* writer, const char* description) {
123 // Add the string to the string pool
124 off_t offset = loc_stringpool_add(writer->pool, description);
125 if (offset < 0)
126 return offset;
127
128 writer->description = offset;
129 return 0;
130}
131
132static int __loc_as_cmp(const void* as1, const void* as2) {
133 return loc_as_cmp(*(struct loc_as**)as1, *(struct loc_as**)as2);
134}
135
136LOC_EXPORT int loc_writer_add_as(struct loc_writer* writer, struct loc_as** as, uint32_t number) {
137 int r = loc_as_new(writer->ctx, writer->pool, as, number);
138 if (r)
139 return r;
140
141 // We have a new AS to add
142 writer->as_count++;
143
144 // Make space
145 writer->as = realloc(writer->as, sizeof(*writer->as) * writer->as_count);
146 if (!writer->as)
147 return -ENOMEM;
148
149 // Add as last element
150 writer->as[writer->as_count - 1] = loc_as_ref(*as);
151
152 // Sort everything
153 qsort(writer->as, writer->as_count, sizeof(*writer->as), __loc_as_cmp);
154
155 return 0;
156}
157
3b5f4af2
MT
158LOC_EXPORT int loc_writer_add_network(struct loc_writer* writer, struct loc_network** network, const char* string) {
159 int r;
160
161 // Create a new network object
162 r = loc_network_new_from_string(writer->ctx, network, string);
163 if (r)
164 return r;
165
166 // Add it to the local tree
167 return loc_network_tree_add_network(writer->networks, *network);
168}
169
c182393f
MT
170static void make_magic(struct loc_writer* writer, struct loc_database_magic* magic) {
171 // Copy magic bytes
172 for (unsigned int i = 0; i < strlen(LOC_DATABASE_MAGIC); i++)
173 magic->magic[i] = LOC_DATABASE_MAGIC[i];
174
175 // Set version
0676cd80 176 magic->version = htobe16(LOC_DATABASE_VERSION);
c182393f
MT
177}
178
179static void align_page_boundary(off_t* offset, FILE* f) {
180 // Move to next page boundary
181 while (*offset % LOC_DATABASE_PAGE_SIZE > 0)
182 *offset += fwrite("", 1, 1, f);
183}
184
185static int loc_database_write_pool(struct loc_writer* writer,
186 struct loc_database_header_v0* header, off_t* offset, FILE* f) {
187 // Save the offset of the pool section
188 DEBUG(writer->ctx, "Pool starts at %jd bytes\n", *offset);
0676cd80 189 header->pool_offset = htobe32(*offset);
c182393f
MT
190
191 // Write the pool
192 size_t pool_length = loc_stringpool_write(writer->pool, f);
193 *offset += pool_length;
194
195 DEBUG(writer->ctx, "Pool has a length of %zu bytes\n", pool_length);
0676cd80 196 header->pool_length = htobe32(pool_length);
c182393f
MT
197
198 return 0;
199}
200
201static int loc_database_write_as_section(struct loc_writer* writer,
202 struct loc_database_header_v0* header, off_t* offset, FILE* f) {
203 DEBUG(writer->ctx, "AS section starts at %jd bytes\n", *offset);
0676cd80 204 header->as_offset = htobe32(*offset);
c182393f
MT
205
206 size_t as_length = 0;
207
208 struct loc_database_as_v0 as;
209 for (unsigned int i = 0; i < writer->as_count; i++) {
210 // Convert AS into database format
211 loc_as_to_database_v0(writer->as[i], &as);
212
213 // Write to disk
214 offset += fwrite(&as, 1, sizeof(as), f);
215 as_length += sizeof(as);
216 }
217
218 DEBUG(writer->ctx, "AS section has a length of %zu bytes\n", as_length);
0676cd80 219 header->as_length = htobe32(as_length);
c182393f
MT
220
221 return 0;
222}
223
f3e02bc5
MT
224static int loc_database_write_network_section(struct loc_network* network, void* data) {
225 FILE* f = (FILE*)data;
226
227 struct loc_database_network_v0 n;
228
229 int r = loc_network_to_database_v0(network, &n);
230 if (r)
231 return r;
232
233 fwrite(&n, 1, sizeof(n), f);
234
235 return 0;
236}
237
238static int loc_database_write_networks_section(struct loc_writer* writer,
239 struct loc_database_header_v0* header, off_t* offset, FILE* f) {
240 DEBUG(writer->ctx, "Networks section starts at %jd bytes\n", *offset);
241 header->networks_offset = htobe32(*offset);
242
243 size_t networks_length = sizeof(struct loc_database_network_v0)
244 * loc_network_tree_count_networks(writer->networks);
245 offset += networks_length;
246
247 int r = loc_network_tree_walk(writer->networks, NULL, loc_database_write_network_section, f);
248 if (r)
249 return r;
250
251 header->networks_length = htobe32(networks_length);
252
253 return 0;
254}
255
c182393f
MT
256LOC_EXPORT int loc_writer_write(struct loc_writer* writer, FILE* f) {
257 struct loc_database_magic magic;
258 make_magic(writer, &magic);
259
260 // Make the header
261 struct loc_database_header_v0 header;
0676cd80
MT
262 header.vendor = htobe32(writer->vendor);
263 header.description = htobe32(writer->description);
c182393f
MT
264
265 time_t now = time(NULL);
266 header.created_at = htobe64(now);
267
268 int r;
269 off_t offset = 0;
270
271 // Start writing at the beginning of the file
272 r = fseek(f, 0, SEEK_SET);
273 if (r)
274 return r;
275
276 // Write the magic
277 offset += fwrite(&magic, 1, sizeof(magic), f);
278
279 // Skip the space we need to write the header later
280 r = fseek(f, sizeof(header), SEEK_CUR);
281 if (r) {
282 DEBUG(writer->ctx, "Could not seek to position after header\n");
283 return r;
284 }
285 offset += sizeof(header);
286
287 align_page_boundary(&offset, f);
288
289 // Write all ASes
290 r = loc_database_write_as_section(writer, &header, &offset, f);
291 if (r)
292 return r;
293
294 align_page_boundary(&offset, f);
295
f3e02bc5
MT
296 // Write all networks
297 r = loc_database_write_networks_section(writer, &header, &offset, f);
298 if (r)
299 return r;
300
301 align_page_boundary(&offset, f);
302
c182393f
MT
303 // Write pool
304 r = loc_database_write_pool(writer, &header, &offset, f);
305 if (r)
306 return r;
307
308 // Write the header
309 r = fseek(f, sizeof(magic), SEEK_SET);
310 if (r)
311 return r;
312
313 offset += fwrite(&header, 1, sizeof(header), f);
314
315 return 0;
316}