1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2011 Couchbase, Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #include "config.h"
19 
20 #include <algorithm>
21 #include <iostream>
22 #include <limits>
23 #include <sstream>
24 #include <vector>
25 
26 #include "configuration.h"
27 
28 #ifdef AUTOCONF_BUILD
29 #include "generated_configuration.cc"
30 #endif
31 
32 #define STATWRITER_NAMESPACE config
33 #include "statwriter.h"
34 #undef STATWRITER_NAMESPACE
35 
Configuration()36 Configuration::Configuration() {
37     initialize();
38 }
39 
getString(const std::string &key) const40 std::string Configuration::getString(const std::string &key) const {
41     Mutex *ptr = const_cast<Mutex*> (&mutex);
42     LockHolder lh(*ptr);
43 
44     std::map<std::string, value_t>::const_iterator iter;
45     if ((iter = attributes.find(key)) == attributes.end()) {
46         return std::string();
47     }
48     cb_assert(iter->second.datatype == DT_STRING);
49     if (iter->second.val.v_string) {
50         return std::string(iter->second.val.v_string);
51     }
52     return std::string();
53 }
54 
getBool(const std::string &key) const55 bool Configuration::getBool(const std::string &key) const {
56     Mutex *ptr = const_cast<Mutex*> (&mutex);
57     LockHolder lh(*ptr);
58 
59     std::map<std::string, value_t>::const_iterator iter;
60     if ((iter = attributes.find(key)) == attributes.end()) {
61         return false;
62     }
63     cb_assert(iter->second.datatype == DT_BOOL);
64     return iter->second.val.v_bool;
65 }
66 
getFloat(const std::string &key) const67 float Configuration::getFloat(const std::string &key) const {
68     Mutex *ptr = const_cast<Mutex*> (&mutex);
69     LockHolder lh(*ptr);
70 
71     std::map<std::string, value_t>::const_iterator iter;
72     if ((iter = attributes.find(key)) == attributes.end()) {
73         return 0;
74     }
75     cb_assert(iter->second.datatype == DT_FLOAT);
76     return iter->second.val.v_float;
77 }
78 
getInteger(const std::string &key) const79 size_t Configuration::getInteger(const std::string &key) const {
80     Mutex *ptr = const_cast<Mutex*> (&mutex);
81     LockHolder lh(*ptr);
82 
83     std::map<std::string, value_t>::const_iterator iter;
84     if ((iter = attributes.find(key)) == attributes.end()) {
85         return 0;
86     }
87     cb_assert(iter->second.datatype == DT_SIZE);
88     return iter->second.val.v_size;
89 }
90 
getSignedInteger(const std::string &key) const91 ssize_t Configuration::getSignedInteger(const std::string &key) const {
92     Mutex *ptr = const_cast<Mutex*> (&mutex);
93     LockHolder lh(*ptr);
94 
95     std::map<std::string, value_t>::const_iterator iter;
96     if ((iter = attributes.find(key)) == attributes.end()) {
97         return 0;
98     }
99     cb_assert(iter->second.datatype == DT_SSIZE);
100     return iter->second.val.v_ssize;
101 }
102 
operator <<(std::ostream &out, const Configuration &config)103 std::ostream& operator <<(std::ostream &out, const Configuration &config) {
104     LockHolder lh(const_cast<Mutex&> (config.mutex));
105     std::map<std::string, Configuration::value_t>::const_iterator iter;
106     for (iter = config.attributes.begin(); iter != config.attributes.end();
107         ++iter) {
108         std::stringstream line;
109         line << iter->first.c_str();
110         line << " = [";
111         switch (iter->second.datatype) {
112         case DT_BOOL:
113             if (iter->second.val.v_bool) {
114                 line << "true";
115             } else {
116                 line << "false";
117             }
118             break;
119         case DT_STRING:
120             line << iter->second.val.v_string;
121             break;
122         case DT_SIZE:
123             line << iter->second.val.v_size;
124             break;
125         case DT_SSIZE:
126             line << iter->second.val.v_ssize;
127             break;
128         case DT_FLOAT:
129             line << iter->second.val.v_float;
130             break;
131         case DT_CONFIGFILE:
132             continue;
133         default:
134             // ignore
135             ;
136         }
137         line << "]" << std::endl;
138         out << line.str();
139     }
140 
141     return out;
142 }
143 
setParameter(const std::string &key, bool value)144 void Configuration::setParameter(const std::string &key, bool value) {
145     LockHolder lh(mutex);
146     std::map<std::string, value_t>::iterator validator = attributes.find(key);
147     if (validator != attributes.end()) {
148         if (validator->second.validator != NULL) {
149             if (!validator->second.validator->validateBool(key, value)) {
150                 throw std::runtime_error("value out of range.");
151             }
152         }
153     }
154     attributes[key].datatype = DT_BOOL;
155     attributes[key].val.v_bool = value;
156     std::vector<ValueChangedListener*> copy(attributes[key].changeListener);
157     lh.unlock();
158     std::vector<ValueChangedListener*>::iterator iter;
159     for (iter = copy.begin(); iter != copy.end(); ++iter) {
160         (*iter)->booleanValueChanged(key, value);
161     }
162 }
163 
setParameter(const std::string &key, size_t value)164 void Configuration::setParameter(const std::string &key, size_t value) {
165     LockHolder lh(mutex);
166     std::map<std::string, value_t>::iterator validator = attributes.find(key);
167     if (validator != attributes.end()) {
168         if (validator->second.validator != NULL) {
169             if (!validator->second.validator->validateSize(key, value)) {
170                 throw std::runtime_error("value out of range.");
171             }
172         }
173     }
174     attributes[key].datatype = DT_SIZE;
175     if (key.compare("cache_size") == 0) {
176         attributes["max_size"].val.v_size = value;
177     } else {
178         attributes[key].val.v_size = value;
179     }
180 
181     std::vector<ValueChangedListener*> copy(attributes[key].changeListener);
182     lh.unlock();
183     std::vector<ValueChangedListener*>::iterator iter;
184     for (iter = copy.begin(); iter != copy.end(); ++iter) {
185         (*iter)->sizeValueChanged(key, value);
186     }
187 }
188 
setParameter(const std::string &key, ssize_t value)189 void Configuration::setParameter(const std::string &key, ssize_t value) {
190     LockHolder lh(mutex);
191     std::map<std::string, value_t>::iterator validator = attributes.find(key);
192     if (validator != attributes.end()) {
193         if (validator->second.validator != NULL) {
194             if (!validator->second.validator->validateSSize(key, value)) {
195                 throw std::runtime_error("value out of range.");
196             }
197         }
198     }
199     attributes[key].datatype = DT_SSIZE;
200     if (key.compare("cache_size") == 0) {
201         attributes["max_size"].val.v_ssize = value;
202     } else {
203         attributes[key].val.v_ssize = value;
204     }
205 
206     std::vector<ValueChangedListener*> copy(attributes[key].changeListener);
207     lh.unlock();
208     std::vector<ValueChangedListener*>::iterator iter;
209     for (iter = copy.begin(); iter != copy.end(); ++iter) {
210         (*iter)->sizeValueChanged(key, value);
211     }
212 }
213 
setParameter(const std::string &key, float value)214 void Configuration::setParameter(const std::string &key, float value) {
215     LockHolder lh(mutex);
216 
217     std::map<std::string, value_t>::iterator validator = attributes.find(key);
218     if (validator != attributes.end()) {
219         if (validator->second.validator != NULL) {
220             if (!validator->second.validator->validateFloat(key, value)) {
221                 throw std::runtime_error("value out of range.");
222             }
223         }
224     }
225 
226     attributes[key].datatype = DT_FLOAT;
227     attributes[key].val.v_float = value;
228     std::vector<ValueChangedListener*> copy(attributes[key].changeListener);
229     lh.unlock();
230     std::vector<ValueChangedListener*>::iterator iter;
231     for (iter = copy.begin(); iter != copy.end(); ++iter) {
232         (*iter)->floatValueChanged(key, value);
233     }
234 }
235 
setParameter(const std::string &key, const std::string &value)236 void Configuration::setParameter(const std::string &key,
237                                  const std::string &value) {
238     if (value.length() == 0) {
239         setParameter(key, (const char *)NULL);
240     } else {
241         setParameter(key, value.c_str());
242     }
243 }
244 
setParameter(const std::string &key, const char *value)245 void Configuration::setParameter(const std::string &key, const char *value) {
246     LockHolder lh(mutex);
247     std::map<std::string, value_t>::iterator validator = attributes.find(key);
248     if (validator != attributes.end()) {
249         if (validator->second.validator != NULL) {
250             if (!validator->second.validator->validateString(key, value)) {
251                 throw std::runtime_error("value out of range.");
252             }
253         }
254     }
255 
256     if (attributes.find(key) != attributes.end() && attributes[key].datatype
257             == DT_STRING) {
258         free((void*)attributes[key].val.v_string);
259     }
260     attributes[key].datatype = DT_STRING;
261     attributes[key].val.v_string = NULL;
262     if (value != NULL) {
263         attributes[key].val.v_string = strdup(value);
264     }
265 
266     std::vector<ValueChangedListener*> copy(attributes[key].changeListener);
267     lh.unlock();
268     std::vector<ValueChangedListener*>::iterator iter;
269     for (iter = copy.begin(); iter != copy.end(); ++iter) {
270         (*iter)->stringValueChanged(key, value);
271     }
272 }
273 
addValueChangedListener(const std::string &key, ValueChangedListener *val)274 void Configuration::addValueChangedListener(const std::string &key,
275                                             ValueChangedListener *val) {
276     LockHolder lh(mutex);
277     if (attributes.find(key) != attributes.end()) {
278         attributes[key].changeListener.push_back(val);
279     }
280 }
281 
setValueValidator(const std::string &key, ValueChangedValidator *validator)282 ValueChangedValidator *Configuration::setValueValidator(const std::string &key,
283                                             ValueChangedValidator *validator) {
284     ValueChangedValidator *ret = 0;
285     LockHolder lh(mutex);
286     if (attributes.find(key) != attributes.end()) {
287         ret = attributes[key].validator;
288         attributes[key].validator = validator;
289     }
290 
291     return ret;
292 }
293 
addStats(ADD_STAT add_stat, const void *c) const294 void Configuration::addStats(ADD_STAT add_stat, const void *c) const {
295     LockHolder lh(const_cast<Mutex&> (mutex));
296     std::map<std::string, value_t>::const_iterator iter;
297     for (iter = attributes.begin(); iter != attributes.end(); ++iter) {
298         std::stringstream value;
299         switch (iter->second.datatype) {
300         case DT_BOOL:
301             value << iter->second.val.v_bool;
302             break;
303         case DT_STRING:
304             value << iter->second.val.v_string;
305             break;
306         case DT_SIZE:
307             value << iter->second.val.v_size;
308             break;
309         case DT_SSIZE:
310             value << iter->second.val.v_ssize;
311             break;
312         case DT_FLOAT:
313             value << iter->second.val.v_float;
314             break;
315         case DT_CONFIGFILE:
316         default:
317             // ignore
318             ;
319         }
320 
321         std::stringstream key;
322         key << "ep_" << iter->first;
323         std::string k = key.str();
324         add_casted_stat(k.c_str(), value.str().data(), add_stat, c);
325     }
326 }
327 
328 /**
329  * Internal container of an engine parameter.
330  */
331 class ConfigItem: public config_item {
332 public:
ConfigItem(const char *theKey, config_datatype theDatatype)333     ConfigItem(const char *theKey, config_datatype theDatatype) :
334                                                                 holder(NULL) {
335         key = theKey;
336         datatype = theDatatype;
337         value.dt_string = &holder;
338     }
339 
340 private:
341     char *holder;
342 };
343 
parseConfiguration(const char *str, SERVER_HANDLE_V1* sapi)344 bool Configuration::parseConfiguration(const char *str,
345                                        SERVER_HANDLE_V1* sapi) {
346     std::vector<ConfigItem *> config;
347 
348     std::map<std::string, value_t>::const_iterator iter;
349     for (iter = attributes.begin(); iter != attributes.end(); ++iter) {
350         config.push_back(new ConfigItem(iter->first.c_str(),
351                                         iter->second.datatype));
352     }
353 
354     // we don't have a good support for alias yet...
355     config.push_back(new ConfigItem("cache_size", DT_SIZE));
356 
357     // And add support for config files...
358     config.push_back(new ConfigItem("config_file", DT_CONFIGFILE));
359 
360     struct config_item *items = (struct config_item*)calloc(config.size() + 1,
361             sizeof(struct config_item));
362     int nelem = config.size();
363     for (int ii = 0; ii < nelem; ++ii) {
364         items[ii].key = config[ii]->key;
365         items[ii].datatype = config[ii]->datatype;
366         items[ii].value.dt_string = config[ii]->value.dt_string;
367     }
368 
369     bool ret = sapi->core->parse_config(str, items, stderr) == 0;
370     for (int ii = 0; ii < nelem; ++ii) {
371         if (items[ii].found) {
372             if (ret) {
373                 switch (items[ii].datatype) {
374                 case DT_STRING:
375                     setParameter(items[ii].key, *(items[ii].value.dt_string));
376                     break;
377                 case DT_SIZE:
378                     setParameter(items[ii].key, *items[ii].value.dt_size);
379                     break;
380                 case DT_SSIZE:
381                     setParameter(items[ii].key,
382                                  (ssize_t)*items[ii].value.dt_ssize);
383                     break;
384                 case DT_BOOL:
385                     setParameter(items[ii].key, *items[ii].value.dt_bool);
386                     break;
387                 case DT_FLOAT:
388                     setParameter(items[ii].key, *items[ii].value.dt_float);
389                     break;
390                 default:
391                     abort();
392                 }
393             }
394 
395             if (items[ii].datatype == DT_STRING) {
396                 free(*items[ii].value.dt_string);
397             }
398         }
399     }
400 
401     // release allocated memory..
402     free(items);
403     std::vector<ConfigItem *>::iterator ii;
404     for (ii = config.begin(); ii != config.end(); ++ii) {
405         delete *ii;
406     }
407 
408     return ret;
409 }
410 
~Configuration()411 Configuration::~Configuration() {
412     std::map<std::string, value_t>::iterator iter;
413     for (iter = attributes.begin(); iter != attributes.end(); ++iter) {
414         std::vector<ValueChangedListener*>::iterator ii;
415         for (ii = iter->second.changeListener.begin();
416              ii != iter->second.changeListener.end(); ++ii) {
417             delete *ii;
418         }
419 
420         delete iter->second.validator;
421         if (iter->second.datatype == DT_STRING) {
422             free((void*)iter->second.val.v_string);
423         }
424     }
425 }
426