Permalink
Cannot retrieve contributors at this time
| /* | |
| * libdhcpcd | |
| * Copyright 2009-2015 Roy Marples <roy@marples.name> | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions | |
| * are met: | |
| * 1. Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * 2. Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| * SUCH DAMAGE. | |
| */ | |
| #define _WITH_GETLINE /* For FreeBSD */ | |
| #include <assert.h> | |
| #include <errno.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| #define IN_LIBDHCPCD | |
| #include "dhcpcd.h" | |
| static DHCPCD_OPTION * | |
| dhcpcd_option_new(const char *opt, const char *val) | |
| { | |
| DHCPCD_OPTION *o; | |
| o = malloc(sizeof(*o)); | |
| if (o == NULL) | |
| return NULL; | |
| o->option = strdup(opt); | |
| if (o->option == NULL) { | |
| free(o); | |
| return NULL; | |
| } | |
| o->value = strdup(val); | |
| if (o->value == NULL) { | |
| free(o->option); | |
| free(o); | |
| return NULL; | |
| } | |
| o->next = NULL; | |
| return o; | |
| } | |
| static void | |
| dhcpcd_option_free(DHCPCD_OPTION *o) | |
| { | |
| free(o->option); | |
| free(o->value); | |
| free(o); | |
| } | |
| void | |
| dhcpcd_config_free(DHCPCD_OPTION *c) | |
| { | |
| DHCPCD_OPTION *n; | |
| while (c) { | |
| n = c->next; | |
| dhcpcd_option_free(c); | |
| c = n; | |
| } | |
| } | |
| static DHCPCD_OPTION * | |
| dhcpcd_config_get1(DHCPCD_OPTION *config, const char *opt, DHCPCD_OPTION **lst) | |
| { | |
| DHCPCD_OPTION *o; | |
| for (o = config; o; o = o->next) { | |
| if (strcmp(o->option, opt) == 0) | |
| return o; | |
| if (lst) | |
| *lst = o; | |
| } | |
| errno = ESRCH; | |
| return NULL; | |
| } | |
| const char * | |
| dhcpcd_config_get(DHCPCD_OPTION *config, const char *opt) | |
| { | |
| DHCPCD_OPTION *o; | |
| assert(opt); | |
| o = dhcpcd_config_get1(config, opt, NULL); | |
| if (o == NULL) | |
| return NULL; | |
| return o->value; | |
| } | |
| static DHCPCD_OPTION * | |
| dhcpcd_config_get_static1(DHCPCD_OPTION *config, const char *opt, | |
| DHCPCD_OPTION **lst) | |
| { | |
| DHCPCD_OPTION *o; | |
| size_t len; | |
| o = config; | |
| len = strlen(opt); | |
| while ((o = dhcpcd_config_get1(o, "static", lst)) != NULL) { | |
| if (strncmp(o->value, opt, len) == 0) | |
| return o; | |
| if (lst) | |
| *lst = o; | |
| o = o->next; | |
| } | |
| return NULL; | |
| } | |
| const char * | |
| dhcpcd_config_get_static(DHCPCD_OPTION *config, const char *opt) | |
| { | |
| DHCPCD_OPTION *o; | |
| assert(opt); | |
| o = dhcpcd_config_get_static1(config, opt, NULL); | |
| if (o == NULL) | |
| return NULL; | |
| return o->value + strlen(opt); | |
| } | |
| static bool | |
| dhcpcd_config_set1(DHCPCD_OPTION **config, const char *opt, const char *val, | |
| bool s) | |
| { | |
| DHCPCD_OPTION *o, *l; | |
| char *t; | |
| size_t len; | |
| l = NULL; | |
| if (s) | |
| o = dhcpcd_config_get_static1(*config, opt, &l); | |
| else | |
| o = dhcpcd_config_get1(*config, opt, &l); | |
| if (val == NULL) { | |
| if (o == NULL) | |
| return true; | |
| if (o == *config) | |
| *config = o->next; | |
| else if (l != NULL) | |
| l->next = o->next; | |
| free(o->option); | |
| free(o->value); | |
| free(o); | |
| return true; | |
| } | |
| if (s) { | |
| len = strlen(opt) + strlen(val) + 2; | |
| t = malloc(len); | |
| if (t == NULL) | |
| return false; | |
| snprintf(t, len, "%s%s", opt, val); | |
| } else { | |
| t = strdup(val); | |
| if (t == NULL) | |
| return false; | |
| } | |
| if (o == NULL) { | |
| if (s) | |
| o = dhcpcd_option_new("static", t); | |
| else | |
| o = dhcpcd_option_new(opt, val); | |
| free(t); | |
| if (o == NULL) | |
| return false; | |
| if (l == NULL) | |
| *config = o; | |
| else | |
| l->next = o; | |
| return true; | |
| } | |
| free(o->value); | |
| o->value = t; | |
| return true; | |
| } | |
| bool | |
| dhcpcd_config_set(DHCPCD_OPTION **config, const char *opt, const char *val) | |
| { | |
| assert(config); | |
| assert(opt); | |
| return dhcpcd_config_set1(config, opt, val, false); | |
| } | |
| bool | |
| dhcpcd_config_set_static(DHCPCD_OPTION **config, | |
| const char *opt, const char *val) | |
| { | |
| assert(config); | |
| assert(opt); | |
| return dhcpcd_config_set1(config, opt, val, true); | |
| } | |
| #define ACT_READ (1 << 0) | |
| #define ACT_WRITE (1 << 1) | |
| #define ACT_LIST (1 << 2) | |
| static DHCPCD_OPTION * | |
| config(DHCPCD_CONNECTION *con, int action, const char *block, const char *name, | |
| const DHCPCD_OPTION *no, char ***list) | |
| { | |
| FILE *fp; | |
| DHCPCD_OPTION *options, *o; | |
| const DHCPCD_OPTION *co; | |
| char *line, *option, *p; | |
| char **buf, **nbuf; | |
| int skip, free_opts; | |
| size_t len, buf_size, buf_len, i; | |
| fp = fopen(con->cffile, "r"); | |
| if (fp == NULL) | |
| return NULL; | |
| options = o = NULL; | |
| skip = block && !(action & ACT_LIST) ? 1 : 0; | |
| buf = NULL; | |
| buf_len = buf_size = 0; | |
| free_opts = 1; | |
| while (getline(&con->buf, &con->buflen, fp) != -1) { | |
| line = con->buf; | |
| /* Trim leading trailing newline and whitespace */ | |
| while (*line == ' ' || *line == '\n' || *line == '\t') | |
| line++; | |
| /* Trim trailing newline and whitespace */ | |
| if (line && *line) { | |
| p = line + strlen(line) - 1; | |
| while (p != line && | |
| (*p == ' ' || *p == '\n' || *p == '\t') && | |
| *(p - 1) != '\\') | |
| *p-- = '\0'; | |
| } | |
| option = strsep(&line, " \t"); | |
| /* Trim trailing whitespace */ | |
| if (line && *line) { | |
| p = line + strlen(line) - 1; | |
| while (p != line && | |
| (*p == ' ' || *p == '\n' || *p == '\t') && | |
| *(p - 1) != '\\') | |
| *p-- = '\0'; | |
| } | |
| if (action & ACT_LIST) { | |
| if (strcmp(option, block) == 0) | |
| skip = 0; | |
| else | |
| skip = 1; | |
| } else { | |
| /* Start of a block, skip if not ours */ | |
| if (strcmp(option, "interface") == 0 || | |
| strcmp(option, "ssid") == 0 || | |
| strcmp(option, "profile") == 0 || | |
| strcmp(option, "fallback") == 0) | |
| { | |
| if (block && name && line && | |
| strcmp(option, block) == 0 && | |
| strcmp(line, name) == 0) | |
| skip = 0; | |
| else | |
| skip = 1; | |
| if (!(action & ACT_WRITE)) | |
| continue; | |
| } | |
| } | |
| if ((action & ACT_WRITE && skip) || | |
| (action & ACT_LIST && !skip)) | |
| { | |
| if (buf_len + 2 > buf_size) { | |
| buf_size += 32; | |
| nbuf = realloc(buf, sizeof(char *) * buf_size); | |
| if (nbuf == NULL) | |
| goto exit; | |
| buf = nbuf; | |
| } | |
| if (action & ACT_WRITE && line && *line != '\0') { | |
| len = strlen(option) + strlen(line) + 2; | |
| buf[buf_len] = malloc(len); | |
| if (buf[buf_len] == NULL) | |
| goto exit; | |
| snprintf(buf[buf_len], len, | |
| "%s %s", option, line); | |
| } else { | |
| if (action & ACT_LIST) | |
| buf[buf_len] = strdup(line); | |
| else | |
| buf[buf_len] = strdup(option); | |
| if (buf[buf_len] == NULL) | |
| goto exit; | |
| } | |
| buf_len++; | |
| } | |
| if (skip || action & ACT_LIST) | |
| continue; | |
| if (*option == '\0' || *option == '#' || *option == ';') | |
| continue; | |
| if (o == NULL) | |
| options = o = malloc(sizeof(*options)); | |
| else { | |
| o->next = malloc(sizeof(*o)); | |
| o = o->next; | |
| } | |
| if (o == NULL) | |
| goto exit; | |
| o->next = NULL; | |
| o->option = strdup(option); | |
| if (o->option == NULL) { | |
| o->value = NULL; | |
| goto exit; | |
| } | |
| if (line == NULL || *line == '\0') | |
| o->value = NULL; | |
| else { | |
| o->value = strdup(line); | |
| if (o->value == NULL) | |
| goto exit; | |
| } | |
| } | |
| if (action & ACT_WRITE) { | |
| fp = freopen(con->cffile, "w", fp); | |
| if (fp == NULL) | |
| goto exit; | |
| if (block) { | |
| skip = 0; | |
| for (i = 0; i < buf_len; i++) { | |
| fputs(buf[i], fp); | |
| fputc('\n', fp); | |
| skip = buf[i][0] == '\0' ? 1 : 0; | |
| } | |
| } else | |
| skip = 1; | |
| if (no && block) { | |
| if (!skip) | |
| fputc('\n', fp); | |
| fprintf(fp, "%s %s\n", block, name); | |
| } | |
| skip = 0; | |
| for (co = no; co; co = co->next) { | |
| if (co->value) | |
| fprintf(fp, "%s %s\n", co->option, co->value); | |
| else | |
| fprintf(fp, "%s\n", co->option); | |
| skip = 1; | |
| } | |
| if (block == NULL) { | |
| if (!skip) | |
| fputc('\n', fp); | |
| for (i = 0; i < buf_len; i++) { | |
| fputs(buf[i], fp); | |
| fputc('\n', fp); | |
| } | |
| } | |
| } else | |
| free_opts = 0; | |
| exit: | |
| if (fp != NULL) | |
| fclose(fp); | |
| if (action & ACT_LIST) { | |
| if (buf) | |
| buf[buf_len] = NULL; | |
| *list = buf; | |
| } else { | |
| for (i = 0; i < buf_len; i++) | |
| free(buf[i]); | |
| free(buf); | |
| } | |
| if (free_opts) { | |
| dhcpcd_config_free(options); | |
| options = NULL; | |
| } | |
| return options; | |
| } | |
| DHCPCD_OPTION * | |
| dhcpcd_config_read(DHCPCD_CONNECTION *con, const char *block, const char *name) | |
| { | |
| assert(con); | |
| return config(con, ACT_READ, block, name, NULL, NULL); | |
| } | |
| bool | |
| dhcpcd_config_writeable(DHCPCD_CONNECTION *con) | |
| { | |
| return (access(con->cffile, W_OK) == 0); | |
| } | |
| bool | |
| dhcpcd_config_write(DHCPCD_CONNECTION *con, | |
| const char *block, const char *name, | |
| const DHCPCD_OPTION *opts) | |
| { | |
| int serrno; | |
| assert(con); | |
| serrno = errno; | |
| errno = 0; | |
| config(con, ACT_WRITE, block, name, opts, NULL); | |
| if (errno) | |
| return false; | |
| errno = serrno; | |
| return true; | |
| } | |
| char ** | |
| dhcpcd_config_blocks(DHCPCD_CONNECTION *con, const char *block) | |
| { | |
| char **blocks; | |
| assert(con); | |
| blocks = NULL; | |
| config(con, ACT_LIST, block, NULL, NULL, &blocks); | |
| return blocks; | |
| } |