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