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