Nut/OS  4.10.3
API Reference
perci.c
Go to the documentation of this file.
00001 /*
00002  * Copyright 2010 by egnite GmbH
00003  *
00004  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  *
00010  * 1. Redistributions of source code must retain the above copyright
00011  *    notice, this list of conditions and the following disclaimer.
00012  * 2. Redistributions in binary form must reproduce the above copyright
00013  *    notice, this list of conditions and the following disclaimer in the
00014  *    documentation and/or other materials provided with the distribution.
00015  * 3. Neither the name of the copyright holders nor the names of
00016  *    contributors may be used to endorse or promote products derived
00017  *    from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00020  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00021  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00022  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00023  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00024  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00025  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00026  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00027  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00028  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00029  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00030  * SUCH DAMAGE.
00031  *
00032  * For additional information see http://www.ethernut.de/
00033  */
00034 
00035 /*
00036  * \file gorp/buffer/perci.c
00037  * \brief Persistent circular buffer.
00038  *
00039  * Applications may use this module for logging any kind of data.
00040  *
00041  * \verbatim
00042  * $Id$
00043  * \endverbatim
00044  */
00045 
00046 #include <io.h>
00047 #include <fcntl.h>
00048 #include <sys/stat.h>
00049 #include <string.h>
00050 #include <stdarg.h>
00051 #include <time.h>
00052 #include <errno.h>
00053 #include <sys/nutdebug.h>
00054 #include <sys/event.h>
00055 
00056 #include <gorp/perci.h>
00057 
00062 
00063 //#define NUTDEBUG
00064 
00071 void PerCiDump(FILE *stream, char *path)
00072 {
00073 #ifdef NUTDEBUG
00074     int fd;
00075     int got;
00076     perci_fast_reclen_t i;
00077     perci_fast_reclen_t ll;
00078     perci_recnum_t recnum;
00079     perci_reclen_t reclen;
00080     uint8_t *recbuf;
00081 
00082     fd = _open(path, _O_RDWR | _O_BINARY);
00083     if (fd == -1) {
00084         fprintf(stream, "Error %d opening %s\n", errno, path);
00085         return;
00086     } else {
00087         fprintf(stream, "Dump %ld bytes in %s\n", _filelength(fd), path);
00088         recbuf = malloc(PERCI_DATASIZE);
00089         for (recnum = 0;; recnum++) {
00090             got = _read(fd, &reclen, sizeof(perci_reclen_t));
00091             if (got <= 0) {
00092                 break;
00093             }
00094             fprintf(stream, "%03u %03u ", recnum, (unsigned int)reclen);
00095             got = _read(fd, recbuf, PERCI_DATASIZE);
00096             for (i = 0, ll = 0; i < (perci_fast_reclen_t) got && i <= (perci_fast_reclen_t) reclen; i++) {
00097                 if (recbuf[i] < ' ') {
00098                     ll += 2;
00099                     if (recbuf[i] == '\n') {
00100                         fputs("\\n", stream);
00101                     }
00102                     else if (recbuf[i] == '\r') {
00103                         fputs("\\r", stream);
00104                     }
00105                     else if (recbuf[i] == '\t') {
00106                         fputs("\\t", stream);
00107                     } else {
00108                         fprintf(stream, "\\x%02x", recbuf[i]);
00109                         ll += 2;
00110                     }
00111                 } else {
00112                     fputc(recbuf[i], stream);
00113                     ll++;
00114                 }
00115                 if (ll > 80) {
00116                     fprintf(stream, "...");
00117                     break;
00118                 }
00119             }
00120             fputc('\n', stream);
00121         }
00122         _close(fd);
00123         free(recbuf);
00124     }
00125 #endif
00126 }
00127 
00151 int PerCiInit(char *path, int recs)
00152 {
00153     int fd;
00154     int i;
00155     PERCI_RECORD *rec;
00156 
00157     /* Check function parameters. */
00158     NUTASSERT(path != NULL);
00159     NUTASSERT(recs >= 2);
00160 
00161     fd = _open(path, _O_RDWR | _O_CREAT | _O_TRUNC | _O_BINARY);
00162     if (fd == -1) {
00163         return -1;
00164     }
00165     rec = calloc(PERCI_RECSIZE, 1);
00166     if (rec) {
00167         for (i = 0; i < recs; i++) {
00168             _write(fd, rec, PERCI_RECSIZE);
00169         }
00170         _write(fd, rec, sizeof(perci_reclen_t));
00171         free(rec);
00172     }
00173     _close(fd);
00174 
00175     return 0;
00176 }
00177 
00202 PERCI_WRITER *PerCiOpen(char *path)
00203 {
00204     uint_fast8_t ok = 0;
00205     PERCI_WRITER *writer;
00206     perci_reclen_t reclen;
00207 
00208     /* Check function parameter. */
00209     NUTASSERT(path != NULL);
00210 
00211     /* Allocate a writer structure and open the file. */
00212     writer = calloc(1, sizeof(PERCI_WRITER));
00213     if (writer) {
00214         /* Open the file. If this fails, release the writer structure
00215            and return a NULL pointer. */
00216         writer->pcw_fd = _open(path, _O_RDWR | _O_BINARY);
00217         if (writer->pcw_fd != -1) {
00218             /* File exists, determine its size. If it doesn't contain at
00219                least 2 records, we consider it corrupted. */
00220             writer->pcw_size = _filelength(writer->pcw_fd);
00221             if (writer->pcw_size >= 2 * PERCI_RECSIZE + sizeof(perci_reclen_t)) {
00222                 writer->pcw_size -= sizeof(perci_reclen_t);
00223 
00224                 /* 
00225                  * Scan the file for the next available record. 
00226                  */
00227                 _seek(writer->pcw_fd, 0, SEEK_SET);
00228                 for (writer->pcw_recnum = 0; writer->pcw_recnum < PERCI_MAX_RECORDS; writer->pcw_recnum++) {
00229                     /* Read the length of the current record. */
00230                     if (_read(writer->pcw_fd, &reclen, sizeof(perci_reclen_t)) != sizeof(perci_reclen_t)) {
00231                         /* Corrupted. */
00232                         break;
00233                     }
00234                     /* If this record isn't completely filled, then we continue
00235                        writing at this point. */
00236                     if (reclen < PERCI_DATASIZE) {
00237                         /* Fill the record buffer. */
00238                         writer->pcw_rec.pcd_len = reclen;
00239                         if (reclen && _read(writer->pcw_fd, writer->pcw_rec.pcd_data, reclen) != reclen) {
00240                             break;
00241                         }
00242                         NutEventPost(&writer->pcw_mutex);
00243                         ok = 1;
00244                         break;
00245                     }
00246                     /* This record is filled, move to the next one. */
00247                     _seek(writer->pcw_fd, PERCI_DATASIZE, SEEK_CUR);
00248                 }
00249             }
00250         }
00251     }
00252     /* Release resources on error. */
00253     if (!ok && writer) {
00254         if (writer->pcw_fd != -1) {
00255             _close(writer->pcw_fd);
00256         }
00257         free(writer);
00258         writer = NULL;
00259     }
00260     return writer;
00261 }
00262 
00269 void PerCiFlush(PERCI_WRITER * writer)
00270 {
00271     /* Check function parameter. */
00272     NUTASSERT(writer != NULL);
00273     NUTASSERT(writer->pcw_fd != -1);
00274 
00275     _seek(writer->pcw_fd, writer->pcw_recnum * PERCI_RECSIZE, SEEK_SET);
00276     _write(writer->pcw_fd, &writer->pcw_rec, sizeof(perci_reclen_t) + writer->pcw_rec.pcd_len);
00277 }
00278 
00285 void PerCiClose(PERCI_WRITER * writer)
00286 {
00287     /* Check function parameter. */
00288     NUTASSERT(writer != NULL);
00289 
00290     if (writer->pcw_rec.pcd_len) {
00291         PerCiFlush(writer);
00292     }
00293     _close(writer->pcw_fd);
00294     free(writer);
00295 }
00296 
00305 int PerCiWrite(PERCI_WRITER * writer, CONST char *data, int len)
00306 {
00307     perci_fast_reclen_t cnt = 0;
00308     perci_fast_reclen_t num;
00309     perci_fast_reclen_t reclen;
00310 
00311     /* Check parameters. */
00312     NUTASSERT(writer != NULL);
00313     NUTASSERT(writer->pcw_fd != -1);
00314     NUTASSERT(writer->pcw_rec.pcd_len <= PERCI_DATASIZE);
00315 
00316     NutEventWait(&writer->pcw_mutex, NUT_WAIT_INFINITE);
00317     while ((perci_fast_reclen_t) len > cnt) {
00318         /* Calculate the number of bytes to write in the next step. */
00319         reclen = (perci_fast_reclen_t) writer->pcw_rec.pcd_len;
00320         num = (perci_fast_reclen_t) len - cnt;
00321         if (num > PERCI_DATASIZE - reclen) {
00322             num = PERCI_DATASIZE - reclen;
00323         }
00324         /* Move the bytes to the record buffer. */
00325         memcpy(&writer->pcw_rec.pcd_data[reclen], data, num);
00326         writer->pcw_rec.pcd_len += num;
00327         cnt += num;
00328         data += num;
00329 
00330         /* If the buffered record is completely filled, then write it 
00331            back to the file and claim the next one. Note, that we write
00332            a whole record, which is sizeof(perci_reclen_t) larger than
00333            the real record size. This way we automatically override the
00334            length of the following record with zero. */
00335         if (writer->pcw_rec.pcd_len == PERCI_DATASIZE) {
00336             _seek(writer->pcw_fd, writer->pcw_recnum * PERCI_RECSIZE, SEEK_SET);
00337             if (_write(writer->pcw_fd, &writer->pcw_rec, sizeof(PERCI_RECORD)) != sizeof(PERCI_RECORD)) {
00338                 NutEventPost(&writer->pcw_mutex);
00339                 return -1;
00340             }
00341             writer->pcw_rec.pcd_len = 0;
00342             writer->pcw_recnum++;
00343             /* Check for wrap around. */
00344             if (writer->pcw_recnum * PERCI_RECSIZE >= writer->pcw_size) {
00345                 writer->pcw_recnum = 0;
00346                 _seek(writer->pcw_fd, 0, SEEK_SET);
00347                 if (_write(writer->pcw_fd, &writer->pcw_rec.pcd_len, sizeof(perci_reclen_t)) != sizeof(perci_reclen_t)) {
00348                     NutEventPost(&writer->pcw_mutex);
00349                     return -1;
00350                 }
00351             }
00352         }
00353     }
00354     NutEventPost(&writer->pcw_mutex);
00355     return cnt;
00356 }
00357 
00372 int PerCiWriteVarList(PERCI_WRITER * writer, CONST char *fmt, va_list ap)
00373 {
00374     int cnt;
00375     char *line;
00376 
00377     /* Check function parameter. */
00378     NUTASSERT(fmt != NULL);
00379 
00380     line = malloc(PERCI_DATASIZE);
00381     if (line) {
00382         /* Potentially dangerous. We need vsnprintf() */
00383         cnt = vsprintf(line, fmt, ap);
00384         NUTASSERT(cnt < PERCI_DATASIZE);
00385         cnt = PerCiWrite(writer, line, strlen(line));
00386         free(line);
00387     } else {
00388         cnt = -1;
00389     }
00390     return cnt;
00391 }
00392 
00393 
00404 int PerCiWriteFormat(PERCI_WRITER * writer, CONST char *fmt, ...)
00405 {
00406     int rc;
00407     va_list ap;
00408 
00409     /* Check function parameter. */
00410     NUTASSERT(fmt != NULL);
00411 
00412     va_start(ap, fmt);
00413     rc = PerCiWriteVarList(writer, fmt, ap);
00414     va_end(ap);
00415 
00416     return rc;
00417 }
00418 
00431 static perci_fast_reclen_t FindNextData(PERCI_WRITER * writer, perci_fast_recnum_t * recnum)
00432 {
00433     perci_reclen_t reclen;
00434     long pos;
00435     int got;
00436 
00437     /* Check parameters. */
00438     NUTASSERT(writer != NULL);
00439 
00440     NutEventWait(&writer->pcw_mutex, NUT_WAIT_INFINITE);
00441     pos = *recnum * PERCI_RECSIZE;
00442     for (;;) {
00443         /* If we reached the end of the file, then continue at its
00444            beginning. */
00445         if (pos >= writer->pcw_size) {
00446             pos = 0;
00447             *recnum = 0;
00448         }
00449         /* If we reached the latest record, then return the number
00450            of bytes in the write buffer. */
00451         if (*recnum == writer->pcw_recnum) {
00452             reclen = writer->pcw_rec.pcd_len;
00453             break;
00454         }
00455         /* Otherwise read the number of bytes available in the record.
00456            This value is stored at the beginning of each record. */
00457         _seek(writer->pcw_fd, pos, SEEK_SET);
00458         got = _read(writer->pcw_fd, &reclen, sizeof(perci_reclen_t));
00459         if (got == sizeof(perci_reclen_t) && reclen) {
00460             break;
00461         }
00462         /* Either this record is empty or the read failed, which we
00463            sliently ignore for now. Move on to the next record. */
00464         pos += PERCI_RECSIZE;
00465         (*recnum)++;
00466     }
00467     NutEventPost(&writer->pcw_mutex);
00468     return reclen;
00469 }
00470 
00490 PERCI_READER *PerCiAttachReader(PERCI_WRITER * writer)
00491 {
00492     PERCI_READER *reader;
00493 
00494     /* Check parameters. */
00495     NUTASSERT(writer != NULL);
00496 
00497     reader = malloc(sizeof(PERCI_READER));
00498     reader->pcr_cil = writer;
00499     reader->pcr_recpos = 0;
00500     /* Search the oldest record that contains data. Start with the one 
00501        above the current record of the writer. */
00502     reader->pcr_recnum = writer->pcw_recnum + 1;
00503     reader->pcr_reclen = FindNextData(writer, &reader->pcr_recnum);
00504 
00505     return reader;
00506 }
00507 
00514 void PerCiDetachReader(PERCI_READER * reader)
00515 {
00516     /* Check parameters. */
00517     NUTASSERT(reader != NULL);
00518     free(reader);
00519 }
00520 
00532 int PerCiRead(PERCI_READER * reader, char *data, int len)
00533 {
00534     int cnt = 0;
00535     perci_fast_reclen_t num;
00536     int got;
00537 
00538     /* Check parameters. */
00539     NUTASSERT(reader != NULL);
00540 
00541     /*
00542      * Loop for for number of requested bytes.
00543      */
00544     while (len > cnt) {
00545         /* Calculate the number of bytes to read in the next step. */
00546         num = len - cnt;
00547         if (num > reader->pcr_reclen - reader->pcr_recpos) {
00548             num = reader->pcr_reclen - reader->pcr_recpos;
00549         }
00550         /* Check if we reached the last record. This is the one currently
00551            used by the writer. */
00552         if (reader->pcr_recnum == reader->pcr_cil->pcw_recnum) {
00553             if (num) {
00554                 /* Get the data from the write buffer. */
00555                 memcpy(data, &reader->pcr_cil->pcw_rec.pcd_data[reader->pcr_recpos], num);
00556                 data += num;
00557                 cnt += num;
00558                 reader->pcr_recpos += num;
00559             } else {
00560                 /* All data read. */
00561                 break;
00562             }
00563         }
00564         /* Not the last record. Continue reading from the file. */
00565         else {
00566             /* Check if we reached the end of the current record. */
00567             if (num) {
00568                 got = _read(reader->pcr_cil->pcw_fd, data, num);
00569                 if (got != num) {
00570                     break;
00571                 }
00572                 data += num;
00573                 cnt += num;
00574                 reader->pcr_recpos += num;
00575             } else {
00576                 /* We consumed all data in this record. Step to the next. */
00577                 reader->pcr_recnum++;
00578                 reader->pcr_recpos = 0;
00579                 /* Find the next record containing data. */
00580                 reader->pcr_reclen = FindNextData(reader->pcr_cil, &reader->pcr_recnum);
00581                 if (reader->pcr_reclen == 0) {
00582                     /* No more data available. */
00583                     break;
00584                 }
00585             }
00586         }
00587     }
00588     return cnt;
00589 }
00590 
00602 int PerCiReadLine(PERCI_READER * reader, char *line, int len)
00603 {
00604     int cnt = 0;
00605     char *cp = line;
00606 
00607     /* Check function parameters. */
00608     NUTASSERT(reader != NULL);
00609     NUTASSERT(line != NULL);
00610 
00611     while (cnt < len) {
00612         if (PerCiRead(reader, cp, 1) != 1) {
00613             break;
00614         }
00615         cnt++;
00616         if (*cp++ == '\n') {
00617             break;
00618         }
00619     }
00620     *cp = 0;
00621 
00622     return cnt;
00623 }
00624