sgwdynspeed: do not use nanosecond stat information
[olsrd.git] / lib / sgwdynspeed / src / speedFile.c
1 #include "speedFile.h"
2
3 /* Plugin includes */
4 #include "sgwDynSpeed.h"
5
6 /* OLSRD includes */
7 #include "olsr_cfg.h"
8
9 /* System includes */
10 #include <stddef.h>
11 #include <stdlib.h>
12 #include <regex.h>
13 #include <errno.h>
14 #include <sys/stat.h>
15 #include <stdio.h>
16 #include <assert.h>
17
18 #define SPEED_UPLINK_NAME   "upstream"
19 #define SPEED_DOWNLINK_NAME "downstream"
20
21 /** the maximal length of a line that is read from the file */
22 #define LINE_LENGTH 256
23
24 /** regular expression describing a comment */
25 static const char * regexCommentString = "^([[:space:]]*|[[:space:]#]+.*)$";
26
27 /** regular expression describing a key/value pair */
28 static const char * regexNameValueString =
29                 "^[[:space:]]*([^[:space:]]+)[[:space:]]*=[[:space:]]*([[:digit:]]+)[[:space:]]*$";
30
31 /** the number of matches in regexNameValueString */
32 static const size_t regexNameValuematchCount = 3;
33
34 /** the compiled regular expression describing a comment */
35 static regex_t regexComment;
36
37 /** the compiled regular expression describing a key/value pair */
38 static regex_t regexNameValue;
39
40 /** true when the plugin has been started */
41 static bool started = false;
42
43 /** type to hold the cached stat result */
44 typedef struct _CachedStat {
45         time_t timeStamp; /* Time of last modification (second resolution) */
46 } CachedStat;
47
48 /** the cached stat result */
49 static CachedStat cachedStat;
50
51 /**
52  Read an unsigned long number from a value string
53
54  @param valueName
55  the name of the value
56  @param value
57  the string to convert to a number
58  @param valueNumber
59  a pointer to the location where to store the number upon successful conversion
60
61  @return
62  - true on success
63  - false otherwise
64  */
65 static bool readUL(const char * valueName, const char * value, unsigned long * valueNumber) {
66         char * endPtr = NULL;
67         unsigned long valueNew;
68
69         assert(valueName != NULL);
70         assert(value != NULL);
71         assert(valueNumber != NULL);
72
73         errno = 0;
74         valueNew = strtoul(value, &endPtr, 10);
75
76         if (!((endPtr != value) && (*value != '\0') && (*endPtr == '\0'))) {
77                 /* invalid conversion */
78                 sgwDynSpeedError(false, "Value of parameter %s (%s) could not be converted to a number", valueName, value);
79                 return false;
80         }
81
82         *valueNumber = valueNew;
83
84         return true;
85 }
86
87 /**
88  * Initialises the speedFile reader.
89  * @return true upon success, false otherwise
90  */
91 bool startSpeedFile(void) {
92         if (started) {
93                 return true;
94         }
95
96         if (regcomp(&regexComment, regexCommentString, REG_EXTENDED | REG_ICASE)) {
97                 sgwDynSpeedError(false, "Could not compile regex \"%s\"", regexCommentString);
98                 return false;
99         }
100
101         if (regcomp(&regexNameValue, regexNameValueString, REG_EXTENDED | REG_ICASE)) {
102                 sgwDynSpeedError(false, "Could not compile regex \"%s\"", regexNameValueString);
103                 regfree(&regexComment);
104                 return false;
105         }
106
107         cachedStat.timeStamp = -1;
108
109         started = true;
110         return true;
111 }
112
113 /**
114  * Cleans up the speedFile reader.
115  */
116 void stopSpeedFile(void) {
117         if (started) {
118                 regfree(&regexNameValue);
119                 regfree(&regexComment);
120                 started = false;
121         }
122 }
123
124 /**
125  * Performs a regex match
126  * @param regex the compiled regex to match against
127  * @param line the line to match
128  * @param nmatch the number of matches to produce
129  * @param pmatch the array with match information
130  * @return true upon success, false otherwise
131  */
132 static bool regexMatch(regex_t * regex, char * line, size_t nmatch, regmatch_t pmatch[]) {
133         int result = regexec(regex, line, nmatch, pmatch, 0);
134         if (!result) {
135                 return true;
136         }
137
138         if (result == REG_NOMATCH) {
139                 return false;
140         }
141
142         {
143                 char msgbuf[256];
144                 regerror(result, regex, msgbuf, sizeof(msgbuf));
145                 sgwDynSpeedError(false, "Regex match failed: %s", msgbuf);
146         }
147
148         return false;
149 }
150
151 /** the buffer in which to store a line read from the file */
152 static char line[LINE_LENGTH];
153
154 /**
155  * Read the speed file
156  * @param fileName the filename
157  */
158 void readSpeedFile(char * fileName) {
159         struct stat statBuf;
160         FILE * fd = NULL;
161         unsigned int lineNumber = 0;
162         char * name = NULL;
163         char * value = NULL;
164         unsigned long uplink = DEF_UPLINK_SPEED;
165         unsigned long downlink = DEF_DOWNLINK_SPEED;
166         bool uplinkSet = false;
167         bool downlinkSet = false;
168
169         if (stat(fileName, &statBuf)) {
170                 /* could not access the file */
171                 goto out;
172         }
173
174         if (!memcmp(&cachedStat.timeStamp, &statBuf.st_mtime, sizeof(cachedStat.timeStamp))) {
175                 /* file did not change since last read */
176                 goto out;
177         }
178
179         fd = fopen(fileName, "r");
180         if (!fd) {
181                 goto out;
182         }
183
184         memcpy(&cachedStat.timeStamp, &statBuf.st_mtime, sizeof(cachedStat.timeStamp));
185
186         while (fgets(line, LINE_LENGTH, fd)) {
187                 regmatch_t pmatch[regexNameValuematchCount];
188
189                 lineNumber++;
190
191                 if (regexMatch(&regexComment, line, 0, NULL)) {
192                         continue;
193                 }
194
195                 if (!regexMatch(&regexNameValue, line, regexNameValuematchCount, pmatch)) {
196                         sgwDynSpeedError(false, "Gateway speed file \"%s\", line %d uses invalid syntax: %s", fileName, lineNumber,
197                                         line);
198                         goto out;
199                 }
200
201                 /* determine name/value */
202                 name = &line[pmatch[1].rm_so];
203                 line[pmatch[1].rm_eo] = '\0';
204                 value = &line[pmatch[2].rm_so];
205                 line[pmatch[2].rm_eo] = '\0';
206
207                 if (!strncasecmp(SPEED_UPLINK_NAME, name, sizeof(line))) {
208                         if (!readUL(SPEED_UPLINK_NAME, value, &uplink)) {
209                                 goto out;
210                         }
211                         uplinkSet = true;
212                 } else if (!strncasecmp(SPEED_DOWNLINK_NAME, name, sizeof(line))) {
213                         if (!readUL(SPEED_DOWNLINK_NAME, value, &downlink)) {
214                                 goto out;
215                         }
216                         downlinkSet = true;
217                 } else {
218                         sgwDynSpeedError(false, "Gateway speed file \"%s\", line %d uses an invalid option \"%s\","
219                                         " valid options are [%s|%s]", fileName, lineNumber, name, SPEED_UPLINK_NAME, SPEED_DOWNLINK_NAME);
220                         goto out;
221                 }
222         }
223
224         fclose(fd);
225
226         if (uplinkSet) {
227                 olsr_cnf->smart_gw_uplink = uplink;
228         }
229         if (downlinkSet) {
230                 olsr_cnf->smart_gw_downlink = downlink;
231         }
232
233         out: return;
234 }