3 * db.c: load and save in-memory database from/to file
4 * copyright (c) 2007 Ben Stewart, Emil Mikulic.
6 * You may use, modify and redistribute this file under the terms of the
7 * GNU General Public License version 2. (see COPYING.GPL)
10 #define _GNU_SOURCE 1 /* for O_NOFOLLOW in Linux */
12 #include <sys/types.h>
20 #include <netinet/in.h> /* for ntohs() and friends */
26 static const unsigned char export_file_header
[] = {0xDA, 0x31, 0x41, 0x59};
27 static const unsigned char export_tag_hosts_ver1
[] = {0xDA, 'H', 'S', 0x01};
28 static const unsigned char export_tag_graph_ver1
[] = {0xDA, 'G', 'R', 0x01};
31 static inline uint64_t
34 /* this is __bswap64 from:
35 * $FreeBSD: src/sys/i386/include/endian.h,v 1.41$
37 return ((_x
>> 56) | ((_x
>> 40) & 0xff00) | ((_x
>> 24) & 0xff0000) |
38 ((_x
>> 8) & 0xff000000) | ((_x
<< 8) & ((uint64_t)0xff << 32)) |
39 ((_x
<< 24) & ((uint64_t)0xff << 40)) |
40 ((_x
<< 40) & ((uint64_t)0xff << 48)) | ((_x
<< 56)));
45 hton64(const uint64_t ho
)
47 if (ntohs(0x1234) == 0x1234) return ho
;
48 else return swap64(ho
);
52 ntoh64(const uint64_t no
)
60 static const char str
[] = { 0x79,0x74,0x69,0x63,0x6b,0x72,0x65,0x6a };
63 assert(sizeof(no
) == 8);
66 assert(ho
== 8751735851613054314ULL);
67 assert(hton64(ntoh64(no
)) == no
);
70 /* ---------------------------------------------------------------------------
71 * Read-from-file helpers. They all return 0 on failure, and 1 on success.
77 off_t ofs
= lseek(fd
, 0, SEEK_CUR
);
79 err(1, "lseek(0, SEEK_CUR) failed");
80 return (unsigned int)ofs
;
83 /* Read <len> bytes from <fd>, warn() and return 0 on failure,
84 * or return 1 for success.
87 readn(const int fd
, void *dest
, const size_t len
)
91 numread
= read(fd
, dest
, len
);
92 if (numread
== (ssize_t
)len
) return 1;
95 warn("at pos %u: couldn't read %d bytes", xtell(fd
), (int)len
);
97 warnx("at pos %u: tried to read %d bytes, got %d",
98 xtell(fd
), (int)len
, (int)numread
);
104 read8(const int fd
, uint8_t *dest
)
106 assert(sizeof(*dest
) == 1);
107 return readn(fd
, dest
, sizeof(*dest
));
110 /* Read a byte and compare it to the expected data.
111 * Returns 0 on failure or mismatch, 1 on success.
114 expect8(const int fd
, uint8_t expecting
)
118 assert(sizeof(tmp
) == 1);
119 if (!readn(fd
, &tmp
, sizeof(tmp
))) return 0;
120 if (tmp
== expecting
) return 1;
122 warnx("at pos %u: expecting 0x%02x, got 0x%02x",
123 xtell(fd
)-1, expecting
, tmp
);
127 /* Read a network order uint16_t from a file
128 * and store it in host order in memory.
131 read16(const int fd
, uint16_t *dest
)
135 assert(sizeof(tmp
) == 2);
136 if (!read(fd
, &tmp
, sizeof(tmp
))) return 0;
141 /* Read a network order uint32_t from a file
142 * and store it in host order in memory.
145 read32(const int fd
, uint32_t *dest
)
149 assert(sizeof(tmp
) == 4);
150 if (!read(fd
, &tmp
, sizeof(tmp
))) return 0;
155 /* Read an in_addr_t from a file. Addresses are always stored in network
156 * order, both in the file and in the host's memory (FIXME: is that right?)
159 readaddr(const int fd
, in_addr_t
*dest
)
161 assert(sizeof(*dest
) == 4);
162 return readn(fd
, dest
, sizeof(*dest
));
165 /* Read a network order uint64_t from a file
166 * and store it in host order in memory.
169 read64(const int fd
, uint64_t *dest
)
173 assert(sizeof(tmp
) == 8);
174 if (!read(fd
, &tmp
, sizeof(tmp
))) return 0;
179 /* ---------------------------------------------------------------------------
180 * Write-to-file helpers. They all return 0 on failure, and 1 on success.
183 /* Write <len> bytes to <fd>, warn() and return 0 on failure,
184 * or return 1 for success.
187 writen(const int fd
, const void *dest
, const size_t len
)
191 numwr
= write(fd
, dest
, len
);
192 if (numwr
== (ssize_t
)len
) return 1;
195 warn("couldn't write %d bytes", (int)len
);
197 warnx("tried to write %d bytes but wrote %d",
198 (int)len
, (int)numwr
);
203 write8(const int fd
, const uint8_t i
)
205 assert(sizeof(i
) == 1);
206 return writen(fd
, &i
, sizeof(i
));
209 /* Given a uint16_t in host order, write it to a file in network order.
212 write16(const int fd
, const uint16_t i
)
214 uint16_t tmp
= htons(i
);
215 assert(sizeof(tmp
) == 2);
216 return writen(fd
, &tmp
, sizeof(tmp
));
219 /* Given a uint32_t in host order, write it to a file in network order.
222 write32(const int fd
, const uint32_t i
)
224 uint32_t tmp
= htonl(i
);
225 assert(sizeof(tmp
) == 4);
226 return writen(fd
, &tmp
, sizeof(tmp
));
229 /* Given a uint64_t in host order, write it to a file in network order.
232 write64(const int fd
, const uint64_t i
)
234 uint64_t tmp
= hton64(i
);
235 assert(sizeof(tmp
) == 8);
236 return writen(fd
, &tmp
, sizeof(tmp
));
240 /* Write an in_addr_t to a file. Addresses are always stored in network
241 * order, both in the file and in the host's memory (FIXME: is that right?)
244 writeaddr(const int fd
, const in_addr_t addr
)
246 assert(sizeof(addr
) == 4);
247 return writen(fd
, &addr
, sizeof(addr
));
250 /* ---------------------------------------------------------------------------
251 * db import/export code follows.
254 /* Check that the global file header is correct / supported. */
256 read_file_header(const int fd
, const uint8_t expected
[4])
260 if (!readn(fd
, got
, sizeof(got
))) return 0;
262 /* Check the header data */
263 if (memcmp(got
, expected
, sizeof(got
)) != 0) {
265 "expecting %02x%02x%02x%02x, got %02x%02x%02x%02x",
266 expected
[0], expected
[1], expected
[2], expected
[3],
267 got
[0], got
[1], got
[2], got
[3]);
273 /* Returns 0 on failure, 1 on success. */
275 db_import_from_fd(const int fd
)
277 if (!read_file_header(fd
, export_file_header
)) return 0;
278 if (!read_file_header(fd
, export_tag_hosts_ver1
)) return 0;
279 if (!hosts_db_import(fd
)) return 0;
280 if (!read_file_header(fd
, export_tag_graph_ver1
)) return 0;
281 if (!graph_import(fd
)) return 0;
286 db_import(const char *filename
)
288 int fd
= open(filename
, O_RDONLY
| O_NOFOLLOW
);
290 warn("can't import from \"%s\"", filename
);
293 if (!db_import_from_fd(fd
)) {
294 warnx("import failed");
295 /* don't stay in an inconsistent state: */
302 /* Returns 0 on failure, 1 on success. */
304 db_export_to_fd(const int fd
)
306 if (!writen(fd
, export_file_header
, sizeof(export_file_header
)))
308 if (!writen(fd
, export_tag_hosts_ver1
, sizeof(export_tag_hosts_ver1
)))
310 if (!hosts_db_export(fd
))
312 if (!writen(fd
, export_tag_graph_ver1
, sizeof(export_tag_graph_ver1
)))
314 if (!graph_export(fd
))
320 db_export(const char *filename
)
322 int fd
= open(filename
, O_WRONLY
| O_CREAT
| O_NOFOLLOW
| O_TRUNC
, 0600);
324 warn("can't export to \"%s\"", filename
);
327 verbosef("exporting db to file \"%s\"", filename
);
328 if (!db_export_to_fd(fd
))
329 warnx("export failed");
331 verbosef("export successful");
333 /* FIXME: should write to another filename and use the rename() syscall to
334 * atomically update the output file on success
339 /* vim:set ts=3 sw=3 tw=78 et: */