Fix compile on Linux and fix some memory errors.
[dhcpcd-ui] / src / libdhcpcd / config.c
1 /*
2  * libdhcpcd
3  * Copyright 2009-2015 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 #define _WITH_GETLINE           /* For FreeBSD */
28
29 #include <assert.h>
30 #include <errno.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35
36 #define IN_LIBDHCPCD
37
38 #include "dhcpcd.h"
39
40 static DHCPCD_OPTION *
41 dhcpcd_option_new(const char *opt, const char *val)
42 {
43         DHCPCD_OPTION *o;
44
45         o = malloc(sizeof(*o));
46         if (o == NULL)
47                 return NULL;
48         o->option = strdup(opt);
49         if (o->option == NULL) {
50                 free(o);
51                 return NULL;
52         }
53         o->value = strdup(val);
54         if (o->value == NULL) {
55                 free(o->option);
56                 free(o);
57                 return NULL;
58         }
59         o->next = NULL;
60         return o;
61 }
62
63 static void
64 dhcpcd_option_free(DHCPCD_OPTION *o)
65 {
66
67         free(o->option);
68         free(o->value);
69         free(o);
70 }
71
72 void
73 dhcpcd_config_free(DHCPCD_OPTION *c)
74 {
75         DHCPCD_OPTION *n;
76
77         while (c) {
78                 n = c->next;
79                 dhcpcd_option_free(c);
80                 c = n;
81         }
82 }
83
84 static DHCPCD_OPTION *
85 dhcpcd_config_get1(DHCPCD_OPTION *config, const char *opt, DHCPCD_OPTION **lst)
86 {
87         DHCPCD_OPTION *o;
88
89         for (o = config; o; o = o->next) {
90                 if (strcmp(o->option, opt) == 0)
91                         return o;
92                 if (lst)
93                         *lst = o;
94         }
95         errno = ESRCH;
96         return NULL;
97 }
98
99 const char *
100 dhcpcd_config_get(DHCPCD_OPTION *config, const char *opt)
101 {
102         DHCPCD_OPTION *o;
103
104         assert(opt);
105         o = dhcpcd_config_get1(config, opt, NULL);
106         if (o == NULL)
107                 return NULL;
108         return o->value;
109 }
110
111 static DHCPCD_OPTION *
112 dhcpcd_config_get_static1(DHCPCD_OPTION *config, const char *opt,
113     DHCPCD_OPTION **lst)
114 {
115         DHCPCD_OPTION *o;
116         size_t len;
117
118         o = config;
119         len = strlen(opt);
120         while ((o = dhcpcd_config_get1(o, "static", lst)) != NULL) {
121                 if (strncmp(o->value, opt, len) == 0)
122                         return o;
123                 if (lst)
124                         *lst = o;
125                 o = o->next;
126         }
127         return NULL;
128 }
129
130 const char *
131 dhcpcd_config_get_static(DHCPCD_OPTION *config, const char *opt)
132 {
133         DHCPCD_OPTION *o;
134
135         assert(opt);
136         o = dhcpcd_config_get_static1(config, opt, NULL);
137         if (o == NULL)
138                 return NULL;
139         return o->value + strlen(opt);
140 }
141
142 static bool
143 dhcpcd_config_set1(DHCPCD_OPTION **config, const char *opt, const char *val,
144     bool s)
145 {
146         DHCPCD_OPTION *o, *l;
147         char *t;
148         size_t len;
149
150         l = NULL;
151         if (s)
152                 o = dhcpcd_config_get_static1(*config, opt, &l);
153         else
154                 o = dhcpcd_config_get1(*config, opt, &l);
155         if (val == NULL) {
156                 if (o == NULL)
157                         return true;
158                 if (o == *config)
159                         *config = o->next;
160                 else if (l != NULL)
161                         l->next = o->next;
162                 free(o->option);
163                 free(o->value);
164                 free(o);
165                 return true;
166         }
167         if (s) {
168                 len = strlen(opt) + strlen(val) + 2;
169                 t = malloc(len);
170                 if (t == NULL)
171                         return false;
172                 snprintf(t, len, "%s%s", opt, val);
173         } else {
174                 t = strdup(val);
175                 if (t == NULL)
176                         return false;
177         }
178         if (o == NULL) {
179                 if (s)
180                         o = dhcpcd_option_new("static", t);
181                 else
182                         o = dhcpcd_option_new(opt, val);
183                 free(t);
184                 if (o == NULL)
185                         return false;
186                 if (l == NULL)
187                         *config = o;
188                 else
189                         l->next = o;
190                 return true;
191         }
192         free(o->value);
193         o->value = t;
194         return true;
195 }
196
197 bool
198 dhcpcd_config_set(DHCPCD_OPTION **config, const char *opt, const char *val)
199 {
200
201         assert(config);
202         assert(opt);
203         return dhcpcd_config_set1(config, opt, val, false);
204 }
205
206 bool
207 dhcpcd_config_set_static(DHCPCD_OPTION **config,
208     const char *opt, const char *val)
209 {
210
211         assert(config);
212         assert(opt);
213         return dhcpcd_config_set1(config, opt, val, true);
214 }
215
216 #define ACT_READ  (1 << 0)
217 #define ACT_WRITE (1 << 1)
218 #define ACT_LIST  (1 << 2)
219
220 static DHCPCD_OPTION *
221 config(DHCPCD_CONNECTION *con, int action, const char *block, const char *name,
222     const DHCPCD_OPTION *no, char ***list)
223 {
224         FILE *fp;
225         DHCPCD_OPTION *options, *o;
226         const DHCPCD_OPTION *co;
227         char *line, *option, *p;
228         char **buf, **nbuf;
229         int skip, free_opts;
230         size_t len, buf_size, buf_len, i;
231
232         fp = fopen(con->cffile, "r");
233         if (fp == NULL)
234                 return NULL;
235         options = o = NULL;
236         skip = block && !(action & ACT_LIST) ? 1 : 0;
237         buf = NULL;
238         buf_len = buf_size = 0;
239         free_opts = 1;
240         while (getline(&con->buf, &con->buflen, fp) != -1) {
241                 line = con->buf;
242                 /* Trim leading trailing newline and whitespace */
243                 while (*line == ' ' || *line == '\n' || *line == '\t')
244                         line++;
245                 /* Trim trailing newline and whitespace */
246                 if (line && *line) {
247                         p = line + strlen(line) - 1;
248                         while (p != line &&
249                             (*p == ' ' || *p == '\n' || *p == '\t') &&
250                             *(p - 1) != '\\')
251                                 *p-- = '\0';
252                 }
253                 option = strsep(&line, " \t");
254                 /* Trim trailing whitespace */
255                 if (line && *line) {
256                         p = line + strlen(line) - 1;
257                         while (p != line &&
258                             (*p == ' ' || *p == '\n' || *p == '\t') &&
259                             *(p - 1) != '\\')
260                                 *p-- = '\0';
261                 }
262                 if (action & ACT_LIST) {
263                         if (strcmp(option, block) == 0)
264                                 skip = 0;
265                         else
266                                 skip = 1;
267                 } else {
268                         /* Start of a block, skip if not ours */
269                         if (strcmp(option, "interface") == 0 ||
270                             strcmp(option, "ssid") == 0)
271                         {
272                                 if (block && name && line &&
273                                     strcmp(option, block) == 0 &&
274                                     strcmp(line, name) == 0)
275                                         skip = 0;
276                                 else
277                                         skip = 1;
278                                 if (!(action & ACT_WRITE))
279                                         continue;
280                         }
281                 }
282                 if ((action & ACT_WRITE && skip) ||
283                     (action & ACT_LIST && !skip))
284                 {
285                         if (buf_len + 2 > buf_size) {
286                                 buf_size += 32;
287                                 nbuf = realloc(buf, sizeof(char *) * buf_size);
288                                 if (nbuf == NULL)
289                                         goto exit;
290                                 buf = nbuf;
291                         }
292                         if (action & ACT_WRITE && line && *line != '\0') {
293                                 len = strlen(option) + strlen(line) + 2;
294                                 buf[buf_len] = malloc(len);
295                                 if (buf[buf_len] == NULL)
296                                         goto exit;
297                                 snprintf(buf[buf_len], len,
298                                     "%s %s", option, line);
299                         } else {
300                                 if (action & ACT_LIST)
301                                         buf[buf_len] = strdup(line);
302                                 else
303                                         buf[buf_len] = strdup(option);
304                                 if (buf[buf_len] == NULL)
305                                         goto exit;
306                         }
307                         buf_len++;
308                 }
309                 if (skip || action & ACT_LIST)
310                         continue;
311                 if (*option == '\0' || *option == '#' || *option == ';')
312                         continue;
313                 if (o == NULL)
314                         options = o = malloc(sizeof(*options));
315                 else {
316                         o->next = malloc(sizeof(*o));
317                         o = o->next;
318                 }
319                 if (o == NULL)
320                         goto exit;
321                 o->next = NULL;
322                 o->option = strdup(option);
323                 if (o->option == NULL) {
324                         o->value = NULL;
325                         goto exit;
326                 }
327                 if (line == NULL || *line == '\0')
328                         o->value = NULL;
329                 else {
330                         o->value = strdup(line);
331                         if (o->value == NULL)
332                                 goto exit;
333                 }
334         }
335
336         if (action & ACT_WRITE) {
337                 fp = freopen(con->cffile, "w", fp);
338                 if (fp == NULL)
339                         goto exit;
340                 if (block) {
341                         skip = 0;
342                         for (i = 0; i < buf_len; i++) {
343                                 fputs(buf[i], fp);
344                                 fputc('\n', fp);
345                                 skip = buf[i][0] == '\0' ? 1 : 0;
346                         }
347                 } else
348                         skip = 1;
349                 if (no && block) {
350                         if (!skip)
351                                 fputc('\n', fp);
352                         fprintf(fp, "%s %s\n", block, name);
353                 }
354                 skip = 0;
355                 for (co = no; co; co = co->next) {
356                         if (co->value)
357                                 fprintf(fp, "%s %s\n", co->option, co->value);
358                         else
359                                 fprintf(fp, "%s\n", co->option);
360                         skip = 1;
361                 }
362                 if (block == NULL) {
363                         if (!skip)
364                                 fputc('\n', fp);
365                         for (i = 0; i < buf_len; i++) {
366                                 fputs(buf[i], fp);
367                                 fputc('\n', fp);
368                         }
369                 }
370         } else
371                 free_opts = 0;
372
373 exit:
374         if (fp != NULL)
375                 fclose(fp);
376         if (action & ACT_LIST) {
377                 if (buf)
378                         buf[buf_len] = NULL;
379                 *list = buf;
380         } else {
381                 for (i = 0; i < buf_len; i++)
382                         free(buf[i]);
383                 free(buf);
384         }
385         if (free_opts) {
386                 dhcpcd_config_free(options);
387                 options = NULL;
388         }
389         return options;
390 }
391
392 DHCPCD_OPTION *
393 dhcpcd_config_read(DHCPCD_CONNECTION *con, const char *block, const char *name)
394 {
395
396         assert(con);
397         return config(con, ACT_READ, block, name, NULL, NULL);
398 }
399
400 bool
401 dhcpcd_config_writeable(DHCPCD_CONNECTION *con)
402 {
403
404         return (access(con->cffile, W_OK) == 0);
405 }
406
407 bool
408 dhcpcd_config_write(DHCPCD_CONNECTION *con,
409     const char *block, const char *name,
410     const DHCPCD_OPTION *opts)
411 {
412         int serrno;
413
414         assert(con);
415         serrno = errno;
416         errno = 0;
417         config(con, ACT_WRITE, block, name, opts, NULL);
418         if (errno)
419                 return false;
420         errno = serrno;
421         return true;
422 }
423
424 char **
425 dhcpcd_config_blocks(DHCPCD_CONNECTION *con, const char *block)
426 {
427         char **blocks;
428
429         assert(con);
430         blocks = NULL;
431         config(con, ACT_LIST, block, NULL, NULL, &blocks);
432         return blocks;
433 }