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 MEROPROPFILEHEADER \
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 */
27static 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
47static pthread_mutex_t readprops_lock = PTHREAD_MUTEX_INITIALIZER;
48
49/**
50 * Returns true if the key is a default property.
51 */
52int
53defaultProperty(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 */
70confkeyval *
71getDefaultProps(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 */
82int
83writeProps(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 */
113static int
114appendProp(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 */
138void
139writePropsBuf(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 */
167int
168readProps(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 */
189int
190readAllProps(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 */
210void
211readPropsBuf(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 */
235char *
236setProp(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