print error on deprecated config items
[freeradius.git] / src / main / conffile.c
index 4ea9535..fb2c48b 100644 (file)
@@ -122,7 +122,6 @@ struct conf_part {
 typedef struct cf_file_t {
        char const      *filename;
        CONF_SECTION    *cs;
-       bool            input;
        struct stat     buf;
 } cf_file_t;
 
@@ -333,7 +332,6 @@ static FILE *cf_file_open(CONF_SECTION *cs, char const *filename)
 
        file->filename = filename;
        file->cs = cs;
-       file->input = true;
 
        if (fstat(fd, &file->buf) == 0) {
 #ifdef S_IWOTH
@@ -362,10 +360,9 @@ static FILE *cf_file_open(CONF_SECTION *cs, char const *filename)
 }
 
 /*
- *     Do some checks on the file as an "input" file.  i.e. one read
- *     by a module.
+ *     Do some checks on the file
  */
-static bool cf_file_input(CONF_SECTION *cs, char const *filename)
+static bool cf_file_check(CONF_SECTION *cs, char const *filename, bool check_perms)
 {
        cf_file_t *file;
        CONF_DATA *cd;
@@ -383,14 +380,18 @@ static bool cf_file_input(CONF_SECTION *cs, char const *filename)
 
        file->filename = filename;
        file->cs = cs;
-       file->input = true;
 
        if (stat(filename, &file->buf) < 0) {
-               ERROR("Unable to open file \"%s\": %s", filename, fr_syserror(errno));
+               ERROR("Unable to check file \"%s\": %s", filename, fr_syserror(errno));
                talloc_free(file);
                return false;
        }
 
+       if (!check_perms) {
+               talloc_free(file);
+               return true;
+       }
+
 #ifdef S_IWOTH
        if ((file->buf.st_mode & S_IWOTH) != 0) {
                ERROR("Configuration file %s is globally writable.  "
@@ -440,11 +441,13 @@ static int file_callback(void *ctx, void *data)
         *      The file changed, we'll need to re-read it.
         */
        if (buf.st_mtime != file->buf.st_mtime) {
-               if (!file->input) {
-                       cb->rcode |= CF_FILE_CONFIG;
-               } else {
-                       (void) cb->callback(cb->modules, file->cs);
+
+               if (cb->callback(cb->modules, file->cs)) {
                        cb->rcode |= CF_FILE_MODULE;
+                       DEBUG3("HUP: Changed module file %s", file->filename);
+               } else {
+                       DEBUG3("HUP: Changed config file %s", file->filename);
+                       cb->rcode |= CF_FILE_CONFIG;
                }
        }
 
@@ -1196,7 +1199,7 @@ static char const *cf_expand_variables(char const *cf, int *lineno,
                                ERROR("%s[%d]: Reference \"%s\" type is invalid", cf, *lineno, input);
                                return NULL;
                        }
-               } else if (memcmp(ptr, "$ENV{", 5) == 0) {
+               } else if (strncmp(ptr, "$ENV{", 5) == 0) {
                        char *env;
 
                        ptr += 5;
@@ -1384,21 +1387,24 @@ static inline int fr_item_validate_ipaddr(CONF_SECTION *cs, char const *name, PW
 int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *data, char const *dflt)
 {
        int rcode;
-       bool deprecated, required, attribute, secret, file_input, cant_be_empty, tmpl, multi;
+       bool deprecated, required, attribute, secret, file_input, cant_be_empty, tmpl, multi, file_exists;
        char **q;
        char const *value;
        CONF_PAIR *cp = NULL;
        fr_ipaddr_t *ipaddr;
        char buffer[8192];
-       CONF_ITEM *c_item = &cs->item;
+       CONF_ITEM *c_item;
 
        if (!cs) return -1;
 
+       c_item = &cs->item;
+
        deprecated = (type & PW_TYPE_DEPRECATED);
        required = (type & PW_TYPE_REQUIRED);
        attribute = (type & PW_TYPE_ATTRIBUTE);
        secret = (type & PW_TYPE_SECRET);
        file_input = (type == PW_TYPE_FILE_INPUT);      /* check, not and */
+       file_exists = (type == PW_TYPE_FILE_EXISTS);    /* check, not and */
        cant_be_empty = (type & PW_TYPE_NOT_EMPTY);
        tmpl = (type & PW_TYPE_TMPL);
        multi = (type & PW_TYPE_MULTI);
@@ -1434,28 +1440,42 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
         */
        } else {
                CONF_PAIR *next = cp;
+
                value = cp->value;
                cp->parsed = true;
                c_item = &cp->item;
 
-               /*
-                *      @fixme We should actually validate
-                *      the value of the pairs too
-                */
-               if (multi) while ((next = cf_pair_find_next(cs, next, name))) {
-                       next->parsed = true;
-               }
-
                if (deprecated) {
                        cf_log_err(c_item, "Configuration item \"%s\" is deprecated", name);
-
                        return -2;
                }
+
+               /*
+                *      A quick check to see if the next item is the same.
+                */
+               if (!multi && cp->item.next && (cp->item.next->type == CONF_ITEM_PAIR)) {
+                       next = cf_item_to_pair(cp->item.next);
+
+                       if (strcmp(next->attr, name) == 0) {
+                               WARN("%s[%d]: Ignoring duplicate configuration item '%s'",
+                                    next->item.filename ? next->item.filename : "unknown",
+                                    next->item.lineno, name);
+                       }
+               }
+                                                                                  
+               if (multi) {
+                       while ((next = cf_pair_find_next(cs, next, name)) != NULL) {
+                               /*
+                                *      @fixme We should actually validate
+                                *      the value of the pairs too
+                                */
+                               next->parsed = true;
+                       };
+               }
        }
 
        if (!value) {
                if (required) {
-               is_required:
                        cf_log_err(c_item, "Configuration item \"%s\" must have a value", name);
 
                        return -1;
@@ -1601,7 +1621,6 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
                        }
                }
 
-               if (required && !value) goto is_required;
                if (cant_be_empty && (value[0] == '\0')) goto cant_be_empty;
 
                if (attribute) {
@@ -1634,7 +1653,11 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
                 *      to be caught as early as possible, during
                 *      server startup.
                 */
-               if (*q && file_input && !cf_file_input(cs, *q)) {
+               if (*q && file_input && !cf_file_check(cs, *q, true)) {
+                       return -1;
+               }
+
+               if (*q && file_exists && !cf_file_check(cs, *q, false)) {
                        return -1;
                }
                break;
@@ -1905,6 +1928,8 @@ int cf_section_parse(CONF_SECTION *cs, void *base, CONF_PARSER const *variables)
                            (variables[i + 1].data == variables[i].data)) {
                                cf_log_err(&(cs->item), "Replace \"%s\" with \"%s\"", variables[i].name,
                                           variables[i + 1].name);
+                       } else {
+                               cf_log_err(&(cs->item), "Cannot use deprecated configuration item \"%s\"", variables[i].name);
                        }
                        goto finish;
                }
@@ -3601,6 +3626,7 @@ void *cf_data_remove(CONF_SECTION *cs, char const *name)
 {
        CONF_DATA mycd;
        CONF_DATA *cd;
+       CONF_ITEM *ci, *it;
        void *data;
 
        if (!cs || !name) return NULL;
@@ -3614,6 +3640,20 @@ void *cf_data_remove(CONF_SECTION *cs, char const *name)
        cd = rbtree_finddata(cs->data_tree, &mycd);
        if (!cd) return NULL;
 
+       ci = cf_data_to_item(cd);
+       if (cs->children == ci) {
+               cs->children = ci->next;
+               if (cs->tail == ci) cs->tail = NULL;
+       } else {
+               for (it = cs->children; it; it = it->next) {
+                       if (it->next == ci) {
+                               it->next = ci->next;
+                               if (cs->tail == ci) cs->tail = it;
+                               break;
+                       }
+               }
+       }
+
        talloc_set_destructor(cd, NULL);        /* Disarm the destructor */
        rbtree_deletebydata(cs->data_tree, &mycd);