sgwdynspeed: errno is not set by strtoul function, so don't use it
[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         struct timespec st_mtim; /* Time of last modification. */
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         int result;
93
94         if (started) {
95                 return true;
96         }
97
98         result = regcomp(&regexComment, regexCommentString, REG_EXTENDED | REG_ICASE);
99         if (result) {
100                 char msgbuf[256];
101                 regerror(result, &regexComment, msgbuf, sizeof(msgbuf));
102                 sgwDynSpeedError(false, "Could not compile regex \"%s\": %s", regexCommentString, msgbuf);
103                 return false;
104         }
105
106         result = regcomp(&regexNameValue, regexNameValueString, REG_EXTENDED | REG_ICASE);
107         if (result) {
108                 char msgbuf[256];
109                 regerror(result, &regexNameValue, msgbuf, sizeof(msgbuf));
110                 sgwDynSpeedError(false, "Could not compile regex \"%s\": %s", regexNameValueString, msgbuf);
111                 regfree(&regexComment);
112                 return false;
113         }
114
115         cachedStat.st_mtim.tv_sec = -1;
116         cachedStat.st_mtim.tv_nsec = -1;
117
118         started = true;
119         return true;
120 }
121
122 /**
123  * Cleans up the speedFile reader.
124  */
125 void stopSpeedFile(void) {
126         if (started) {
127                 regfree(&regexNameValue);
128                 regfree(&regexComment);
129                 started = false;
130         }
131 }
132
133 /**
134  * Performs a regex match
135  * @param regex the compiled regex to match against
136  * @param line the line to match
137  * @param nmatch the number of matches to produce
138  * @param pmatch the array with match information
139  * @return true upon success, false otherwise
140  */
141 static bool regexMatch(regex_t * regex, char * line, size_t nmatch, regmatch_t pmatch[]) {
142         int result = regexec(regex, line, nmatch, pmatch, 0);
143         if (!result) {
144                 return true;
145         }
146
147         if (result == REG_NOMATCH) {
148                 return false;
149         }
150
151         {
152                 char msgbuf[256];
153                 regerror(result, regex, msgbuf, sizeof(msgbuf));
154                 sgwDynSpeedError(false, "Regex match failed: %s", msgbuf);
155         }
156
157         return false;
158 }
159
160 /** the buffer in which to store a line read from the file */
161 static char line[LINE_LENGTH];
162
163 /**
164  * Read the speed file
165  * @param fileName the filename
166  */
167 void readSpeedFile(char * fileName) {
168         struct stat statBuf;
169         FILE * fd = NULL;
170         unsigned int lineNumber = 0;
171         char * name = NULL;
172         char * value = NULL;
173         unsigned long uplink = DEF_UPLINK_SPEED;
174         unsigned long downlink = DEF_DOWNLINK_SPEED;
175         bool uplinkSet = false;
176         bool downlinkSet = false;
177
178         if (stat(fileName, &statBuf)) {
179                 /* could not access the file */
180                 goto out;
181         }
182
183         if (!memcmp(&cachedStat.st_mtim, &statBuf.st_mtim, sizeof(cachedStat.st_mtim))) {
184                 /* file did not change since last read */
185                 goto out;
186         }
187
188         fd = fopen(fileName, "r");
189         if (!fd) {
190                 goto out;
191         }
192
193         memcpy(&cachedStat.st_mtim, &statBuf.st_mtim, sizeof(cachedStat.st_mtim));
194
195         while (fgets(line, LINE_LENGTH, fd)) {
196                 regmatch_t pmatch[regexNameValuematchCount];
197
198                 lineNumber++;
199
200                 if (regexMatch(&regexComment, line, 0, NULL)) {
201                         continue;
202                 }
203
204                 if (!regexMatch(&regexNameValue, line, regexNameValuematchCount, pmatch)) {
205                         sgwDynSpeedError(false, "Gateway speed file \"%s\", line %d uses invalid syntax: %s", fileName, lineNumber,
206                                         line);
207                         goto out;
208                 }
209
210                 /* determine name/value */
211                 name = &line[pmatch[1].rm_so];
212                 line[pmatch[1].rm_eo] = '\0';
213                 value = &line[pmatch[2].rm_so];
214                 line[pmatch[2].rm_eo] = '\0';
215
216                 if (!strncasecmp(SPEED_UPLINK_NAME, name, sizeof(line))) {
217                         if (!readUL(SPEED_UPLINK_NAME, value, &uplink)) {
218                                 goto out;
219                         }
220                         uplinkSet = true;
221                 } else if (!strncasecmp(SPEED_DOWNLINK_NAME, name, sizeof(line))) {
222                         if (!readUL(SPEED_DOWNLINK_NAME, value, &downlink)) {
223                                 goto out;
224                         }
225                         downlinkSet = true;
226                 } else {
227                         sgwDynSpeedError(false, "Gateway speed file \"%s\", line %d uses an invalid option \"%s\","
228                                         " valid options are [%s|%s]", fileName, lineNumber, name, SPEED_UPLINK_NAME, SPEED_DOWNLINK_NAME);
229                         goto out;
230                 }
231         }
232
233         fclose(fd);
234
235         if (uplinkSet) {
236                 olsr_cnf->smart_gw_uplink = uplink;
237         }
238         if (downlinkSet) {
239                 olsr_cnf->smart_gw_downlink = downlink;
240         }
241
242         out: return;
243 }