/* * Configuration parser * * Copyright 2000-2011 Willy Tarreau * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * */ /* This is to have crypt() and sched_setaffinity() defined on Linux */ #define _GNU_SOURCE #ifdef USE_LIBCRYPT #ifdef USE_CRYPT_H /* some platforms such as Solaris need this */ #include #endif #endif /* USE_LIBCRYPT */ #include #include #include #include #include #include #include #include #include #ifdef USE_CPU_AFFINITY #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_CPU_AFFINITY #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Used to chain configuration sections definitions. This list * stores struct cfg_section */ struct list sections = LIST_HEAD_INIT(sections); struct list postparsers = LIST_HEAD_INIT(postparsers); extern struct proxy *mworker_proxy; /* curproxy is only valid during parsing and will be NULL afterwards. */ struct proxy *curproxy = NULL; /* last defaults section parsed, NULL after parsing */ struct proxy *last_defproxy = NULL; char *cursection = NULL; int cfg_maxpconn = 0; /* # of simultaneous connections per proxy (-N) */ int cfg_maxconn = 0; /* # of simultaneous connections, (-n) */ char *cfg_scope = NULL; /* the current scope during the configuration parsing */ int non_global_section_parsed = 0; /* how to handle default paths */ static enum default_path_mode { DEFAULT_PATH_CURRENT = 0, /* "current": paths are relative to CWD (this is the default) */ DEFAULT_PATH_CONFIG, /* "config": paths are relative to config file */ DEFAULT_PATH_PARENT, /* "parent": paths are relative to config file's ".." */ DEFAULT_PATH_ORIGIN, /* "origin": paths are relative to default_path_origin */ } default_path_mode; char initial_cwd[PATH_MAX]; static char current_cwd[PATH_MAX]; /* List head of all known configuration keywords */ struct cfg_kw_list cfg_keywords = { .list = LIST_HEAD_INIT(cfg_keywords.list) }; /* * Shifts one position to the left. * This function tricky preserves internal allocated structure of the * . We defer the deallocation of the "shifted off" element, by * making it an empty string and moving it into the gap that appears after * the shift. */ static void lshift_args(char **args) { int i; char *shifted; shifted = args[0]; for (i = 0; *args[i + 1]; i++) args[i] = args[i + 1]; *shifted = '\0'; args[i] = shifted; } /* * converts to a list of listeners which are dynamically allocated. * The format is "{addr|'*'}:port[-end][,{addr|'*'}:port[-end]]*", where : * - can be empty or "*" to indicate INADDR_ANY ; * - is a numerical port from 1 to 65535 ; * - indicates to use the range from to instead (inclusive). * This can be repeated as many times as necessary, separated by a coma. * Function returns 1 for success or 0 if error. In case of errors, if is * not NULL, it must be a valid pointer to either NULL or a freeable area that * will be replaced with an error message. */ int str2listener(char *str, struct proxy *curproxy, struct bind_conf *bind_conf, const char *file, int line, char **err) { struct protocol *proto; char *next, *dupstr; int port, end; next = dupstr = strdup(str); while (next && *next) { struct sockaddr_storage *ss2; int fd = -1; str = next; /* 1) look for the end of the first address */ if ((next = strchr(str, ',')) != NULL) { *next++ = 0; } ss2 = str2sa_range(str, NULL, &port, &end, &fd, &proto, NULL, err, (curproxy == global.cli_fe || curproxy == mworker_proxy) ? NULL : global.unix_bind.prefix, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_PORT_RANGE | PA_O_SOCKET_FD | PA_O_STREAM | PA_O_XPRT); if (!ss2) goto fail; if (ss2->ss_family == AF_CUST_RHTTP_SRV) { /* Check if a previous non reverse HTTP present is * already defined. If DGRAM or STREAM is set, this * indicates that we are currently parsing the second * or more address. */ if (bind_conf->options & (BC_O_USE_SOCK_DGRAM|BC_O_USE_SOCK_STREAM) && !(bind_conf->options & BC_O_REVERSE_HTTP)) { memprintf(err, "Cannot mix reverse HTTP bind with others.\n"); goto fail; } bind_conf->rhttp_srvname = strdup(str + strlen("rhttp@")); if (!bind_conf->rhttp_srvname) { memprintf(err, "Cannot allocate reverse HTTP bind.\n"); goto fail; } bind_conf->options |= BC_O_REVERSE_HTTP; } else if (bind_conf->options & BC_O_REVERSE_HTTP) { /* Standard address mixed with a previous reverse HTTP one. */ memprintf(err, "Cannot mix reverse HTTP bind with others.\n"); goto fail; } /* OK the address looks correct */ if (proto->proto_type == PROTO_TYPE_DGRAM) bind_conf->options |= BC_O_USE_SOCK_DGRAM; else bind_conf->options |= BC_O_USE_SOCK_STREAM; if (proto->xprt_type == PROTO_TYPE_DGRAM) bind_conf->options |= BC_O_USE_XPRT_DGRAM; else bind_conf->options |= BC_O_USE_XPRT_STREAM; if (!create_listeners(bind_conf, ss2, port, end, fd, proto, err)) { memprintf(err, "%s for address '%s'.\n", *err, str); goto fail; } } /* end while(next) */ free(dupstr); return 1; fail: free(dupstr); return 0; } /* * converts to a list of datagram-oriented listeners which are dynamically * allocated. * The format is "{addr|'*'}:port[-end][,{addr|'*'}:port[-end]]*", where : * - can be empty or "*" to indicate INADDR_ANY ; * - is a numerical port from 1 to 65535 ; * - indicates to use the range from to instead (inclusive). * This can be repeated as many times as necessary, separated by a coma. * Function returns 1 for success or 0 if error. In case of errors, if is * not NULL, it must be a valid pointer to either NULL or a freeable area that * will be replaced with an error message. */ int str2receiver(char *str, struct proxy *curproxy, struct bind_conf *bind_conf, const char *file, int line, char **err) { struct protocol *proto; char *next, *dupstr; int port, end; next = dupstr = strdup(str); while (next && *next) { struct sockaddr_storage *ss2; int fd = -1; str = next; /* 1) look for the end of the first address */ if ((next = strchr(str, ',')) != NULL) { *next++ = 0; } ss2 = str2sa_range(str, NULL, &port, &end, &fd, &proto, NULL, err, curproxy == global.cli_fe ? NULL : global.unix_bind.prefix, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_PORT_RANGE | PA_O_SOCKET_FD | PA_O_DGRAM | PA_O_XPRT); if (!ss2) goto fail; /* OK the address looks correct */ if (!create_listeners(bind_conf, ss2, port, end, fd, proto, err)) { memprintf(err, "%s for address '%s'.\n", *err, str); goto fail; } } /* end while(next) */ free(dupstr); return 1; fail: free(dupstr); return 0; } /* * Sends a warning if proxy does not have at least one of the * capabilities in . An optional may be added at the end * of the warning to help the user. Returns 1 if a warning was emitted * or 0 if the condition is valid. */ int warnifnotcap(struct proxy *proxy, int cap, const char *file, int line, const char *arg, const char *hint) { char *msg; switch (cap) { case PR_CAP_BE: msg = "no backend"; break; case PR_CAP_FE: msg = "no frontend"; break; case PR_CAP_BE|PR_CAP_FE: msg = "neither frontend nor backend"; break; default: msg = "not enough"; break; } if (!(proxy->cap & cap)) { ha_warning("parsing [%s:%d] : '%s' ignored because %s '%s' has %s capability.%s\n", file, line, arg, proxy_type_str(proxy), proxy->id, msg, hint ? hint : ""); return 1; } return 0; } /* * Sends an alert if proxy does not have at least one of the * capabilities in . An optional may be added at the end * of the alert to help the user. Returns 1 if an alert was emitted * or 0 if the condition is valid. */ int failifnotcap(struct proxy *proxy, int cap, const char *file, int line, const char *arg, const char *hint) { char *msg; switch (cap) { case PR_CAP_BE: msg = "no backend"; break; case PR_CAP_FE: msg = "no frontend"; break; case PR_CAP_BE|PR_CAP_FE: msg = "neither frontend nor backend"; break; default: msg = "not enough"; break; } if (!(proxy->cap & cap)) { ha_alert("parsing [%s:%d] : '%s' not allowed because %s '%s' has %s capability.%s\n", file, line, arg, proxy_type_str(proxy), proxy->id, msg, hint ? hint : ""); return 1; } return 0; } /* * Report an error in when there are too many arguments. This version is * intended to be used by keyword parsers so that the message will be included * into the general error message. The index is the current keyword in args. * Return 0 if the number of argument is correct, otherwise build a message and * return 1. Fill err_code with an ERR_ALERT and an ERR_FATAL if not null. The * message may also be null, it will simply not be produced (useful to check only). * and are only affected on error. */ int too_many_args_idx(int maxarg, int index, char **args, char **msg, int *err_code) { int i; if (!*args[index + maxarg + 1]) return 0; if (msg) { *msg = NULL; memprintf(msg, "%s", args[0]); for (i = 1; i <= index; i++) memprintf(msg, "%s %s", *msg, args[i]); memprintf(msg, "'%s' cannot handle unexpected argument '%s'.", *msg, args[index + maxarg + 1]); } if (err_code) *err_code |= ERR_ALERT | ERR_FATAL; return 1; } /* * same as too_many_args_idx with a 0 index */ int too_many_args(int maxarg, char **args, char **msg, int *err_code) { return too_many_args_idx(maxarg, 0, args, msg, err_code); } /* * Report a fatal Alert when there is too much arguments * The index is the current keyword in args * Return 0 if the number of argument is correct, otherwise emit an alert and return 1 * Fill err_code with an ERR_ALERT and an ERR_FATAL */ int alertif_too_many_args_idx(int maxarg, int index, const char *file, int linenum, char **args, int *err_code) { char *kw = NULL; int i; if (!*args[index + maxarg + 1]) return 0; memprintf(&kw, "%s", args[0]); for (i = 1; i <= index; i++) { memprintf(&kw, "%s %s", kw, args[i]); } ha_alert("parsing [%s:%d] : '%s' cannot handle unexpected argument '%s'.\n", file, linenum, kw, args[index + maxarg + 1]); free(kw); *err_code |= ERR_ALERT | ERR_FATAL; return 1; } /* * same as alertif_too_many_args_idx with a 0 index */ int alertif_too_many_args(int maxarg, const char *file, int linenum, char **args, int *err_code) { return alertif_too_many_args_idx(maxarg, 0, file, linenum, args, err_code); } /* Report it if a request ACL condition uses some keywords that are * incompatible with the place where the ACL is used. It returns either 0 or * ERR_WARN so that its result can be or'ed with err_code. Note that may * be NULL and then will be ignored. In case of error, is dynamically * allocated to contains a description. */ int warnif_cond_conflicts(const struct acl_cond *cond, unsigned int where, char **err) { const struct acl *acl; const char *kw; if (!cond) return 0; acl = acl_cond_conflicts(cond, where); if (acl) { if (acl->name && *acl->name) { memprintf(err, "acl '%s' will never match because it only involves keywords that are incompatible with '%s'", acl->name, sample_ckp_names(where)); } else { memprintf(err, "anonymous acl will never match because it uses keyword '%s' which is incompatible with '%s'", LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw, sample_ckp_names(where)); } return ERR_WARN; } if (!acl_cond_kw_conflicts(cond, where, &acl, &kw)) return 0; if (acl->name && *acl->name) { memprintf(err, "acl '%s' involves keywords '%s' which is incompatible with '%s'", acl->name, kw, sample_ckp_names(where)); } else { memprintf(err, "anonymous acl involves keyword '%s' which is incompatible with '%s'", kw, sample_ckp_names(where)); } return ERR_WARN; } /* Report it if an ACL uses a L6 sample fetch from an HTTP proxy. It returns * either 0 or ERR_WARN so that its result can be or'ed with err_code. Note that * may be NULL and then will be ignored. */ int warnif_tcp_http_cond(const struct proxy *px, const struct acl_cond *cond) { if (!cond || px->mode != PR_MODE_HTTP) return 0; if (cond->use & (SMP_USE_L6REQ|SMP_USE_L6RES)) { ha_warning("Proxy '%s': L6 sample fetches ignored on HTTP proxies (declared at %s:%d).\n", px->id, cond->file, cond->line); return ERR_WARN; } return 0; } /* try to find in the word that looks closest to by counting * transitions between letters, digits and other characters. Will return the * best matching word if found, otherwise NULL. An optional array of extra * words to compare may be passed in , but it must then be terminated * by a NULL entry. If unused it may be NULL. */ const char *cfg_find_best_match(const char *word, const struct list *list, int section, const char **extra) { uint8_t word_sig[1024]; // 0..25=letter, 26=digit, 27=other, 28=begin, 29=end uint8_t list_sig[1024]; const struct cfg_kw_list *kwl; int index; const char *best_ptr = NULL; int dist, best_dist = INT_MAX; make_word_fingerprint(word_sig, word); list_for_each_entry(kwl, list, list) { for (index = 0; kwl->kw[index].kw != NULL; index++) { if (kwl->kw[index].section != section) continue; make_word_fingerprint(list_sig, kwl->kw[index].kw); dist = word_fingerprint_distance(word_sig, list_sig); if (dist < best_dist) { best_dist = dist; best_ptr = kwl->kw[index].kw; } } } while (extra && *extra) { make_word_fingerprint(list_sig, *extra); dist = word_fingerprint_distance(word_sig, list_sig); if (dist < best_dist) { best_dist = dist; best_ptr = *extra; } extra++; } if (best_dist > 2 * strlen(word) || (best_ptr && best_dist > 2 * strlen(best_ptr))) best_ptr = NULL; return best_ptr; } /* Parse a string representing a process number or a set of processes. It must * be "all", "odd", "even", a number between 1 and or a range with * two such numbers delimited by a dash ('-'). On success, it returns * 0. otherwise it returns 1 with an error message in . * * Note: this function can also be used to parse a thread number or a set of * threads. */ int parse_process_number(const char *arg, unsigned long *proc, int max, int *autoinc, char **err) { if (autoinc) { *autoinc = 0; if (strncmp(arg, "auto:", 5) == 0) { arg += 5; *autoinc = 1; } } if (strcmp(arg, "all") == 0) *proc |= ~0UL; else if (strcmp(arg, "odd") == 0) *proc |= ~0UL/3UL; /* 0x555....555 */ else if (strcmp(arg, "even") == 0) *proc |= (~0UL/3UL) << 1; /* 0xAAA...AAA */ else { const char *p, *dash = NULL; unsigned int low, high; for (p = arg; *p; p++) { if (*p == '-' && !dash) dash = p; else if (!isdigit((unsigned char)*p)) { memprintf(err, "'%s' is not a valid number/range.", arg); return -1; } } low = high = str2uic(arg); if (dash) high = ((!*(dash+1)) ? max : str2uic(dash + 1)); if (high < low) { unsigned int swap = low; low = high; high = swap; } if (low < 1 || low > max || high > max) { memprintf(err, "'%s' is not a valid number/range." " It supports numbers from 1 to %d.\n", arg, max); return 1; } for (;low <= high; low++) *proc |= 1UL << (low-1); } *proc &= ~0UL >> (LONGBITS - max); return 0; } /* * Parse a line in a , or section. * Returns the error code, 0 if OK, or any combination of : * - ERR_ABORT: must abort ASAP * - ERR_FATAL: we can continue parsing but not start the service * - ERR_WARN: a warning has been emitted * - ERR_ALERT: an alert has been emitted * Only the two first ones can stop processing, the two others are just * indicators. */ int cfg_parse_mailers(const char *file, int linenum, char **args, int kwm) { static struct mailers *curmailers = NULL; struct mailer *newmailer = NULL; const char *err; int err_code = 0; char *errmsg = NULL; if (strcmp(args[0], "mailers") == 0) { /* new mailers section */ if (!*args[1]) { ha_alert("parsing [%s:%d] : missing name for mailers section.\n", file, linenum); err_code |= ERR_ALERT | ERR_ABORT; goto out; } err = invalid_char(args[1]); if (err) { ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n", file, linenum, *err, args[0], args[1]); err_code |= ERR_ALERT | ERR_ABORT; goto out; } for (curmailers = mailers; curmailers != NULL; curmailers = curmailers->next) { /* * If there are two proxies with the same name only following * combinations are allowed: */ if (strcmp(curmailers->id, args[1]) == 0) { ha_alert("Parsing [%s:%d]: mailers section '%s' has the same name as another mailers section declared at %s:%d.\n", file, linenum, args[1], curmailers->conf.file, curmailers->conf.line); err_code |= ERR_ALERT | ERR_FATAL; } } if ((curmailers = calloc(1, sizeof(*curmailers))) == NULL) { ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum); err_code |= ERR_ALERT | ERR_ABORT; goto out; } curmailers->next = mailers; mailers = curmailers; curmailers->conf.file = strdup(file); curmailers->conf.line = linenum; curmailers->id = strdup(args[1]); curmailers->timeout.mail = DEF_MAILALERTTIME;/* XXX: Would like to Skip to the next alert, if any, ASAP. * But need enough time so that timeouts don't occur * during tcp procssing. For now just us an arbitrary default. */ } else if (strcmp(args[0], "mailer") == 0) { /* mailer definition */ struct sockaddr_storage *sk; int port1, port2; struct protocol *proto; if (!*args[2]) { ha_alert("parsing [%s:%d] : '%s' expects and [:] as arguments.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } err = invalid_char(args[1]); if (err) { ha_alert("parsing [%s:%d] : character '%c' is not permitted in server name '%s'.\n", file, linenum, *err, args[1]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } if ((newmailer = calloc(1, sizeof(*newmailer))) == NULL) { ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum); err_code |= ERR_ALERT | ERR_ABORT; goto out; } /* the mailers are linked backwards first */ curmailers->count++; newmailer->next = curmailers->mailer_list; curmailers->mailer_list = newmailer; newmailer->mailers = curmailers; newmailer->conf.file = strdup(file); newmailer->conf.line = linenum; newmailer->id = strdup(args[1]); sk = str2sa_range(args[2], NULL, &port1, &port2, NULL, &proto, NULL, &errmsg, NULL, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_STREAM | PA_O_XPRT | PA_O_CONNECT); if (!sk) { ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg); err_code |= ERR_ALERT | ERR_FATAL; goto out; } if (proto->sock_prot != IPPROTO_TCP) { ha_alert("parsing [%s:%d] : '%s %s' : TCP not supported for this address family.\n", file, linenum, args[0], args[1]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } newmailer->addr = *sk; newmailer->proto = proto; newmailer->xprt = xprt_get(XPRT_RAW); newmailer->sock_init_arg = NULL; } else if (strcmp(args[0], "timeout") == 0) { if (!*args[1]) { ha_alert("parsing [%s:%d] : '%s' expects 'mail' and