Only start new wpa events for link types and if we don't have one already open.
[dhcpcd-ui] / src / libdhcpcd / config.c
1 /*
2  * libdhcpcd
3  * Copyright 2009-2014 Roy Marples <roy@marples.name>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <assert.h>
28 #include <errno.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #define IN_LIBDHCPCD
34
35 #include "dhcpcd.h"
36
37 static DHCPCD_OPTION *
38 dhcpcd_option_new(const char *opt, const char *val)
39 {
40         DHCPCD_OPTION *o;
41
42         o = malloc(sizeof(*o));
43         if (o == NULL)
44                 return NULL;
45         o->option = strdup(opt);
46         if (o->option == NULL) {
47                 free(o);
48                 return NULL;
49         }
50         o->value = strdup(val);
51         if (o->value == NULL) {
52                 free(o->option);
53                 free(o);
54                 return NULL;
55         }
56         o->next = NULL;
57         return o;
58 }
59
60 static void
61 dhcpcd_option_free(DHCPCD_OPTION *o)
62 {
63
64         free(o->option);
65         free(o->value);
66         free(o);
67 }
68
69 void
70 dhcpcd_config_free(DHCPCD_OPTION *c)
71 {
72         DHCPCD_OPTION *n;
73
74         while (c) {
75                 n = c->next;
76                 dhcpcd_option_free(c);
77                 c = n;
78         }
79 }
80
81 static DHCPCD_OPTION *
82 dhcpcd_config_get1(DHCPCD_OPTION *config, const char *opt, DHCPCD_OPTION **lst)
83 {
84         DHCPCD_OPTION *o;
85
86         for (o = config; o; o = o->next) {
87                 if (strcmp(o->option, opt) == 0)
88                         return o;
89                 if (lst)
90                         *lst = o;
91         }
92         errno = ESRCH;
93         return NULL;
94 }
95
96 const char *
97 dhcpcd_config_get(DHCPCD_OPTION *config, const char *opt)
98 {
99         DHCPCD_OPTION *o;
100
101         assert(opt);
102         o = dhcpcd_config_get1(config, opt, NULL);
103         if (o == NULL)
104                 return NULL;
105         return o->value;
106 }
107
108 static DHCPCD_OPTION *
109 dhcpcd_config_get_static1(DHCPCD_OPTION *config, const char *opt,
110     DHCPCD_OPTION **lst)
111 {
112         DHCPCD_OPTION *o;
113         size_t len;
114
115         o = config;
116         len = strlen(opt);
117         while ((o = dhcpcd_config_get1(o, "static", lst)) != NULL) {
118                 if (strncmp(o->value, opt, len) == 0)
119                         return o;
120                 if (lst)
121                         *lst = o;
122                 o = o->next;
123         }
124         return NULL;
125 }
126
127 const char *
128 dhcpcd_config_get_static(DHCPCD_OPTION *config, const char *opt)
129 {
130         DHCPCD_OPTION *o;
131
132         assert(opt);
133         o = dhcpcd_config_get_static1(config, opt, NULL);
134         if (o == NULL)
135                 return NULL;
136         return o->value + strlen(opt);
137 }
138
139 static bool
140 dhcpcd_config_set1(DHCPCD_OPTION **config, const char *opt, const char *val,
141     bool s)
142 {
143         DHCPCD_OPTION *o, *l;
144         char *t;
145         size_t len;
146
147         l = NULL;
148         if (s)
149                 o = dhcpcd_config_get_static1(*config, opt, &l);
150         else
151                 o = dhcpcd_config_get1(*config, opt, &l);
152         if (val == NULL) {
153                 if (o == NULL)
154                         return true;
155                 if (o == *config)
156                         *config = o->next;
157                 else if (l != NULL)
158                         l->next = o->next;
159                 free(o->option);
160                 free(o->value);
161                 free(o);
162                 return true;
163         }
164         if (s) {
165                 len = strlen(opt) + strlen(val) + 2;
166                 t = malloc(len);
167                 if (t == NULL)
168                         return false;
169                 snprintf(t, len, "%s%s", opt, val);
170         } else {
171                 t = strdup(val);
172                 if (t == NULL)
173                         return false;
174         }
175         if (o == NULL) {
176                 if (s)
177                         o = dhcpcd_option_new("static", t);
178                 else
179                         o = dhcpcd_option_new(opt, val);
180                 if (o == NULL)
181                         return false;
182                 if (l == NULL)
183                         *config = o;
184                 else
185                         l->next = o;
186                 return true;
187         }
188         free(o->value);
189         o->value = t;
190         return true;
191 }
192
193 bool
194 dhcpcd_config_set(DHCPCD_OPTION **config, const char *opt, const char *val)
195 {
196
197         assert(config);
198         assert(opt);
199         return dhcpcd_config_set1(config, opt, val, false);
200 }
201
202 bool
203 dhcpcd_config_set_static(DHCPCD_OPTION **config,
204     const char *opt, const char *val)
205 {
206
207         assert(config);
208         assert(opt);
209         return dhcpcd_config_set1(config, opt, val, true);
210 }
211
212 #define ACT_READ  (1 << 0)
213 #define ACT_WRITE (1 << 1)
214 #define ACT_LIST  (1 << 2)
215
216 static DHCPCD_OPTION *
217 config(DHCPCD_CONNECTION *con, int action, const char *block, const char *name,
218     const DHCPCD_OPTION *no, char ***list)
219 {
220         FILE *fp;
221         DHCPCD_OPTION *options, *o;
222         const DHCPCD_OPTION *co;
223         char *line, *option, *p;
224         char **buf, **nbuf;
225         int skip, free_opts;
226         size_t len, buf_size, buf_len, i;
227
228         fp = fopen(con->cffile, "r");
229         if (fp == NULL)
230                 return NULL;
231         options = o = NULL;
232         skip = block && !(action & ACT_LIST) ? 1 : 0;
233         buf = NULL;
234         buf_len = buf_size = 0;
235         free_opts = 1;
236         while (getline(&con->buf, &con->buflen, fp) != -1) {
237                 line = con->buf;
238                 /* Trim leading trailing newline and whitespace */
239                 while (*line == ' ' || *line == '\n' || *line == '\t')
240                         line++;
241                 /* Trim trailing newline and whitespace */
242                 if (line && *line) {
243                         p = line + strlen(line) - 1;
244                         while (p != line &&
245                             (*p == ' ' || *p == '\n' || *p == '\t') &&
246                             *(p - 1) != '\\')
247                                 *p-- = '\0';
248                 }
249                 option = strsep(&line, " \t");
250                 /* Trim trailing whitespace */
251                 if (line && *line) {
252                         p = line + strlen(line) - 1;
253                         while (p != line &&
254                             (*p == ' ' || *p == '\n' || *p == '\t') &&
255                             *(p - 1) != '\\')
256                                 *p-- = '\0';
257                 }
258                 if (action & ACT_LIST) {
259                         if (strcmp(option, block) == 0)
260                                 skip = 0;
261                         else
262                                 skip = 1;
263                 } else {
264                         /* Start of a block, skip if not ours */
265                         if (strcmp(option, "interface") == 0 ||
266                             strcmp(option, "ssid") == 0)
267                         {
268                                 if (block && name && line &&
269                                     strcmp(option, block) == 0 &&
270                                     strcmp(line, name) == 0)
271                                         skip = 0;
272                                 else
273                                         skip = 1;
274                                 if (!(action & ACT_WRITE))
275                                         continue;
276                         }
277                 }
278                 if ((action & ACT_WRITE && skip) ||
279                     (action & ACT_LIST && !skip))
280                 {
281                         if (buf_len + 2 > buf_size) {
282                                 buf_size += 32;
283                                 nbuf = realloc(buf, sizeof(char *) * buf_size);
284                                 if (nbuf == NULL)
285                                         goto exit;
286                                 buf = nbuf;
287                         }
288                         if (action & ACT_WRITE && line && *line != '\0') {
289                                 len = strlen(option) + strlen(line) + 2;
290                                 buf[buf_len] = malloc(len);
291                                 if (buf[buf_len] == NULL)
292                                         goto exit;
293                                 snprintf(buf[buf_len], len,
294                                     "%s %s", option, line);
295                         } else {
296                                 if (action & ACT_LIST)
297                                         buf[buf_len] = strdup(line);
298                                 else
299                                         buf[buf_len] = strdup(option);
300                                 if (buf[buf_len] == NULL)
301                                         goto exit;
302                         }
303                         buf_len++;
304                 }
305                 if (skip || action & ACT_LIST)
306                         continue;
307                 if (*option == '\0' || *option == '#' || *option == ';')
308                         continue;
309                 if (o == NULL)
310                         options = o = malloc(sizeof(*options));
311                 else {
312                         o->next = malloc(sizeof(*o));
313                         o = o->next;
314                 }
315                 if (o == NULL)
316                         goto exit;
317                 o->next = NULL;
318                 o->option = strdup(option);
319                 if (o->option == NULL) {
320                         o->value = NULL;
321                         goto exit;
322                 }
323                 if (line == NULL || *line == '\0')
324                         o->value = NULL;
325                 else {
326                         o->value = strdup(line);
327                         if (o->value == NULL)
328                                 goto exit;
329                 }
330         }
331
332         if (action & ACT_WRITE) {
333                 fp = freopen(con->cffile, "w", fp);
334                 if (fp == NULL)
335                         goto exit;
336                 if (block) {
337                         skip = 0;
338                         for (i = 0; i < buf_len; i++) {
339                                 fputs(buf[i], fp);
340                                 fputc('\n', fp);
341                                 skip = buf[i][0] == '\0' ? 1 : 0;
342                         }
343                 } else
344                         skip = 1;
345                 if (no && block) {
346                         if (!skip)
347                                 fputc('\n', fp);
348                         fprintf(fp, "%s %s\n", block, name);
349                 }
350                 skip = 0;
351                 for (co = no; co; co = co->next) {
352                         if (co->value)
353                                 fprintf(fp, "%s %s\n", co->option, co->value);
354                         else
355                                 fprintf(fp, "%s\n", co->option);
356                         skip = 1;
357                 }
358                 if (block == NULL) {
359                         if (!skip)
360                                 fputc('\n', fp);
361                         for (i = 0; i < buf_len; i++) {
362                                 fputs(buf[i], fp);
363                                 fputc('\n', fp);
364                         }
365                 }
366         } else
367                 free_opts = 0;
368
369 exit:
370         if (fp != NULL)
371                 fclose(fp);
372         if (action & ACT_LIST) {
373                 if (buf)
374                         buf[buf_len] = NULL;
375                 *list = buf;
376         } else {
377                 for (i = 0; i < buf_len; i++)
378                         free(buf[i]);
379                 free(buf);
380         }
381         if (free_opts) {
382                 dhcpcd_config_free(options);
383                 options = NULL;
384         }
385         return options;
386 }
387
388 DHCPCD_OPTION *
389 dhcpcd_config_read(DHCPCD_CONNECTION *con, const char *block, const char *name)
390 {
391
392         assert(con);
393         return config(con, ACT_READ, block, name, NULL, NULL);
394 }
395
396 bool
397 dhcpcd_config_write(DHCPCD_CONNECTION *con,
398     const char *block, const char *name,
399     const DHCPCD_OPTION *opts)
400 {
401         int serrno;
402
403         assert(con);
404         serrno = errno;
405         errno = 0;
406         config(con, ACT_WRITE, block, name, opts, NULL);
407         if (errno)
408                 return false;
409         errno = serrno;
410         return true;
411 }
412
413 char **
414 dhcpcd_config_blocks(DHCPCD_CONNECTION *con, const char *block)
415 {
416         char **blocks;
417
418         assert(con);
419         blocks = NULL;
420         config(con, ACT_LIST, block, NULL, NULL, &blocks);
421         return blocks;
422 }