| 1 | /* |
| 2 | * This Source Code Form is subject to the terms of the Mozilla Public |
| 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 5 | * |
| 6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. |
| 7 | */ |
| 8 | |
| 9 | /** |
| 10 | * properties |
| 11 | * Fabian Groffen |
| 12 | * Simple functions that deal with the property file |
| 13 | */ |
| 14 | |
| 15 | #include "monetdb_config.h" |
| 16 | #include "properties.h" |
| 17 | #include "utils.h" |
| 18 | #include <string.h> /* memcpy */ |
| 19 | #include <pthread.h> |
| 20 | #include <ctype.h> |
| 21 | |
| 22 | #define \ |
| 23 | "# DO NOT EDIT THIS FILE - use monetdb(1) and monetdbd(1) to set properties\n" \ |
| 24 | "# This file is used by monetdbd\n\n" |
| 25 | |
| 26 | /* these are the properties used for starting an mserver */ |
| 27 | static const confkeyval _internal_prop_keys[PROPLENGTH] = { |
| 28 | {"type" , NULL, 0, STR}, |
| 29 | {"shared" , NULL, 0, STR}, |
| 30 | {"nthreads" , NULL, 0, INT}, |
| 31 | {"optpipe" , NULL, 0, STR}, |
| 32 | {"readonly" , NULL, 0, BOOLEAN}, |
| 33 | {"embedr" , NULL, 0, BOOLEAN}, |
| 34 | {"embedpy" , NULL, 0, BOOLEAN}, |
| 35 | {"embedpy3" , NULL, 0, BOOLEAN}, |
| 36 | {"embedc" , NULL, 0, BOOLEAN}, |
| 37 | {"ipv6" , NULL, 0, BOOLEAN}, |
| 38 | {"listenaddr" , NULL, 0, STR}, |
| 39 | {"nclients" , NULL, 0, INT}, |
| 40 | {"mfunnel" , NULL, 0, STR}, |
| 41 | {"dbextra" , NULL, 0, STR}, |
| 42 | {"memmaxsize" , NULL, 0, INT}, |
| 43 | {"vmmaxsize" , NULL, 0, INT}, |
| 44 | { NULL, NULL, 0, INVALID} |
| 45 | }; |
| 46 | |
| 47 | static pthread_mutex_t readprops_lock = PTHREAD_MUTEX_INITIALIZER; |
| 48 | |
| 49 | /** |
| 50 | * Returns true if the key is a default property. |
| 51 | */ |
| 52 | int |
| 53 | defaultProperty(const char *property) { |
| 54 | int i; |
| 55 | if (property == NULL) |
| 56 | return 0; |
| 57 | for (i = 0; _internal_prop_keys[i].key != NULL; i++) |
| 58 | if (strcmp(property, _internal_prop_keys[i].key) == 0) |
| 59 | return 1; |
| 60 | return 0; |
| 61 | } |
| 62 | |
| 63 | /** |
| 64 | * Returns the currently supported list of properties. This list can be |
| 65 | * used to read all values, modify some and write the file back again. |
| 66 | * The returned list is malloced, the keys are a pointer to a static |
| 67 | * copy and hence need not to be freed, e.g. free after freeConfFile |
| 68 | * is enough. |
| 69 | */ |
| 70 | confkeyval * |
| 71 | getDefaultProps(void) |
| 72 | { |
| 73 | confkeyval *ret = malloc(sizeof(_internal_prop_keys)); |
| 74 | memcpy(ret, _internal_prop_keys, sizeof(_internal_prop_keys)); |
| 75 | return(ret); |
| 76 | } |
| 77 | |
| 78 | /** |
| 79 | * Writes the given key-value list to MEROPROPFILE in the given path. |
| 80 | * Returns 0 when the properties could be successfully written to the file. |
| 81 | */ |
| 82 | int |
| 83 | writeProps(confkeyval *ckv, const char *path) |
| 84 | { |
| 85 | char file[1024]; |
| 86 | FILE *cnf; |
| 87 | |
| 88 | snprintf(file, 1024, "%s/" MEROPROPFILE, path); |
| 89 | pthread_mutex_lock(&readprops_lock); |
| 90 | if ((cnf = fopen(file, "w" )) == NULL) { |
| 91 | pthread_mutex_unlock(&readprops_lock); |
| 92 | return(1); |
| 93 | } |
| 94 | |
| 95 | fprintf(cnf, "%s" , MEROPROPFILEHEADER); |
| 96 | while (ckv->key != NULL) { |
| 97 | if (ckv->val != NULL) |
| 98 | fprintf(cnf, "%s=%s\n" , ckv->key, ckv->val); |
| 99 | ckv++; |
| 100 | } |
| 101 | |
| 102 | fflush(cnf); |
| 103 | fclose(cnf); |
| 104 | pthread_mutex_unlock(&readprops_lock); |
| 105 | |
| 106 | return(0); |
| 107 | } |
| 108 | |
| 109 | /** |
| 110 | * Appends additional (non-default) property MEROPROPFILE in the given path. |
| 111 | * Returns 0 when the property could be successfully appended to the file. |
| 112 | */ |
| 113 | static int |
| 114 | appendProp(confkeyval *ckv, const char *path) |
| 115 | { |
| 116 | char file[1024]; |
| 117 | FILE *cnf; |
| 118 | |
| 119 | snprintf(file, 1024, "%s/" MEROPROPFILE, path); |
| 120 | if ((cnf = fopen(file, "a" )) == NULL) |
| 121 | return(1); |
| 122 | |
| 123 | if (ckv->key != NULL && ckv->val != NULL) { |
| 124 | fprintf(cnf, "%s=%s\n" , ckv->key, ckv->val); |
| 125 | } |
| 126 | |
| 127 | fflush(cnf); |
| 128 | fclose(cnf); |
| 129 | |
| 130 | return(0); |
| 131 | } |
| 132 | |
| 133 | /** |
| 134 | * Writes the given key-value list to a buffer and sets its pointer to |
| 135 | * buf. This function deals with the allocation of the buffer, hence |
| 136 | * the caller should free it. |
| 137 | */ |
| 138 | void |
| 139 | writePropsBuf(confkeyval *ckv, char **buf) |
| 140 | { |
| 141 | confkeyval *w; |
| 142 | size_t len = sizeof(MEROPROPFILEHEADER); |
| 143 | char *p; |
| 144 | |
| 145 | w = ckv; |
| 146 | while (w->key != NULL) { |
| 147 | if (w->val != NULL) |
| 148 | len += strlen(w->key) + 1 + strlen(w->val) + 1; |
| 149 | w++; |
| 150 | } |
| 151 | |
| 152 | p = *buf = malloc(sizeof(char) * len + 1); |
| 153 | memcpy(p, MEROPROPFILEHEADER, sizeof(MEROPROPFILEHEADER)); |
| 154 | p += sizeof(MEROPROPFILEHEADER) - 1; |
| 155 | w = ckv; |
| 156 | while (w->key != NULL) { |
| 157 | if (w->val != NULL) |
| 158 | p += sprintf(p, "%s=%s\n" , w->key, w->val); |
| 159 | w++; |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | /** |
| 164 | * Read a property file, filling in the requested key-values. Returns 0 |
| 165 | * when reading the property file succeeded. |
| 166 | */ |
| 167 | int |
| 168 | readProps(confkeyval *ckv, const char *path) |
| 169 | { |
| 170 | char file[1024]; |
| 171 | FILE *cnf; |
| 172 | |
| 173 | snprintf(file, 1024, "%s/" MEROPROPFILE, path); |
| 174 | pthread_mutex_lock(&readprops_lock); |
| 175 | if ((cnf = fopen(file, "r" )) != NULL) { |
| 176 | readConfFile(ckv, cnf); |
| 177 | fclose(cnf); |
| 178 | pthread_mutex_unlock(&readprops_lock); |
| 179 | return(0); |
| 180 | } |
| 181 | pthread_mutex_unlock(&readprops_lock); |
| 182 | return(1); |
| 183 | } |
| 184 | |
| 185 | /** |
| 186 | * Read all properties from a property file. |
| 187 | * Returns 0 when reading the property file succeeded. |
| 188 | */ |
| 189 | int |
| 190 | readAllProps(confkeyval *ckv, const char *path) |
| 191 | { |
| 192 | char file[1024]; |
| 193 | FILE *cnf; |
| 194 | |
| 195 | snprintf(file, 1024, "%s/" MEROPROPFILE, path); |
| 196 | pthread_mutex_lock(&readprops_lock); |
| 197 | if ((cnf = fopen(file, "r" )) != NULL) { |
| 198 | readConfFileFull(ckv, cnf); |
| 199 | fclose(cnf); |
| 200 | pthread_mutex_unlock(&readprops_lock); |
| 201 | return(0); |
| 202 | } |
| 203 | pthread_mutex_unlock(&readprops_lock); |
| 204 | return(1); |
| 205 | } |
| 206 | |
| 207 | /** |
| 208 | * Read properties from buf, filling in the requested key-values. |
| 209 | */ |
| 210 | void |
| 211 | readPropsBuf(confkeyval *ckv, char *buf) |
| 212 | { |
| 213 | confkeyval *t; |
| 214 | char *p; |
| 215 | char *err; |
| 216 | char *lasts = NULL; |
| 217 | size_t len; |
| 218 | |
| 219 | while((p = strtok_r(buf, "\n" , &lasts)) != NULL) { |
| 220 | buf = NULL; /* strtok */ |
| 221 | for (t = ckv; t->key != NULL; t++) { |
| 222 | len = strlen(t->key); |
| 223 | if (strncmp(p, t->key, len) == 0 && p[len] == '=') { |
| 224 | if ((err = setConfVal(t, p + len + 1)) != NULL) |
| 225 | free(err); /* ignore, just fall back to default */ |
| 226 | } |
| 227 | } |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | /** |
| 232 | * Sets a single property, performing all necessary checks to validate |
| 233 | * the key and associated value. |
| 234 | */ |
| 235 | char * |
| 236 | setProp(char *path, char *key, char *val) |
| 237 | { |
| 238 | char *err; |
| 239 | char buf[8096]; |
| 240 | confkeyval *props = getDefaultProps(); |
| 241 | confkeyval *kv; |
| 242 | |
| 243 | readProps(props, path); |
| 244 | kv = findConfKey(props, key); |
| 245 | if (kv != NULL && (err = setConfVal(kv, val)) != NULL) { |
| 246 | /* first just attempt to set the value (type-check) in memory */ |
| 247 | freeConfFile(props); |
| 248 | free(props); |
| 249 | return(err); |
| 250 | } |
| 251 | |
| 252 | if (val != NULL) { |
| 253 | /* handle the semantially enriched types */ |
| 254 | if (strcmp(key, "forward" ) == 0) { |
| 255 | if (strcmp(val, "proxy" ) != 0 && strcmp(val, "redirect" ) != 0) { |
| 256 | snprintf(buf, sizeof(buf), "expected 'proxy' or 'redirect' " |
| 257 | "for property 'forward', got: %s" , val); |
| 258 | freeConfFile(props); |
| 259 | free(props); |
| 260 | return(strdup(buf)); |
| 261 | } |
| 262 | } else if (strcmp(key, "shared" ) == 0) { |
| 263 | char *value = val; |
| 264 | /* check if tag matches [A-Za-z0-9./]+ */ |
| 265 | if (*value == '\0') { |
| 266 | freeConfFile(props); |
| 267 | free(props); |
| 268 | return(strdup("tag to share cannot be empty" )); |
| 269 | } |
| 270 | while (*value != '\0') { |
| 271 | if (!( |
| 272 | (*value >= 'A' && *value <= 'Z') || |
| 273 | (*value >= 'a' && *value <= 'z') || |
| 274 | isdigit((unsigned char) *value) || |
| 275 | (*value == '.' || *value == '/') |
| 276 | )) |
| 277 | { |
| 278 | snprintf(buf, sizeof(buf), |
| 279 | "invalid character '%c' at %d " |
| 280 | "in tag name '%s'\n" , |
| 281 | *value, (int)(value - val), val); |
| 282 | freeConfFile(props); |
| 283 | free(props); |
| 284 | return(strdup(buf)); |
| 285 | } |
| 286 | value++; |
| 287 | } |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | /* ok, if we've reached this point we can write this stuff out! */ |
| 292 | /* Let's check if this was a default property of an additional one. |
| 293 | * Non-default properties will have a NULL kv */ |
| 294 | if (kv == NULL && val != NULL) { |
| 295 | confkeyval *addProperty = (struct _confkeyval *) malloc(sizeof(struct _confkeyval)); |
| 296 | addProperty->key = strdup(key); |
| 297 | addProperty->val = strdup(val); |
| 298 | addProperty->ival = 0; |
| 299 | addProperty->type = STR; |
| 300 | |
| 301 | appendProp(addProperty, path); |
| 302 | free(addProperty); |
| 303 | } else { |
| 304 | writeProps(props, path); |
| 305 | } |
| 306 | |
| 307 | freeConfFile(props); |
| 308 | free(props); |
| 309 | |
| 310 | return(NULL); |
| 311 | } |
| 312 | |
| 313 | /* vim:set ts=4 sw=4 noexpandtab: */ |
| 314 | |