1/*
2 * Copyright (c) 2006-2007 Voltaire, Inc. All rights reserved.
3 *
4 * This software is available to you under a choice of one of two
5 * licenses.  You may choose to be licensed under the terms of the GNU
6 * General Public License (GPL) Version 2, available from the file
7 * COPYING in the main directory of this source tree, or the
8 * OpenIB.org BSD license below:
9 *
10 *     Redistribution and use in source and binary forms, with or
11 *     without modification, are permitted provided that the following
12 *     conditions are met:
13 *
14 *      - Redistributions of source code must retain the above
15 *        copyright notice, this list of conditions and the following
16 *        disclaimer.
17 *
18 *      - Redistributions in binary form must reproduce the above
19 *        copyright notice, this list of conditions and the following
20 *        disclaimer in the documentation and/or other materials
21 *        provided with the distribution.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * SOFTWARE.
31 *
32 */
33
34/*
35 * Abstract:
36 *    Implementation of opensm partition management configuration
37 */
38
39#if HAVE_CONFIG_H
40#  include <config.h>
41#endif				/* HAVE_CONFIG_H */
42
43#include <stdlib.h>
44#include <stdio.h>
45#include <string.h>
46#include <errno.h>
47#include <ctype.h>
48
49#include <iba/ib_types.h>
50#include <opensm/osm_base.h>
51#include <opensm/osm_partition.h>
52#include <opensm/osm_subnet.h>
53#include <opensm/osm_log.h>
54
55#include <complib/cl_byteswap.h>
56
57struct part_conf {
58	osm_log_t *p_log;
59	osm_subn_t *p_subn;
60	osm_prtn_t *p_prtn;
61	unsigned is_ipoib, mtu, rate, sl, scope_mask;
62	boolean_t full;
63};
64
65extern osm_prtn_t *osm_prtn_make_new(osm_log_t * p_log, osm_subn_t * p_subn,
66				     const char *name, uint16_t pkey);
67extern ib_api_status_t osm_prtn_add_all(osm_log_t * p_log,
68					osm_subn_t * p_subn,
69					osm_prtn_t * p, boolean_t full);
70extern ib_api_status_t osm_prtn_add_port(osm_log_t * p_log,
71					 osm_subn_t * p_subn, osm_prtn_t * p,
72					 ib_net64_t guid, boolean_t full);
73extern ib_api_status_t osm_prtn_add_mcgroup(osm_log_t * p_log,
74					    osm_subn_t * p_subn, osm_prtn_t * p,
75					    uint8_t rate,
76					    uint8_t mtu, uint8_t scope);
77
78static int partition_create(unsigned lineno, struct part_conf *conf,
79			    char *name, char *id, char *flag, char *flag_val)
80{
81	uint16_t pkey;
82	unsigned int scope;
83
84	if (!id && name && isdigit(*name)) {
85		id = name;
86		name = NULL;
87	}
88
89	if (id) {
90		char *end;
91
92		pkey = (uint16_t) strtoul(id, &end, 0);
93		if (end == id || *end)
94			return -1;
95	} else
96		pkey = 0;
97
98	conf->p_prtn = osm_prtn_make_new(conf->p_log, conf->p_subn,
99					 name, cl_hton16(pkey));
100	if (!conf->p_prtn)
101		return -1;
102
103	if (!conf->p_subn->opt.qos && conf->sl != OSM_DEFAULT_SL) {
104		OSM_LOG(conf->p_log, OSM_LOG_DEBUG, "Overriding SL %d"
105			" to default SL %d on partition %s"
106			" as QoS is not enabled.\n",
107			conf->sl, OSM_DEFAULT_SL, name);
108		conf->sl = OSM_DEFAULT_SL;
109	}
110	conf->p_prtn->sl = (uint8_t) conf->sl;
111
112	if (!conf->is_ipoib)
113		return 0;
114
115	if (!conf->scope_mask) {
116		osm_prtn_add_mcgroup(conf->p_log, conf->p_subn, conf->p_prtn,
117				     (uint8_t) conf->rate,
118				     (uint8_t) conf->mtu,
119				     0);
120		return 0;
121	}
122
123	for (scope = 0; scope < 16; scope++) {
124		if (((1<<scope) & conf->scope_mask) == 0)
125			continue;
126
127		osm_prtn_add_mcgroup(conf->p_log, conf->p_subn, conf->p_prtn,
128				     (uint8_t) conf->rate,
129				     (uint8_t) conf->mtu,
130				     (uint8_t) scope);
131	}
132	return 0;
133}
134
135static int partition_add_flag(unsigned lineno, struct part_conf *conf,
136			      char *flag, char *val)
137{
138	int len = strlen(flag);
139	if (!strncmp(flag, "ipoib", len)) {
140		conf->is_ipoib = 1;
141	} else if (!strncmp(flag, "mtu", len)) {
142		if (!val || (conf->mtu = strtoul(val, NULL, 0)) == 0)
143			OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
144				"PARSE WARN: line %d: "
145				"flag \'mtu\' requires valid value"
146				" - skipped\n", lineno);
147	} else if (!strncmp(flag, "rate", len)) {
148		if (!val || (conf->rate = strtoul(val, NULL, 0)) == 0)
149			OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
150				"PARSE WARN: line %d: "
151				"flag \'rate\' requires valid value"
152				" - skipped\n", lineno);
153	} else if (!strncmp(flag, "scope", len)) {
154		unsigned int scope;
155		if (!val || (scope = strtoul(val, NULL, 0)) == 0 || scope > 0xF)
156			OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
157				"PARSE WARN: line %d: "
158				"flag \'scope\' requires valid value"
159				" - skipped\n", lineno);
160		else
161			conf->scope_mask |= (1<<scope);
162	} else if (!strncmp(flag, "sl", len)) {
163		unsigned sl;
164		char *end;
165
166		if (!val || !*val || (sl = strtoul(val, &end, 0)) > 15 ||
167		    (*end && !isspace(*end)))
168			OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
169				"PARSE WARN: line %d: "
170				"flag \'sl\' requires valid value"
171				" - skipped\n", lineno);
172		else
173			conf->sl = sl;
174	} else if (!strncmp(flag, "defmember", len)) {
175		if (!val || (strncmp(val, "limited", strlen(val))
176			     && strncmp(val, "full", strlen(val))))
177			OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
178				"PARSE WARN: line %d: "
179				"flag \'defmember\' requires valid value (limited or full)"
180				" - skipped\n", lineno);
181		else
182			conf->full = strncmp(val, "full", strlen(val)) == 0;
183	} else {
184		OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
185			"PARSE WARN: line %d: "
186			"unrecognized partition flag \'%s\'"
187			" - ignored\n", lineno, flag);
188	}
189	return 0;
190}
191
192static int partition_add_port(unsigned lineno, struct part_conf *conf,
193			      char *name, char *flag)
194{
195	osm_prtn_t *p = conf->p_prtn;
196	ib_net64_t guid;
197	boolean_t full = conf->full;
198
199	if (!name || !*name || !strncmp(name, "NONE", strlen(name)))
200		return 0;
201
202	if (flag) {
203		/* reset default membership to limited */
204		full = FALSE;
205		if (!strncmp(flag, "full", strlen(flag)))
206			full = TRUE;
207		else if (strncmp(flag, "limited", strlen(flag))) {
208			OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
209				"PARSE WARN: line %d: "
210				"unrecognized port flag \'%s\'."
211				" Assume \'limited\'\n", lineno, flag);
212		}
213	}
214
215	if (!strncmp(name, "ALL", strlen(name))) {
216		return osm_prtn_add_all(conf->p_log, conf->p_subn, p,
217					full) == IB_SUCCESS ? 0 : -1;
218	} else if (!strncmp(name, "SELF", strlen(name))) {
219		guid = cl_ntoh64(conf->p_subn->sm_port_guid);
220	} else {
221		char *end;
222		guid = strtoull(name, &end, 0);
223		if (!guid || *end)
224			return -1;
225	}
226
227	if (osm_prtn_add_port(conf->p_log, conf->p_subn, p,
228			      cl_hton64(guid), full) != IB_SUCCESS)
229		return -1;
230
231	return 0;
232}
233
234/* conf file parser */
235
236#define STRIP_HEAD_SPACES(p) while (*(p) == ' ' || *(p) == '\t' || \
237		*(p) == '\n') { (p)++; }
238#define STRIP_TAIL_SPACES(p) { char *q = (p) + strlen(p); \
239				while ( q != (p) && ( *q == '\0' || \
240					*q == ' ' || *q == '\t' || \
241					*q == '\n')) { *q-- = '\0'; }; }
242
243static int parse_name_token(char *str, char **name, char **val)
244{
245	int len = 0;
246	char *p, *q;
247
248	*name = *val = NULL;
249
250	p = str;
251
252	while (*p == ' ' || *p == '\t' || *p == '\n')
253		p++;
254
255	q = strchr(p, '=');
256	if (q)
257		*q++ = '\0';
258
259	len = strlen(str) + 1;
260	str = q;
261
262	q = p + strlen(p);
263	while (q != p && (*q == '\0' || *q == ' ' || *q == '\t' || *q == '\n'))
264		*q-- = '\0';
265
266	*name = p;
267
268	p = str;
269	if (!p)
270		return len;
271
272	while (*p == ' ' || *p == '\t' || *p == '\n')
273		p++;
274
275	q = p + strlen(p);
276	len += (int)(q - str) + 1;
277	while (q != p && (*q == '\0' || *q == ' ' || *q == '\t' || *q == '\n'))
278		*q-- = '\0';
279	*val = p;
280
281	return len;
282}
283
284static struct part_conf *new_part_conf(osm_log_t * p_log, osm_subn_t * p_subn)
285{
286	static struct part_conf part;
287	struct part_conf *conf = &part;
288
289	memset(conf, 0, sizeof(*conf));
290	conf->p_log = p_log;
291	conf->p_subn = p_subn;
292	conf->p_prtn = NULL;
293	conf->is_ipoib = 0;
294	conf->sl = OSM_DEFAULT_SL;
295	conf->full = FALSE;
296	return conf;
297}
298
299static int flush_part_conf(struct part_conf *conf)
300{
301	memset(conf, 0, sizeof(*conf));
302	return 0;
303}
304
305static int parse_part_conf(struct part_conf *conf, char *str, int lineno)
306{
307	int ret, len = 0;
308	char *name, *id, *flag, *flval;
309	char *q, *p;
310
311	p = str;
312	if (*p == '\t' || *p == '\0' || *p == '\n')
313		p++;
314
315	len += (int)(p - str);
316	str = p;
317
318	if (conf->p_prtn)
319		goto skip_header;
320
321	q = strchr(p, ':');
322	if (!q) {
323		OSM_LOG(conf->p_log, OSM_LOG_ERROR, "PARSE ERROR: line %d: "
324			"no partition definition found\n", lineno);
325		fprintf(stderr, "\nPARSE ERROR: line %d: "
326			"no partition definition found\n", lineno);
327		return -1;
328	}
329
330	*q++ = '\0';
331	str = q;
332
333	name = id = flag = flval = NULL;
334
335	q = strchr(p, ',');
336	if (q)
337		*q = '\0';
338
339	ret = parse_name_token(p, &name, &id);
340	p += ret;
341	len += ret;
342
343	while (q) {
344		flag = flval = NULL;
345		q = strchr(p, ',');
346		if (q)
347			*q++ = '\0';
348		ret = parse_name_token(p, &flag, &flval);
349		if (!flag) {
350			OSM_LOG(conf->p_log, OSM_LOG_ERROR,
351				"PARSE ERROR: line %d: "
352				"bad partition flags\n", lineno);
353			fprintf(stderr, "\nPARSE ERROR: line %d: "
354				"bad partition flags\n", lineno);
355			return -1;
356		}
357		p += ret;
358		len += ret;
359		partition_add_flag(lineno, conf, flag, flval);
360	}
361
362	if (p != str || (partition_create(lineno, conf,
363					  name, id, flag, flval) < 0)) {
364		OSM_LOG(conf->p_log, OSM_LOG_ERROR, "PARSE ERROR: line %d: "
365			"bad partition definition\n", lineno);
366		fprintf(stderr, "\nPARSE ERROR: line %d: "
367			"bad partition definition\n", lineno);
368		return -1;
369	}
370
371skip_header:
372	do {
373		name = flag = NULL;
374		q = strchr(p, ',');
375		if (q)
376			*q++ = '\0';
377		ret = parse_name_token(p, &name, &flag);
378		if (partition_add_port(lineno, conf, name, flag) < 0) {
379			OSM_LOG(conf->p_log, OSM_LOG_ERROR,
380				"PARSE ERROR: line %d: "
381				"bad PortGUID\n", lineno);
382			fprintf(stderr, "PARSE ERROR: line %d: "
383				"bad PortGUID\n", lineno);
384			return -1;
385		}
386		p += ret;
387		len += ret;
388	} while (q);
389
390	return len;
391}
392
393int osm_prtn_config_parse_file(osm_log_t * p_log, osm_subn_t * p_subn,
394			       const char *file_name)
395{
396	char line[1024];
397	struct part_conf *conf = NULL;
398	FILE *file;
399	int lineno;
400
401	file = fopen(file_name, "r");
402	if (!file) {
403		OSM_LOG(p_log, OSM_LOG_VERBOSE,
404			"Cannot open config file \'%s\': %s\n",
405			file_name, strerror(errno));
406		return -1;
407	}
408
409	lineno = 0;
410
411	while (fgets(line, sizeof(line) - 1, file) != NULL) {
412		char *q, *p = line;
413
414		lineno++;
415
416		p = line;
417
418		q = strchr(p, '#');
419		if (q)
420			*q = '\0';
421
422		do {
423			int len;
424			while (*p == ' ' || *p == '\t' || *p == '\n')
425				p++;
426			if (*p == '\0')
427				break;
428
429			if (!conf && !(conf = new_part_conf(p_log, p_subn))) {
430				OSM_LOG(conf->p_log, OSM_LOG_ERROR,
431					"PARSE ERROR: line %d: "
432					"internal: cannot create config\n",
433					lineno);
434				fprintf(stderr,
435					"PARSE ERROR: line %d: "
436					"internal: cannot create config\n",
437					lineno);
438				break;
439			}
440
441			q = strchr(p, ';');
442			if (q)
443				*q = '\0';
444
445			len = parse_part_conf(conf, p, lineno);
446			if (len < 0) {
447				break;
448			}
449
450			p += len;
451
452			if (q) {
453				flush_part_conf(conf);
454				conf = NULL;
455			}
456		} while (q);
457	}
458
459	fclose(file);
460
461	return 0;
462}
463