709a8e841c068f6950655b10c896717c2ac0c6c9
[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                 free(t);
181                 if (o == NULL)
182                         return false;
183                 if (l == NULL)
184                         *config = o;
185                 else
186                         l->next = o;
187                 return true;
188         }
189         free(o->value);
190         o->value = t;
191         return true;
192 }
193
194 bool
195 dhcpcd_config_set(DHCPCD_OPTION **config, const char *opt, const char *val)
196 {
197
198         assert(config);
199         assert(opt);
200         return dhcpcd_config_set1(config, opt, val, false);
201 }
202
203 bool
204 dhcpcd_config_set_static(DHCPCD_OPTION **config,
205     const char *opt, const char *val)
206 {
207
208         assert(config);
209         assert(opt);
210         return dhcpcd_config_set1(config, opt, val, true);
211 }
212
213 #define ACT_READ  (1 << 0)
214 #define ACT_WRITE (1 << 1)
215 #define ACT_LIST  (1 << 2)
216
217 static DHCPCD_OPTION *
218 config(DHCPCD_CONNECTION *con, int action, const char *block, const char *name,
219     const DHCPCD_OPTION *no, char ***list)
220 {
221         FILE *fp;
222         DHCPCD_OPTION *options, *o;
223         const DHCPCD_OPTION *co;
224         char *line, *option, *p;
225         char **buf, **nbuf;
226         int skip, free_opts;
227         size_t len, buf_size, buf_len, i;
228
229         fp = fopen(con->cffile, "r");
230         if (fp == NULL)
231                 return NULL;
232         options = o = NULL;
233         skip = block && !(action & ACT_LIST) ? 1 : 0;
234         buf = NULL;
235         buf_len = buf_size = 0;
236         free_opts = 1;
237         while (getline(&con->buf, &con->buflen, fp) != -1) {
238                 line = con->buf;
239                 /* Trim leading trailing newline and whitespace */
240                 while (*line == ' ' || *line == '\n' || *line == '\t')
241                         line++;
242                 /* Trim trailing newline and whitespace */
243                 if (line && *line) {
244                         p = line + strlen(line) - 1;
245                         while (p != line &&
246                             (*p == ' ' || *p == '\n' || *p == '\t') &&
247                             *(p - 1) != '\\')
248                                 *p-- = '\0';
249                 }
250                 option = strsep(&line, " \t");
251                 /* Trim trailing whitespace */
252                 if (line && *line) {
253                         p = line + strlen(line) - 1;
254                         while (p != line &&
255                             (*p == ' ' || *p == '\n' || *p == '\t') &&
256                             *(p - 1) != '\\')
257                                 *p-- = '\0';
258                 }
259                 if (action & ACT_LIST) {
260                         if (strcmp(option, block) == 0)
261                                 skip = 0;
262                         else
263                                 skip = 1;
264                 } else {
265                         /* Start of a block, skip if not ours */
266                         if (strcmp(option, "interface") == 0 ||
267                             strcmp(option, "ssid") == 0)
268                         {
269                                 if (block && name && line &&
270                                     strcmp(option, block) == 0 &&
271                                     strcmp(line, name) == 0)
272                                         skip = 0;
273                                 else
274                                         skip = 1;
275                                 if (!(action & ACT_WRITE))
276                                         continue;
277                         }
278                 }
279                 if ((action & ACT_WRITE && skip) ||
280                     (action & ACT_LIST && !skip))
281                 {
282                         if (buf_len + 2 > buf_size) {
283                                 buf_size += 32;
284                                 nbuf = realloc(buf, sizeof(char *) * buf_size);
285                                 if (nbuf == NULL)
286                                         goto exit;
287                                 buf = nbuf;
288                         }
289                         if (action & ACT_WRITE && line && *line != '\0') {
290                                 len = strlen(option) + strlen(line) + 2;
291                                 buf[buf_len] = malloc(len);
292                                 if (buf[buf_len] == NULL)
293                                         goto exit;
294                                 snprintf(buf[buf_len], len,
295                                     "%s %s", option, line);
296                         } else {
297                                 if (action & ACT_LIST)
298                                         buf[buf_len] = strdup(line);
299                                 else
300                                         buf[buf_len] = strdup(option);
301                                 if (buf[buf_len] == NULL)
302                                         goto exit;
303                         }
304                         buf_len++;
305                 }
306                 if (skip || action & ACT_LIST)
307                         continue;
308                 if (*option == '\0' || *option == '#' || *option == ';')
309                         continue;
310                 if (o == NULL)
311                         options = o = malloc(sizeof(*options));
312                 else {
313                         o->next = malloc(sizeof(*o));
314                         o = o->next;
315                 }
316                 if (o == NULL)
317                         goto exit;
318                 o->next = NULL;
319                 o->option = strdup(option);
320                 if (o->option == NULL) {
321                         o->value = NULL;
322                         goto exit;
323                 }
324                 if (line == NULL || *line == '\0')
325                         o->value = NULL;
326                 else {
327                         o->value = strdup(line);
328                         if (o->value == NULL)
329                                 goto exit;
330                 }
331         }
332
333         if (action & ACT_WRITE) {
334                 fp = freopen(con->cffile, "w", fp);
335                 if (fp == NULL)
336                         goto exit;
337                 if (block) {
338                         skip = 0;
339                         for (i = 0; i < buf_len; i++) {
340                                 fputs(buf[i], fp);
341                                 fputc('\n', fp);
342                                 skip = buf[i][0] == '\0' ? 1 : 0;
343                         }
344                 } else
345                         skip = 1;
346                 if (no && block) {
347                         if (!skip)
348                                 fputc('\n', fp);
349                         fprintf(fp, "%s %s\n", block, name);
350                 }
351                 skip = 0;
352                 for (co = no; co; co = co->next) {
353                         if (co->value)
354                                 fprintf(fp, "%s %s\n", co->option, co->value);
355                         else
356                                 fprintf(fp, "%s\n", co->option);
357                         skip = 1;
358                 }
359                 if (block == NULL) {
360                         if (!skip)
361                                 fputc('\n', fp);
362                         for (i = 0; i < buf_len; i++) {
363                                 fputs(buf[i], fp);
364                                 fputc('\n', fp);
365                         }
366                 }
367         } else
368                 free_opts = 0;
369
370 exit:
371         if (fp != NULL)
372                 fclose(fp);
373         if (action & ACT_LIST) {
374                 if (buf)
375                         buf[buf_len] = NULL;
376                 *list = buf;
377         } else {
378                 for (i = 0; i < buf_len; i++)
379                         free(buf[i]);
380                 free(buf);
381         }
382         if (free_opts) {
383                 dhcpcd_config_free(options);
384                 options = NULL;
385         }
386         return options;
387 }
388
389 DHCPCD_OPTION *
390 dhcpcd_config_read(DHCPCD_CONNECTION *con, const char *block, const char *name)
391 {
392
393         assert(con);
394         return config(con, ACT_READ, block, name, NULL, NULL);
395 }
396
397 bool
398 dhcpcd_config_write(DHCPCD_CONNECTION *con,
399     const char *block, const char *name,
400     const DHCPCD_OPTION *opts)
401 {
402         int serrno;
403
404         assert(con);
405         serrno = errno;
406         errno = 0;
407         config(con, ACT_WRITE, block, name, opts, NULL);
408         if (errno)
409                 return false;
410         errno = serrno;
411         return true;
412 }
413
414 char **
415 dhcpcd_config_blocks(DHCPCD_CONNECTION *con, const char *block)
416 {
417         char **blocks;
418
419         assert(con);
420         blocks = NULL;
421         config(con, ACT_LIST, block, NULL, NULL, &blocks);
422         return blocks;
423 }