pud: clear the cached timestamp when the position file can't be read
[olsrd.git] / lib / pud / src / posFile.c
1 #include "posFile.h"
2
3 /* Plugin includes */
4 #include "pud.h"
5 #include "configTools.h"
6
7 /* OLSR includes */
8
9 /* System includes */
10 #include <stdio.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <string.h>
14 #include <regex.h>
15 #include <sys/stat.h>
16
17 /** the maximal length of a line that is read from the file */
18 #define LINE_LENGTH 256
19
20 /** regular expression describing a comment */
21 static const char * regexCommentString = "^([[:space:]]*|[[:space:]#]+.*)$";
22
23 /** regular expression describing a key/value pair */
24 static const char * regexNameValueString =
25                 "^[[:space:]]*([^[:space:]]+)[[:space:]]*=[[:space:]]*([^[:space:]]+)[[:space:]]*$";
26
27 /** the number of matches in regexNameValueString */
28 static const size_t regexNameValuematchCount = 3;
29
30 /** the compiled regular expression describing a comment */
31 static regex_t regexComment;
32
33 /** the compiled regular expression describing a key/value pair */
34 static regex_t regexNameValue;
35
36 /** true when the plugin has been started */
37 static bool started = false;
38
39 /** type to hold the cached stat result */
40 typedef struct _CachedStat {
41         time_t timeStamp; /* Time of last modification (second resolution) */
42 } CachedStat;
43
44 /** the cached stat result */
45 static CachedStat cachedStat;
46
47 /**
48  * Initialises the positionFile reader.
49  * @return true upon success, false otherwise
50  */
51 bool startPositionFile(void) {
52         if (started) {
53                 return true;
54         }
55
56         if (regcomp(&regexComment, regexCommentString, REG_EXTENDED | REG_ICASE)) {
57                 pudError(false, "Could not compile regex \"%s\"", regexCommentString);
58                 return false;
59         }
60
61         if (regcomp(&regexNameValue, regexNameValueString, REG_EXTENDED | REG_ICASE)) {
62                 pudError(false, "Could not compile regex \"%s\"", regexNameValueString);
63                 regfree(&regexComment);
64                 return false;
65         }
66
67         cachedStat.timeStamp = -1;
68
69         started = true;
70         return true;
71 }
72
73 /**
74  * Cleans up the positionFile reader.
75  */
76 void stopPositionFile(void) {
77         if (started) {
78                 regfree(&regexNameValue);
79                 regfree(&regexComment);
80                 started = false;
81         }
82 }
83
84 /**
85  * Performs a regex match
86  * @param regex the compiled regex to match against
87  * @param line the line to match
88  * @param nmatch the number of matches to produce
89  * @param pmatch the array with match information
90  * @return true upon success, false otherwise
91  */
92 static bool regexMatch(regex_t * regex, char * line, size_t nmatch, regmatch_t pmatch[]) {
93         int result = regexec(regex, line, nmatch, pmatch, 0);
94         if (!result) {
95                 return true;
96         }
97
98         if (result == REG_NOMATCH) {
99                 return false;
100         }
101
102         {
103                 char msgbuf[256];
104                 regerror(result, regex, msgbuf, sizeof(msgbuf));
105                 pudError(false, "Regex match failed: %s", msgbuf);
106         }
107
108         return false;
109 }
110
111 /** the buffer in which to store a line read from the file */
112 static char line[LINE_LENGTH];
113
114 /**
115  * Read the position file
116  * @param fileName the filename
117  * @param nmeaInfo the NMEA data
118  */
119 bool readPositionFile(char * fileName, nmeaINFO * nmeaInfo) {
120         int fd;
121         struct stat statBuf;
122         FILE * fp = NULL;
123         unsigned int lineNumber = 0;
124         char * name = NULL;
125         char * value = NULL;
126         nmeaINFO result;
127         bool retval = false;
128
129         fd = open(fileName, O_RDONLY);
130         if (fd < 0) {
131                 /* could not open the file */
132                 memset(&cachedStat.timeStamp, 0, sizeof(cachedStat.timeStamp));
133                 goto out;
134         }
135
136         if (fstat(fd, &statBuf)) {
137                 /* could not stat the file */
138                 memset(&cachedStat.timeStamp, 0, sizeof(cachedStat.timeStamp));
139                 goto out;
140         }
141
142         if (!memcmp(&cachedStat.timeStamp, &statBuf.st_mtime, sizeof(cachedStat.timeStamp))) {
143                 /* file did not change since last read */
144                 goto out;
145         }
146
147         fp = fdopen(fd, "r");
148         if (!fp) {
149                 /* could not open the file */
150                 goto out;
151         }
152
153         nmea_zero_INFO(&result);
154         result.sig = POSFILE_DEFAULT_SIG;
155         result.fix = POSFILE_DEFAULT_FIX;
156         result.HDOP = POSFILE_DEFAULT_HDOP;
157         result.VDOP = POSFILE_CALCULATED_VDOP(result.HDOP);
158         result.PDOP = POSFILE_CALCULATED_PDOP(result.HDOP);
159         result.lat = POSFILE_DEFAULT_LAT;
160         result.lon = POSFILE_DEFAULT_LON;
161         result.elv = POSFILE_DEFAULT_ELV;
162         result.speed = POSFILE_DEFAULT_SPEED;
163         result.track = POSFILE_DEFAULT_TRACK;
164         result.mtrack = POSFILE_DEFAULT_MTRACK;
165         result.magvar = POSFILE_DEFAULT_MAGVAR;
166
167         memcpy(&cachedStat.timeStamp, &statBuf.st_mtime, sizeof(cachedStat.timeStamp));
168
169         while (fgets(line, LINE_LENGTH, fp)) {
170                 regmatch_t pmatch[regexNameValuematchCount];
171
172                 lineNumber++;
173
174                 if (regexMatch(&regexComment, line, 0, NULL)) {
175                         continue;
176                 }
177
178                 if (!regexMatch(&regexNameValue, line, regexNameValuematchCount, pmatch)) {
179                         pudError(false, "Position file \"%s\", line %d uses invalid syntax: %s", fileName, lineNumber, line);
180                         goto out;
181                 }
182
183                 /* determine name/value */
184                 name = &line[pmatch[1].rm_so];
185                 line[pmatch[1].rm_eo] = '\0';
186                 value = &line[pmatch[2].rm_so];
187                 line[pmatch[2].rm_eo] = '\0';
188
189                 if (!strncasecmp(POSFILE_NAME_SIG, name, sizeof(line))) {
190                         if (!strncasecmp(POSFILE_VALUE_SIG_BAD, value, sizeof(line))) {
191                                 result.sig = NMEA_SIG_BAD;
192                         } else if (!strncasecmp(POSFILE_VALUE_SIG_LOW, value, sizeof(line))) {
193                                 result.sig = NMEA_SIG_LOW;
194                         } else if (!strncasecmp(POSFILE_VALUE_SIG_MID, value, sizeof(line))) {
195                                 result.sig = NMEA_SIG_MID;
196                         } else if (!strncasecmp(POSFILE_VALUE_SIG_HIGH, value, sizeof(line))) {
197                                 result.sig = NMEA_SIG_HIGH;
198                         } else {
199                                 pudError(false, "Position file \"%s\", line %d uses an invalid value for \"%s\","
200                                                 " valid values are [%s|%s\%s|%s]", fileName, lineNumber, POSFILE_NAME_SIG,
201                                                 POSFILE_VALUE_SIG_BAD, POSFILE_VALUE_SIG_LOW, POSFILE_VALUE_SIG_MID, POSFILE_VALUE_SIG_HIGH);
202                                 goto out;
203                         }
204                         nmea_INFO_set_present(&result.present, SIG);
205                 } else if (!strncasecmp(POSFILE_NAME_FIX, name, sizeof(line))) {
206                         if (!strncasecmp(POSFILE_VALUE_FIX_BAD, value, sizeof(line))) {
207                                 result.fix = NMEA_FIX_BAD;
208                         } else if (!strncasecmp(POSFILE_VALUE_FIX_2D, value, sizeof(line))) {
209                                 result.fix = NMEA_FIX_2D;
210                         } else if (!strncasecmp(POSFILE_VALUE_FIX_3D, value, sizeof(line))) {
211                                 result.fix = NMEA_FIX_3D;
212                         } else {
213                                 pudError(false, "Position file \"%s\", line %d uses an invalid value for \"%s\","
214                                                 " valid values are [%s\%s|%s]", fileName, lineNumber, POSFILE_NAME_FIX, POSFILE_VALUE_FIX_BAD,
215                                                 POSFILE_VALUE_FIX_2D, POSFILE_VALUE_FIX_3D);
216                                 goto out;
217                         }
218                         nmea_INFO_set_present(&result.present, FIX);
219                 } else if (!strncasecmp(POSFILE_NAME_HDOP, name, sizeof(line))) {
220                         double val;
221                         if (!readDouble(POSFILE_NAME_HDOP, value, &val)) {
222                                 goto out;
223                         }
224
225                         result.HDOP = val;
226                         result.VDOP = POSFILE_CALCULATED_VDOP(result.HDOP);
227                         result.PDOP = POSFILE_CALCULATED_PDOP(result.HDOP);
228                         nmea_INFO_set_present(&result.present, HDOP);
229                         nmea_INFO_set_present(&result.present, VDOP);
230                         nmea_INFO_set_present(&result.present, PDOP);
231                 } else if (!strncasecmp(POSFILE_NAME_LAT, name, sizeof(line))) {
232                         double val;
233                         if (!readDouble(POSFILE_NAME_LAT, value, &val)) {
234                                 goto out;
235                         }
236
237                         result.lat = val;
238                         nmea_INFO_set_present(&result.present, LAT);
239                 } else if (!strncasecmp(POSFILE_NAME_LON, name, sizeof(line))) {
240                         double val;
241                         if (!readDouble(POSFILE_NAME_LON, value, &val)) {
242                                 goto out;
243                         }
244
245                         result.lon = val;
246                         nmea_INFO_set_present(&result.present, LON);
247                 } else if (!strncasecmp(POSFILE_NAME_ELV, name, sizeof(line))) {
248                         double val;
249                         if (!readDouble(POSFILE_NAME_ELV, value, &val)) {
250                                 goto out;
251                         }
252
253                         result.elv = val;
254                         nmea_INFO_set_present(&result.present, ELV);
255                 } else if (!strncasecmp(POSFILE_NAME_SPEED, name, sizeof(line))) {
256                         double val;
257                         if (!readDouble(POSFILE_NAME_SPEED, value, &val)) {
258                                 goto out;
259                         }
260
261                         result.speed = val;
262                         nmea_INFO_set_present(&result.present, SPEED);
263                 } else if (!strncasecmp(POSFILE_NAME_TRACK, name, sizeof(line))) {
264                         double val;
265                         if (!readDouble(POSFILE_NAME_TRACK, value, &val)) {
266                                 goto out;
267                         }
268
269                         result.track = val;
270                         nmea_INFO_set_present(&result.present, TRACK);
271                 } else if (!strncasecmp(POSFILE_NAME_MTRACK, name, sizeof(line))) {
272                         double val;
273                         if (!readDouble(POSFILE_NAME_MTRACK, value, &val)) {
274                                 goto out;
275                         }
276
277                         result.mtrack = val;
278                         nmea_INFO_set_present(&result.present, MTRACK);
279                 } else if (!strncasecmp(POSFILE_NAME_MAGVAR, name, sizeof(line))) {
280                         double val;
281                         if (!readDouble(POSFILE_NAME_MAGVAR, value, &val)) {
282                                 goto out;
283                         }
284
285                         result.magvar = val;
286                         nmea_INFO_set_present(&result.present, MAGVAR);
287                 } else {
288                         pudError(false, "Position file \"%s\", line %d uses an invalid option \"%s\","
289                                         " valid options are [%s|%s|%s|%s|%s|%s|%s|%s|%s|%s]", fileName, lineNumber, name, POSFILE_NAME_SIG,
290                                         POSFILE_NAME_FIX, POSFILE_NAME_HDOP, POSFILE_NAME_LAT, POSFILE_NAME_LON, POSFILE_NAME_ELV,
291                                         POSFILE_NAME_SPEED, POSFILE_NAME_TRACK, POSFILE_NAME_MTRACK, POSFILE_NAME_MAGVAR);
292                         goto out;
293                 }
294         }
295
296         fclose(fp);
297         fp = 0;
298
299         result.smask = POSFILE_DEFAULT_SMASK;
300         nmea_INFO_set_present(&result.present, SMASK);
301
302         nmea_INFO_sanitise(&result);
303         nmea_INFO_unit_conversion(&result);
304
305         memcpy(nmeaInfo, &result, sizeof(result));
306         retval = true;
307
308         out: if (fp) {
309                 fclose(fp);
310         }
311         if (fd >= 0) {
312                 close(fd);
313         }
314         return retval;
315 }