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 | |