c29741ce495777d746237c2cb1aee8a4e8f8feb1
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*
73 rrd_open(const char * const file_name, rrd_t *rrd, unsigned rdwr)
74 {
75 int flags = 0;
76 mode_t mode = S_IRUSR;
77 int version, prot = PROT_READ;
78 off_t offset = 0;
79 char *data;
80 struct stat statb;
81 rrd_file_t *rrd_file = malloc(sizeof(rrd_file_t));
82 if (rrd_file == NULL) {
83 rrd_set_error("allocating rrd_file descriptor for '%s'",
84 file_name);
85 return NULL;
86 }
87 memset(rrd_file, 0, sizeof(rrd_file_t));
88 rrd_init(rrd);
89 if (rdwr == RRD_READWRITE) {
90 mode |= S_IWUSR;
91 prot |= PROT_WRITE;
92 } else if (rdwr == RRD_CREAT) {
93 mode |= S_IWUSR;
94 prot |= PROT_WRITE;
95 flags |= (O_CREAT|O_TRUNC);
96 }
97 #ifdef O_NONBLOCK
98 flags |= O_NONBLOCK;
99 #endif
101 if ((rrd_file->fd = open(file_name, flags, mode)) < 0 ){
102 rrd_set_error("opening '%s': %s",file_name, rrd_strerror(errno));
103 return NULL;
104 }
106 /* ???: length = lseek(rrd_file->fd, 0, SEEK_END); */
107 /* ??? locking the whole area of the file may overdo it a bit, does it? */
108 if ((fstat(rrd_file->fd, &statb)) < 0) {
109 rrd_set_error("fstat '%s': %s",file_name, rrd_strerror(errno));
110 goto out_close;
111 }
112 rrd_file->file_len = statb.st_size;
114 #ifdef HAVE_POSIX_FADVISE
115 /* In general we need no read-ahead when dealing with rrd_files.
116 When we stop reading, it is highly unlikely that we start up again.
117 In this manner we actually save time and diskaccess (and buffer cache).
118 Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */
119 if (0 != posix_fadvise(rrd_file->fd, 0, 0, POSIX_FADV_RANDOM)) {
120 rrd_set_error("setting POSIX_FADV_RANDOM on '%s': %s",file_name, rrd_strerror(errno));
121 goto out_close;
122 }
123 #endif
125 /*
126 if (rdwr == RRD_READWRITE)
127 {
128 if (setvbuf((rrd_file->fd),NULL,_IONBF,2)) {
129 rrd_set_error("failed to disable the stream buffer\n");
130 return (-1);
131 }
132 }
133 */
134 data = mmap(0, rrd_file->file_len, prot, MAP_SHARED,
135 rrd_file->fd, offset);
137 /* lets see if the first read worked */
138 if (data == MAP_FAILED) {
139 rrd_set_error("error mmaping file '%s'",file_name);
140 goto out_close;
141 }
142 rrd_file->file_start = data;
143 #ifdef USE_MADVISE
144 if (rrd == NULL) { /*XXX: currently not used! */
145 /* We will read everything in a moment (copying) */
146 madvise(data, rrd_file->file_len, MADV_WILLNEED|MADV_SEQUENTIAL);
147 goto out_done;
148 }
149 /* We do not need to read anything in for the moment */
150 madvise(data, rrd_file->file_len, MADV_DONTNEED);
151 #endif
153 #ifdef USE_MADVISE
154 /* the stat_head will be needed soonish, so hint accordingly */
155 madvise(data+offset, sizeof(stat_head_t), MADV_WILLNEED);
156 #endif
158 rrd->stat_head = (stat_head_t*)(data + offset);
159 offset += sizeof(stat_head_t);
161 /* lets do some test if we are on track ... */
162 if (memcmp(rrd->stat_head->cookie,RRD_COOKIE,sizeof(RRD_COOKIE)) != 0) {
163 rrd_set_error("'%s' is not an RRD file",file_name);
164 goto out_nullify_head;
165 }
167 if (rrd->stat_head->float_cookie != FLOAT_COOKIE){
168 rrd_set_error("This RRD was created on other architecture");
169 goto out_nullify_head;
170 }
172 version = atoi(rrd->stat_head->version);
174 if (version > atoi(RRD_VERSION)) {
175 rrd_set_error("can't handle RRD file version %s",
176 rrd->stat_head->version);
177 goto out_nullify_head;
178 }
180 #ifdef USE_MADVISE
181 /* the ds_def will be needed soonish, so hint accordingly */
182 madvise(data+offset, sizeof(ds_def_t)*rrd->stat_head->ds_cnt, MADV_WILLNEED);
183 #endif
184 rrd->ds_def = (ds_def_t*)(data + offset);
185 offset += sizeof(ds_def_t) * rrd->stat_head->ds_cnt;
187 #ifdef USE_MADVISE
188 /* the rra_def will be needed soonish, so hint accordingly */
189 madvise(data+offset, sizeof(rra_def_t)*rrd->stat_head->rra_cnt, MADV_WILLNEED);
190 #endif
191 rrd->rra_def = (rra_def_t*)(data + offset);
192 offset += sizeof(rra_def_t) * rrd->stat_head->rra_cnt;
194 /* handle different format for the live_head */
195 if (version < 3) {
196 rrd->live_head = (live_head_t *)malloc(sizeof(live_head_t));
197 if (rrd->live_head == NULL) {
198 rrd_set_error("live_head_t malloc");
199 goto out_close;
200 }
201 memmove(&rrd->live_head->last_up, data+offset, sizeof(long));
202 rrd->live_head->last_up_usec = 0;
203 } else {
204 #ifdef USE_MADVISE
205 /* the live_head will be needed soonish, so hint accordingly */
206 madvise(data+offset, sizeof(live_head_t), MADV_WILLNEED);
207 #endif
208 rrd->live_head = (live_head_t*)(data + offset);
209 offset += sizeof(live_head_t);
210 }
211 // This doesn't look like it needs madvise
212 rrd->pdp_prep = (pdp_prep_t*)(data + offset);
213 offset += sizeof(pdp_prep_t) * rrd->stat_head->ds_cnt;
215 // This could benefit from madvise()ing
216 rrd->cdp_prep = (cdp_prep_t*)(data + offset);
217 offset += sizeof(cdp_prep_t) *
218 (rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt);
220 // This could benefit from madvise()ing
221 rrd->rra_ptr = (rra_ptr_t*)(data + offset);
222 offset += sizeof(rra_ptr_t) * rrd->stat_head->rra_cnt;
223 #ifdef USE_MADVISE
224 out_done:
225 #endif
226 rrd_file->header_len = offset;
227 rrd_file->pos = offset;
228 /* we could close(rrd_file->fd); here, the mapping is still valid anyway */
229 return (rrd_file);
230 out_nullify_head:
231 rrd->stat_head = NULL;
232 out_close:
233 close(rrd_file->fd);
234 return NULL;
235 }
237 /* Close a reference to an rrd_file. */
238 int rrd_close(rrd_file_t* rrd_file) {
239 int ret = 0;
240 #ifdef HAVE_MMAP
241 ret = munmap(rrd_file->file_start, rrd_file->file_len);
242 // if (ret != 0)
243 // rrd_set_error("munmap rrd_file");
244 #endif
245 free(rrd_file);
246 rrd_file = NULL;
247 return ret;
248 }
250 /* Set position of rrd_file. */
251 off_t rrd_seek(rrd_file_t* rrd_file, off_t off, int whence) {
252 off_t ret = 0;
253 #ifdef HAVE_MMAP
254 if (whence == SEEK_SET)
255 rrd_file->pos = off;
256 else if (whence == SEEK_CUR)
257 rrd_file->pos += off;
258 else if (whence == SEEK_END)
259 rrd_file->pos = rrd_file->file_len + off;
260 #else
261 ret = lseek(rrd_file->fd, off, whence);
262 if (ret < 0)
263 rrd_set_error("lseek: %s", rrd_strerror(errno));
264 rrd_file->pos = ret;
265 #endif
266 //XXX: mimic fseek, which returns 0 upon success
267 return ret == -1; //XXX: or just ret to mimic lseek
268 }
270 /* Get current position in rrd_file. */
271 off_t rrd_tell(rrd_file_t* rrd_file) {
272 return rrd_file->pos;
273 }
275 /* read count bytes into buffer buf, starting at rrd_file->pos.
276 * Returns the number of bytes read. */
277 ssize_t rrd_read(rrd_file_t* rrd_file, void*buf, size_t count) {
278 #ifdef HAVE_MMAP
279 char* pos = rrd_file->file_start + rrd_file->pos;
280 buf = memmove(buf, pos, count);
281 return count;
282 #else
283 ssize_t ret;
284 ret = read(rrd_file->fd, buf, count);
285 //XXX: eventually add generic rrd_set_error(""); here
286 return ret;
287 #endif
288 }
290 /* write count bytes from buffer buf to the current position
291 * rrd_file->pos of rrd_file->fd. */
292 ssize_t rrd_write(rrd_file_t * rrd_file, const void*buf, size_t count) {
293 ssize_t ret = count;
294 #ifdef HAVE_MMAP
295 char *off, *new_pos;
296 off = rrd_file->file_start + rrd_file->pos;
297 new_pos = memmove(rrd_file->file_start + rrd_file->pos, buf, count);
298 ret = new_pos - off;
299 #else
300 ret = write(rrd_file->fd, buf, count)
301 #endif
302 return ret;
303 }
305 /* flush all data pending to be written to FD. */
306 void rrd_flush(rrd_file_t* rrd_file)
307 {
308 if (fdatasync(rrd_file->fd) != 0) {
309 rrd_set_error("flushing fd %d: %s", rrd_file->fd,
310 rrd_strerror(errno));
311 }
312 }
314 void rrd_init(rrd_t *rrd)
315 {
316 rrd->stat_head = NULL;
317 rrd->ds_def = NULL;
318 rrd->rra_def = NULL;
319 rrd->live_head = NULL;
320 rrd->rra_ptr = NULL;
321 rrd->pdp_prep = NULL;
322 rrd->cdp_prep = NULL;
323 rrd->rrd_value = NULL;
324 }
326 void rrd_free(rrd_t UNUSED(*rrd))
327 {
328 #ifndef HAVE_MMAP
329 if (atoi(rrd->stat_head->version) < 3)
330 free(rrd->live_head);
331 free(rrd->stat_head);
332 free(rrd->ds_def);
333 free(rrd->rra_def);
334 free(rrd->rra_ptr);
335 free(rrd->pdp_prep);
336 free(rrd->cdp_prep);
337 free(rrd->rrd_value);
338 #endif
339 }
341 /* routine used by external libraries to free memory allocated by
342 * rrd library */
343 void rrd_freemem(void *mem)
344 {
345 free(mem);
346 }
348 int readfile(const char *file_name, char **buffer, int skipfirst){
349 long writecnt=0,totalcnt = MEMBLK;
350 long offset = 0;
351 FILE *input=NULL;
352 char c ;
353 if ((strcmp("-",file_name) == 0)) { input = stdin; }
354 else {
355 if ((input = fopen(file_name,"rb")) == NULL ){
356 rrd_set_error("opening '%s': %s",file_name,rrd_strerror(errno));
357 return (-1);
358 }
359 }
360 if (skipfirst){
361 do { c = getc(input); offset++; } while (c != '\n' && ! feof(input));
362 }
363 if (strcmp("-",file_name)) {
364 fseek(input, 0, SEEK_END);
365 /* have extra space for detecting EOF without realloc */
366 totalcnt = (ftell(input) + 1) / sizeof(char) - offset;
367 if (totalcnt < MEMBLK)
368 totalcnt = MEMBLK; /* sanitize */
369 fseek(input, offset * sizeof(char), SEEK_SET);
370 }
371 if (((*buffer) = (char *) malloc((totalcnt+4) * sizeof(char))) == NULL) {
372 perror("Allocate Buffer:");
373 exit(1);
374 };
375 do{
376 writecnt += fread((*buffer)+writecnt, 1, (totalcnt - writecnt) * sizeof(char),input);
377 if (writecnt >= totalcnt){
378 totalcnt += MEMBLK;
379 if (((*buffer)=rrd_realloc((*buffer), (totalcnt+4) * sizeof(char)))==NULL){
380 perror("Realloc Buffer:");
381 exit(1);
382 };
383 }
384 } while (! feof(input));
385 (*buffer)[writecnt] = '\0';
386 if (strcmp("-",file_name) != 0) {fclose(input);};
387 return writecnt;
388 }