e7eed20a0806493bd11c988692ff9d3d0267bdc0
1 /*****************************************************************************
2 * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
3 *****************************************************************************
4 * rrd_open.c Open an RRD File
5 *****************************************************************************
6 * $Id$
7 * $Log$
8 * Revision 1.10 2004/05/26 22:11:12 oetiker
9 * reduce compiler warnings. Many small fixes. -- Mike Slifcak <slif@bellsouth.net>
10 *
11 * Revision 1.9 2003/04/29 21:56:49 oetiker
12 * readline in rrd_open.c reads the file in 8 KB blocks, and calls realloc for
13 * each block. realloc is very slow in Mac OS X for huge blocks, e.g. when
14 * restoring databases from huge xml files. This patch finds the size of the
15 * file, and starts out with malloc'ing the full size.
16 * -- Peter Speck <speck@ruc.dk>
17 *
18 * Revision 1.8 2003/04/11 19:43:44 oetiker
19 * New special value COUNT which allows calculations based on the position of a
20 * value within a data set. Bug fix in rrd_rpncalc.c. PREV returned erroneus
21 * value for the second value. Bug fix in rrd_restore.c. Bug causing seek error
22 * when accesing an RRD restored from an xml that holds an RRD version <3.
23 * -- Ruben Justo <ruben@ainek.com>
24 *
25 * Revision 1.7 2003/03/31 21:22:12 oetiker
26 * enables RRDtool updates with microsecond or in case of windows millisecond
27 * precision. This is needed to reduce time measurement error when archive step
28 * is small. (<30s) -- Sasha Mikheev <sasha@avalon-net.co.il>
29 *
30 * Revision 1.6 2003/02/13 07:05:27 oetiker
31 * Find attached the patch I promised to send to you. Please note that there
32 * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c
33 * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This
34 * library is identical to librrd, but it contains support code for per-thread
35 * global variables currently used for error information only. This is similar
36 * to how errno per-thread variables are implemented. librrd_th must be linked
37 * alongside of libpthred
38 *
39 * There is also a new file "THREADS", holding some documentation.
40 *
41 * -- Peter Stamfest <peter@stamfest.at>
42 *
43 * Revision 1.5 2002/06/20 00:21:03 jake
44 * More Win32 build changes; thanks to Kerry Calvert.
45 *
46 * Revision 1.4 2002/02/01 20:34:49 oetiker
47 * fixed version number and date/time
48 *
49 * Revision 1.3 2001/03/04 13:01:55 oetiker
50 * Aberrant Behavior Detection support. A brief overview added to rrdtool.pod.
51 * Major updates to rrd_update.c, rrd_create.c. Minor update to other core files.
52 * This is backwards compatible! But new files using the Aberrant stuff are not readable
53 * by old rrdtool versions. See http://cricket.sourceforge.net/aberrant/rrd_hw.htm
54 * -- Jake Brutlag <jakeb@corp.webtv.net>
55 *
56 * Revision 1.2 2001/03/04 10:29:20 oetiker
57 * fixed filedescriptor leak
58 * -- Mike Franusich <mike@franusich.com>
59 *
60 * Revision 1.1.1.1 2001/02/25 22:25:05 oetiker
61 * checkin
62 *
63 *****************************************************************************/
65 #include "rrd_tool.h"
66 #include "unused.h"
67 #define MEMBLK 8192
69 /* open a database file, return its header and a open filehandle */
70 /* positioned to the first cdp in the first rra */
72 rrd_file_t *rrd_open(
73 const char *const file_name,
74 rrd_t *rrd,
75 unsigned rdwr)
76 {
77 int flags = 0;
78 mode_t mode = S_IRUSR;
79 int version, prot = PROT_READ;
80 off_t offset = 0;
81 char *data;
82 struct stat statb;
83 rrd_file_t *rrd_file = malloc(sizeof(rrd_file_t));
85 if (rrd_file == NULL) {
86 rrd_set_error("allocating rrd_file descriptor for '%s'", file_name);
87 return NULL;
88 }
89 memset(rrd_file, 0, sizeof(rrd_file_t));
90 rrd_init(rrd);
91 if (rdwr == RRD_READWRITE) {
92 mode |= S_IWUSR;
93 prot |= PROT_WRITE;
94 } else if (rdwr == RRD_CREAT) {
95 mode |= S_IWUSR;
96 prot |= PROT_WRITE;
97 flags |= (O_CREAT | O_TRUNC);
98 }
99 #ifdef O_NONBLOCK
100 flags |= O_NONBLOCK;
101 #endif
103 if ((rrd_file->fd = open(file_name, flags, mode)) < 0) {
104 rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
105 return NULL;
106 }
108 /* ???: length = lseek(rrd_file->fd, 0, SEEK_END); */
109 /* ??? locking the whole area of the file may overdo it a bit, does it? */
110 if ((fstat(rrd_file->fd, &statb)) < 0) {
111 rrd_set_error("fstat '%s': %s", file_name, rrd_strerror(errno));
112 goto out_close;
113 }
114 rrd_file->file_len = statb.st_size;
116 #ifdef HAVE_POSIX_FADVISE
117 /* In general we need no read-ahead when dealing with rrd_files.
118 When we stop reading, it is highly unlikely that we start up again.
119 In this manner we actually save time and diskaccess (and buffer cache).
120 Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */
121 if (0 != posix_fadvise(rrd_file->fd, 0, 0, POSIX_FADV_RANDOM)) {
122 rrd_set_error("setting POSIX_FADV_RANDOM on '%s': %s", file_name,
123 rrd_strerror(errno));
124 goto out_close;
125 }
126 #endif
128 /*
129 if (rdwr == RRD_READWRITE)
130 {
131 if (setvbuf((rrd_file->fd),NULL,_IONBF,2)) {
132 rrd_set_error("failed to disable the stream buffer\n");
133 return (-1);
134 }
135 }
136 */
137 data = mmap(0, rrd_file->file_len, prot, MAP_SHARED,
138 rrd_file->fd, offset);
140 /* lets see if the first read worked */
141 if (data == MAP_FAILED) {
142 rrd_set_error("error mmaping file '%s'", file_name);
143 goto out_close;
144 }
145 rrd_file->file_start = data;
146 #ifdef USE_MADVISE
147 if (rrd == NULL) { /*XXX: currently not used! */
148 /* We will read everything in a moment (copying) */
149 madvise(data, rrd_file->file_len, MADV_WILLNEED | MADV_SEQUENTIAL);
150 goto out_done;
151 }
152 /* We do not need to read anything in for the moment */
153 madvise(data, rrd_file->file_len, MADV_DONTNEED);
154 #endif
156 #ifdef USE_MADVISE
157 /* the stat_head will be needed soonish, so hint accordingly */
158 madvise(data + offset, sizeof(stat_head_t), MADV_WILLNEED);
159 #endif
161 rrd->stat_head = (stat_head_t *) (data + offset);
162 offset += sizeof(stat_head_t);
164 /* lets do some test if we are on track ... */
165 if (memcmp(rrd->stat_head->cookie, RRD_COOKIE, sizeof(RRD_COOKIE)) != 0) {
166 rrd_set_error("'%s' is not an RRD file", file_name);
167 goto out_nullify_head;
168 }
170 if (rrd->stat_head->float_cookie != FLOAT_COOKIE) {
171 rrd_set_error("This RRD was created on other architecture");
172 goto out_nullify_head;
173 }
175 version = atoi(rrd->stat_head->version);
177 if (version > atoi(RRD_VERSION)) {
178 rrd_set_error("can't handle RRD file version %s",
179 rrd->stat_head->version);
180 goto out_nullify_head;
181 }
182 #ifdef USE_MADVISE
183 /* the ds_def will be needed soonish, so hint accordingly */
184 madvise(data + offset, sizeof(ds_def_t) * rrd->stat_head->ds_cnt,
185 MADV_WILLNEED);
186 #endif
187 rrd->ds_def = (ds_def_t *) (data + offset);
188 offset += sizeof(ds_def_t) * rrd->stat_head->ds_cnt;
190 #ifdef USE_MADVISE
191 /* the rra_def will be needed soonish, so hint accordingly */
192 madvise(data + offset, sizeof(rra_def_t) * rrd->stat_head->rra_cnt,
193 MADV_WILLNEED);
194 #endif
195 rrd->rra_def = (rra_def_t *) (data + offset);
196 offset += sizeof(rra_def_t) * rrd->stat_head->rra_cnt;
198 /* handle different format for the live_head */
199 if (version < 3) {
200 rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
201 if (rrd->live_head == NULL) {
202 rrd_set_error("live_head_t malloc");
203 goto out_close;
204 }
205 memmove(&rrd->live_head->last_up, data + offset, sizeof(long));
206 rrd->live_head->last_up_usec = 0;
207 } else {
208 #ifdef USE_MADVISE
209 /* the live_head will be needed soonish, so hint accordingly */
210 madvise(data + offset, sizeof(live_head_t), MADV_WILLNEED);
211 #endif
212 rrd->live_head = (live_head_t *) (data + offset);
213 offset += sizeof(live_head_t);
214 }
215 // This doesn't look like it needs madvise
216 rrd->pdp_prep = (pdp_prep_t *) (data + offset);
217 offset += sizeof(pdp_prep_t) * rrd->stat_head->ds_cnt;
219 // This could benefit from madvise()ing
220 rrd->cdp_prep = (cdp_prep_t *) (data + offset);
221 offset += sizeof(cdp_prep_t) *
222 (rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt);
224 // This could benefit from madvise()ing
225 rrd->rra_ptr = (rra_ptr_t *) (data + offset);
226 offset += sizeof(rra_ptr_t) * rrd->stat_head->rra_cnt;
227 #ifdef USE_MADVISE
228 out_done:
229 #endif
230 rrd_file->header_len = offset;
231 rrd_file->pos = offset;
232 /* we could close(rrd_file->fd); here, the mapping is still valid anyway */
233 return (rrd_file);
234 out_nullify_head:
235 rrd->stat_head = NULL;
236 out_close:
237 close(rrd_file->fd);
238 return NULL;
239 }
241 /* Close a reference to an rrd_file. */
242 int rrd_close(
243 rrd_file_t * rrd_file)
244 {
245 int ret = 0;
247 #ifdef HAVE_MMAP
248 ret = munmap(rrd_file->file_start, rrd_file->file_len);
249 // if (ret != 0)
250 // rrd_set_error("munmap rrd_file");
251 #endif
252 free(rrd_file);
253 rrd_file = NULL;
254 return ret;
255 }
257 /* Set position of rrd_file. */
258 off_t rrd_seek(
259 rrd_file_t * rrd_file,
260 off_t off,
261 int whence)
262 {
263 off_t ret = 0;
265 #ifdef HAVE_MMAP
266 if (whence == SEEK_SET)
267 rrd_file->pos = off;
268 else if (whence == SEEK_CUR)
269 rrd_file->pos += off;
270 else if (whence == SEEK_END)
271 rrd_file->pos = rrd_file->file_len + off;
272 #else
273 ret = lseek(rrd_file->fd, off, whence);
274 if (ret < 0)
275 rrd_set_error("lseek: %s", rrd_strerror(errno));
276 rrd_file->pos = ret;
277 #endif
278 //XXX: mimic fseek, which returns 0 upon success
279 return ret == -1; //XXX: or just ret to mimic lseek
280 }
282 /* Get current position in rrd_file. */
283 off_t rrd_tell(
284 rrd_file_t * rrd_file)
285 {
286 return rrd_file->pos;
287 }
289 /* read count bytes into buffer buf, starting at rrd_file->pos.
290 * Returns the number of bytes read. */
291 ssize_t rrd_read(
292 rrd_file_t * rrd_file,
293 void *buf,
294 size_t count)
295 {
296 #ifdef HAVE_MMAP
297 char *pos = rrd_file->file_start + rrd_file->pos;
299 buf = memmove(buf, pos, count);
300 return count;
301 #else
302 ssize_t ret;
304 ret = read(rrd_file->fd, buf, count);
305 //XXX: eventually add generic rrd_set_error(""); here
306 return ret;
307 #endif
308 }
310 /* write count bytes from buffer buf to the current position
311 * rrd_file->pos of rrd_file->fd. */
312 ssize_t rrd_write(
313 rrd_file_t * rrd_file,
314 const void *buf,
315 size_t count)
316 {
317 ssize_t ret = count;
319 #ifdef HAVE_MMAP
320 char *off, *new_pos;
322 off = rrd_file->file_start + rrd_file->pos;
323 new_pos = memmove(rrd_file->file_start + rrd_file->pos, buf, count);
324 ret = new_pos - off;
325 #else
326 ret = write(rrd_file->fd, buf, count)
327 #endif
328 return ret;
329 }
331 /* flush all data pending to be written to FD. */
332 void rrd_flush(
333 rrd_file_t * rrd_file)
334 {
335 if (fdatasync(rrd_file->fd) != 0) {
336 rrd_set_error("flushing fd %d: %s", rrd_file->fd,
337 rrd_strerror(errno));
338 }
339 }
341 void rrd_init(
342 rrd_t *rrd)
343 {
344 rrd->stat_head = NULL;
345 rrd->ds_def = NULL;
346 rrd->rra_def = NULL;
347 rrd->live_head = NULL;
348 rrd->rra_ptr = NULL;
349 rrd->pdp_prep = NULL;
350 rrd->cdp_prep = NULL;
351 rrd->rrd_value = NULL;
352 }
354 void rrd_free(
355 rrd_t UNUSED(*rrd))
356 {
357 #ifndef HAVE_MMAP
358 if (atoi(rrd->stat_head->version) < 3)
359 free(rrd->live_head);
360 free(rrd->stat_head);
361 free(rrd->ds_def);
362 free(rrd->rra_def);
363 free(rrd->rra_ptr);
364 free(rrd->pdp_prep);
365 free(rrd->cdp_prep);
366 free(rrd->rrd_value);
367 #endif
368 }
370 /* routine used by external libraries to free memory allocated by
371 * rrd library */
372 void rrd_freemem(
373 void *mem)
374 {
375 free(mem);
376 }
378 int readfile(
379 const char *file_name,
380 char **buffer,
381 int skipfirst)
382 {
383 long writecnt = 0, totalcnt = MEMBLK;
384 long offset = 0;
385 FILE *input = NULL;
386 char c;
388 if ((strcmp("-", file_name) == 0)) {
389 input = stdin;
390 } else {
391 if ((input = fopen(file_name, "rb")) == NULL) {
392 rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
393 return (-1);
394 }
395 }
396 if (skipfirst) {
397 do {
398 c = getc(input);
399 offset++;
400 } while (c != '\n' && !feof(input));
401 }
402 if (strcmp("-", file_name)) {
403 fseek(input, 0, SEEK_END);
404 /* have extra space for detecting EOF without realloc */
405 totalcnt = (ftell(input) + 1) / sizeof(char) - offset;
406 if (totalcnt < MEMBLK)
407 totalcnt = MEMBLK; /* sanitize */
408 fseek(input, offset * sizeof(char), SEEK_SET);
409 }
410 if (((*buffer) = (char *) malloc((totalcnt + 4) * sizeof(char))) == NULL) {
411 perror("Allocate Buffer:");
412 exit(1);
413 };
414 do {
415 writecnt +=
416 fread((*buffer) + writecnt, 1,
417 (totalcnt - writecnt) * sizeof(char), input);
418 if (writecnt >= totalcnt) {
419 totalcnt += MEMBLK;
420 if (((*buffer) =
421 rrd_realloc((*buffer),
422 (totalcnt + 4) * sizeof(char))) == NULL) {
423 perror("Realloc Buffer:");
424 exit(1);
425 };
426 }
427 } while (!feof(input));
428 (*buffer)[writecnt] = '\0';
429 if (strcmp("-", file_name) != 0) {
430 fclose(input);
431 };
432 return writecnt;
433 }