1 /*
2  * Copyright (c) 2015-2016, Cisco Systems, Inc. All rights reserved.
3  * Copyright (c) 2015, Intel Corp., Inc.  All rights reserved.
4  *
5  * This software is available to you under a choice of one of two
6  * licenses.  You may choose to be licensed under the terms of the GNU
7  * General Public License (GPL) Version 2, available from the file
8  * COPYING in the main directory of this source tree, or the
9  * BSD license below:
10  *
11  *     Redistribution and use in source and binary forms, with or
12  *     without modification, are permitted provided that the following
13  *     conditions are met:
14  *
15  *      - Redistributions of source code must retain the above
16  *        copyright notice, this list of conditions and the following
17  *        disclaimer.
18  *
19  *      - Redistributions in binary form must reproduce the above
20  *        copyright notice, this list of conditions and the following
21  *        disclaimer in the documentation and/or other materials
22  *        provided with the distribution.
23  *
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31  * SOFTWARE.
32  *
33  */
34 
35 #include <stdio.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <ctype.h>
39 
40 #include <rdma/fi_errno.h>
41 
42 #include "ofi.h"
43 #include "ofi_list.h"
44 
45 
46 extern int ofi_init;
47 extern void fi_ini(void);
48 
49 struct fi_param_entry {
50 	const struct fi_provider *provider;
51 	char *name;
52 	enum fi_param_type type;
53 	char *help_string;
54 	char *env_var_name;
55 	struct dlist_entry entry;
56 };
57 
58 /* TODO: Add locking around param_list when adding dynamic removal */
59 static DEFINE_LIST(param_list);
60 
61 
62 static struct fi_param_entry *
fi_find_param(const struct fi_provider * provider,const char * param_name)63 fi_find_param(const struct fi_provider *provider, const char *param_name)
64 {
65 	struct fi_param_entry *param;
66 	struct dlist_entry *entry;
67 
68 	for (entry = param_list.next; entry != &param_list; entry = entry->next) {
69 		param = container_of(entry, struct fi_param_entry, entry);
70 		if (param->provider == provider &&
71 		    strcmp(param->name, param_name) == 0) {
72 			return param;
73 		}
74 	}
75 
76 	FI_DBG(provider, FI_LOG_CORE,
77 		"Failed to find parameter %s: was not defined\n", param_name);
78 	return NULL;
79 }
80 
81 __attribute__((visibility ("default"),EXTERNALLY_VISIBLE))
DEFAULT_SYMVER_PRE(fi_getparams)82 int DEFAULT_SYMVER_PRE(fi_getparams)(struct fi_param **params, int *count)
83 {
84 	struct fi_param *vhead = NULL;
85 	struct fi_param_entry *param;
86 	struct dlist_entry *entry;
87 	int cnt, i;
88 	char *tmp;
89 
90 	if (!ofi_init)
91 		fi_ini();
92 
93 	for (entry = param_list.next, cnt = 0; entry != &param_list;
94 	     entry = entry->next)
95 		cnt++;
96 
97 	if (cnt == 0)
98 		goto out;
99 
100 	// last extra entry will be all NULL
101 	vhead = calloc(cnt + 1, sizeof (*vhead));
102 	if (!vhead)
103 		return -FI_ENOMEM;
104 
105 	for (entry = param_list.next, i = 0; entry != &param_list;
106 	     entry = entry->next, i++) {
107 		param = container_of(entry, struct fi_param_entry, entry);
108 		vhead[i].name = strdup(param->env_var_name);
109 		vhead[i].type = param->type;
110 		vhead[i].help_string = strdup(param->help_string);
111 
112 		tmp = getenv(param->env_var_name);
113 		if (tmp)
114 			vhead[i].value = strdup(tmp);
115 
116 		if (!vhead[i].name || !vhead[i].help_string) {
117 			fi_freeparams(vhead);
118 			return -FI_ENOMEM;
119 		}
120 	}
121 
122 out:
123 	*count = cnt;
124 	*params = vhead;
125 	return FI_SUCCESS;
126 }
127 DEFAULT_SYMVER(fi_getparams_, fi_getparams, FABRIC_1.0);
128 
129 __attribute__((visibility ("default"),EXTERNALLY_VISIBLE))
DEFAULT_SYMVER_PRE(fi_freeparams)130 void DEFAULT_SYMVER_PRE(fi_freeparams)(struct fi_param *params)
131 {
132 	int i;
133 	for (i = 0; params[i].name; ++i) {
134 		free((void*) params[i].name);
135 		free((void*) params[i].help_string);
136 		free((void*) params[i].value);
137 	}
138 	free(params);
139 }
140 DEFAULT_SYMVER(fi_freeparams_, fi_freeparams, FABRIC_1.0);
141 
fi_free_param(struct fi_param_entry * param)142 static void fi_free_param(struct fi_param_entry *param)
143 {
144 	free(param->name);
145 	free(param->help_string);
146 	free(param->env_var_name);
147 	free(param);
148 }
149 
fi_param_undefine(const struct fi_provider * provider)150 void fi_param_undefine(const struct fi_provider *provider)
151 {
152 	struct fi_param_entry *param;
153 	struct dlist_entry *entry;
154 	struct dlist_entry *next;
155 
156 	for (entry = param_list.next; entry != &param_list; entry = next) {
157 		next = entry->next;
158 		param = container_of(entry, struct fi_param_entry, entry);
159 		if (param->provider == provider) {
160 			FI_DBG(provider, FI_LOG_CORE, "Removing param: %s\n", param->name);
161 			dlist_remove(entry);
162 			fi_free_param(param);
163 		}
164 	}
165 }
166 
167 __attribute__((visibility ("default"),EXTERNALLY_VISIBLE))
DEFAULT_SYMVER_PRE(fi_param_define)168 int DEFAULT_SYMVER_PRE(fi_param_define)(const struct fi_provider *provider,
169 		const char *param_name, enum fi_param_type type,
170 		const char *help_string_fmt, ...)
171 {
172 	int i, ret;
173 	struct fi_param_entry *v;
174 	char *tmp_str;
175 	va_list vargs;
176 
177 	if (!provider)
178 		provider = &core_prov;
179 
180 	// Check for bozo cases
181 	if (param_name == NULL || help_string_fmt == NULL || *help_string_fmt == '\0') {
182 		FI_DBG(provider, FI_LOG_CORE,
183 			"Failed to register %s variable: provider coding error\n",
184 			param_name);
185 		return -FI_EINVAL;
186 	}
187 
188 	v = calloc(1, sizeof(*v));
189 	if (!v) {
190 		FI_DBG(provider, FI_LOG_CORE,
191 			"Failed to register %s variable: ENOMEM\n", param_name);
192 		return -FI_ENOMEM;
193 	}
194 
195 	v->provider = provider;
196 	v->name = strdup(param_name);
197 	v->type = type;
198 
199 	va_start(vargs, help_string_fmt);
200 	ret = vasprintf(&v->help_string, help_string_fmt, vargs);
201 	va_end(vargs);
202 	if (ret < 0)
203 		v->help_string = NULL;
204 
205 	if (provider != &core_prov) {
206 		ret = asprintf(&tmp_str, "%s: %s", provider->name, v->help_string);
207 		free(v->help_string);
208 		if (ret < 0)
209 			v->help_string = NULL;
210 		v->help_string = tmp_str;
211 		ret = asprintf(&v->env_var_name, "FI_%s_%s", provider->name, param_name);
212 		if (ret < 0)
213 			v->env_var_name = NULL;
214 	} else {
215 		ret = asprintf(&v->env_var_name, "FI_%s", param_name);
216 		if (ret < 0)
217 			v->env_var_name = NULL;
218 	}
219 	if (!v->name || !v->help_string || !v->env_var_name) {
220 		fi_free_param(v);
221 		FI_DBG(provider, FI_LOG_CORE,
222 			"Failed to register %s variable: ENOMEM\n", param_name);
223 		return -FI_ENOMEM;
224 	}
225 
226 	for (i = 0; v->env_var_name[i]; ++i)
227 		v->env_var_name[i] = (char) toupper(v->env_var_name[i]);
228 
229 	dlist_insert_tail(&v->entry, &param_list);
230 
231 	FI_DBG(provider, FI_LOG_CORE, "registered var %s\n", param_name);
232 	return FI_SUCCESS;
233 }
234 DEFAULT_SYMVER(fi_param_define_, fi_param_define, FABRIC_1.0);
235 
fi_parse_bool(const char * str_value)236 static int fi_parse_bool(const char *str_value)
237 {
238 	if (strcmp(str_value, "0") == 0 ||
239 	    strcasecmp(str_value, "false") == 0 ||
240 	    strcasecmp(str_value, "no") == 0 ||
241 	    strcasecmp(str_value, "off") == 0) {
242 		return 0;
243 	}
244 
245 	if (strcmp(str_value, "1") == 0 ||
246 	    strcasecmp(str_value, "true") == 0 ||
247 	    strcasecmp(str_value, "yes") == 0 ||
248 	    strcasecmp(str_value, "on") == 0) {
249 		return 1;
250 	}
251 
252 	return -1;
253 }
254 
255 __attribute__((visibility ("default"),EXTERNALLY_VISIBLE))
DEFAULT_SYMVER_PRE(fi_param_get)256 int DEFAULT_SYMVER_PRE(fi_param_get)(struct fi_provider *provider,
257 		const char *param_name, void *value)
258 {
259 	struct fi_param_entry *param;
260 	char *str_value;
261 	int ret = FI_SUCCESS;
262 
263 	if (!provider)
264 		provider = &core_prov;
265 
266 	if (!param_name || !value) {
267 		FI_DBG(provider, FI_LOG_CORE,
268 			"Failed to read %s variable: provider coding error\n",
269 			param_name);
270 		return -FI_EINVAL;
271 	}
272 
273 	param = fi_find_param(provider, param_name);
274 	if (!param)
275 		return -FI_ENOENT;
276 
277 	str_value = getenv(param->env_var_name);
278 	if (!str_value) {
279 		FI_INFO(provider, FI_LOG_CORE,
280 			"variable %s=<not set>\n", param_name);
281 		ret = -FI_ENODATA;
282 		goto out;
283 	}
284 
285 	switch (param->type) {
286 	case FI_PARAM_STRING:
287 		* ((char **) value) = str_value;
288 		FI_INFO(provider, FI_LOG_CORE,
289 			"read string var %s=%s\n", param_name, *(char **) value);
290 		break;
291 	case FI_PARAM_INT:
292 		* ((int *) value) = strtol(str_value, NULL, 0);
293 		FI_INFO(provider, FI_LOG_CORE,
294 			"read int var %s=%d\n", param_name, *(int *) value);
295 		break;
296 	case FI_PARAM_BOOL:
297 		* ((int *) value) = fi_parse_bool(str_value);
298 		FI_INFO(provider, FI_LOG_CORE,
299 			"read bool var %s=%d\n", param_name, *(int *) value);
300 		if (*(int *) value == -1)
301 			ret = -FI_EINVAL;
302 		break;
303 	case FI_PARAM_SIZE_T:
304 		* ((size_t *) value) = strtol(str_value, NULL, 0);
305 		FI_INFO(provider, FI_LOG_CORE,
306 			"read long var %s=%zu\n", param_name, *(size_t *) value);
307 		break;
308 	}
309 
310 out:
311 	return ret;
312 }
313 DEFAULT_SYMVER(fi_param_get_, fi_param_get, FABRIC_1.0);
314 
315 
fi_param_init(void)316 void fi_param_init(void)
317 {
318 	dlist_init(&param_list);
319 }
320 
fi_param_fini(void)321 void fi_param_fini(void)
322 {
323 	struct fi_param_entry *param;
324 	struct dlist_entry *entry;
325 
326 	while (!dlist_empty(&param_list)) {
327 		entry = param_list.next;
328 		param = container_of(entry, struct fi_param_entry, entry);
329 		dlist_remove(entry);
330 		fi_free_param(param);
331 	}
332 }
333