1219820Sjeff/*
2219820Sjeff * Copyright (c) 2006-2007 Voltaire, Inc. All rights reserved.
3219820Sjeff *
4219820Sjeff * This software is available to you under a choice of one of two
5219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
6219820Sjeff * General Public License (GPL) Version 2, available from the file
7219820Sjeff * COPYING in the main directory of this source tree, or the
8219820Sjeff * OpenIB.org BSD license below:
9219820Sjeff *
10219820Sjeff *     Redistribution and use in source and binary forms, with or
11219820Sjeff *     without modification, are permitted provided that the following
12219820Sjeff *     conditions are met:
13219820Sjeff *
14219820Sjeff *      - Redistributions of source code must retain the above
15219820Sjeff *        copyright notice, this list of conditions and the following
16219820Sjeff *        disclaimer.
17219820Sjeff *
18219820Sjeff *      - Redistributions in binary form must reproduce the above
19219820Sjeff *        copyright notice, this list of conditions and the following
20219820Sjeff *        disclaimer in the documentation and/or other materials
21219820Sjeff *        provided with the distribution.
22219820Sjeff *
23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30219820Sjeff * SOFTWARE.
31219820Sjeff *
32219820Sjeff */
33219820Sjeff
34219820Sjeff/*
35219820Sjeff * Abstract:
36219820Sjeff *    Implementation of opensm partition management configuration
37219820Sjeff */
38219820Sjeff
39219820Sjeff#if HAVE_CONFIG_H
40219820Sjeff#  include <config.h>
41219820Sjeff#endif				/* HAVE_CONFIG_H */
42219820Sjeff
43219820Sjeff#include <stdlib.h>
44219820Sjeff#include <stdio.h>
45219820Sjeff#include <string.h>
46219820Sjeff#include <errno.h>
47219820Sjeff#include <ctype.h>
48219820Sjeff
49219820Sjeff#include <iba/ib_types.h>
50219820Sjeff#include <opensm/osm_base.h>
51219820Sjeff#include <opensm/osm_partition.h>
52219820Sjeff#include <opensm/osm_subnet.h>
53219820Sjeff#include <opensm/osm_log.h>
54219820Sjeff
55219820Sjeff#include <complib/cl_byteswap.h>
56219820Sjeff
57219820Sjeffstruct part_conf {
58219820Sjeff	osm_log_t *p_log;
59219820Sjeff	osm_subn_t *p_subn;
60219820Sjeff	osm_prtn_t *p_prtn;
61219820Sjeff	unsigned is_ipoib, mtu, rate, sl, scope_mask;
62219820Sjeff	boolean_t full;
63219820Sjeff};
64219820Sjeff
65219820Sjeffextern osm_prtn_t *osm_prtn_make_new(osm_log_t * p_log, osm_subn_t * p_subn,
66219820Sjeff				     const char *name, uint16_t pkey);
67219820Sjeffextern ib_api_status_t osm_prtn_add_all(osm_log_t * p_log,
68219820Sjeff					osm_subn_t * p_subn,
69219820Sjeff					osm_prtn_t * p, boolean_t full);
70219820Sjeffextern ib_api_status_t osm_prtn_add_port(osm_log_t * p_log,
71219820Sjeff					 osm_subn_t * p_subn, osm_prtn_t * p,
72219820Sjeff					 ib_net64_t guid, boolean_t full);
73219820Sjeffextern ib_api_status_t osm_prtn_add_mcgroup(osm_log_t * p_log,
74219820Sjeff					    osm_subn_t * p_subn, osm_prtn_t * p,
75219820Sjeff					    uint8_t rate,
76219820Sjeff					    uint8_t mtu, uint8_t scope);
77219820Sjeff
78219820Sjeffstatic int partition_create(unsigned lineno, struct part_conf *conf,
79219820Sjeff			    char *name, char *id, char *flag, char *flag_val)
80219820Sjeff{
81219820Sjeff	uint16_t pkey;
82219820Sjeff	unsigned int scope;
83219820Sjeff
84219820Sjeff	if (!id && name && isdigit(*name)) {
85219820Sjeff		id = name;
86219820Sjeff		name = NULL;
87219820Sjeff	}
88219820Sjeff
89219820Sjeff	if (id) {
90219820Sjeff		char *end;
91219820Sjeff
92219820Sjeff		pkey = (uint16_t) strtoul(id, &end, 0);
93219820Sjeff		if (end == id || *end)
94219820Sjeff			return -1;
95219820Sjeff	} else
96219820Sjeff		pkey = 0;
97219820Sjeff
98219820Sjeff	conf->p_prtn = osm_prtn_make_new(conf->p_log, conf->p_subn,
99219820Sjeff					 name, cl_hton16(pkey));
100219820Sjeff	if (!conf->p_prtn)
101219820Sjeff		return -1;
102219820Sjeff
103219820Sjeff	if (!conf->p_subn->opt.qos && conf->sl != OSM_DEFAULT_SL) {
104219820Sjeff		OSM_LOG(conf->p_log, OSM_LOG_DEBUG, "Overriding SL %d"
105219820Sjeff			" to default SL %d on partition %s"
106219820Sjeff			" as QoS is not enabled.\n",
107219820Sjeff			conf->sl, OSM_DEFAULT_SL, name);
108219820Sjeff		conf->sl = OSM_DEFAULT_SL;
109219820Sjeff	}
110219820Sjeff	conf->p_prtn->sl = (uint8_t) conf->sl;
111219820Sjeff
112219820Sjeff	if (!conf->is_ipoib)
113219820Sjeff		return 0;
114219820Sjeff
115219820Sjeff	if (!conf->scope_mask) {
116219820Sjeff		osm_prtn_add_mcgroup(conf->p_log, conf->p_subn, conf->p_prtn,
117219820Sjeff				     (uint8_t) conf->rate,
118219820Sjeff				     (uint8_t) conf->mtu,
119219820Sjeff				     0);
120219820Sjeff		return 0;
121219820Sjeff	}
122219820Sjeff
123219820Sjeff	for (scope = 0; scope < 16; scope++) {
124219820Sjeff		if (((1<<scope) & conf->scope_mask) == 0)
125219820Sjeff			continue;
126219820Sjeff
127219820Sjeff		osm_prtn_add_mcgroup(conf->p_log, conf->p_subn, conf->p_prtn,
128219820Sjeff				     (uint8_t) conf->rate,
129219820Sjeff				     (uint8_t) conf->mtu,
130219820Sjeff				     (uint8_t) scope);
131219820Sjeff	}
132219820Sjeff	return 0;
133219820Sjeff}
134219820Sjeff
135219820Sjeffstatic int partition_add_flag(unsigned lineno, struct part_conf *conf,
136219820Sjeff			      char *flag, char *val)
137219820Sjeff{
138219820Sjeff	int len = strlen(flag);
139219820Sjeff	if (!strncmp(flag, "ipoib", len)) {
140219820Sjeff		conf->is_ipoib = 1;
141219820Sjeff	} else if (!strncmp(flag, "mtu", len)) {
142219820Sjeff		if (!val || (conf->mtu = strtoul(val, NULL, 0)) == 0)
143219820Sjeff			OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
144219820Sjeff				"PARSE WARN: line %d: "
145219820Sjeff				"flag \'mtu\' requires valid value"
146219820Sjeff				" - skipped\n", lineno);
147219820Sjeff	} else if (!strncmp(flag, "rate", len)) {
148219820Sjeff		if (!val || (conf->rate = strtoul(val, NULL, 0)) == 0)
149219820Sjeff			OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
150219820Sjeff				"PARSE WARN: line %d: "
151219820Sjeff				"flag \'rate\' requires valid value"
152219820Sjeff				" - skipped\n", lineno);
153219820Sjeff	} else if (!strncmp(flag, "scope", len)) {
154219820Sjeff		unsigned int scope;
155219820Sjeff		if (!val || (scope = strtoul(val, NULL, 0)) == 0 || scope > 0xF)
156219820Sjeff			OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
157219820Sjeff				"PARSE WARN: line %d: "
158219820Sjeff				"flag \'scope\' requires valid value"
159219820Sjeff				" - skipped\n", lineno);
160219820Sjeff		else
161219820Sjeff			conf->scope_mask |= (1<<scope);
162219820Sjeff	} else if (!strncmp(flag, "sl", len)) {
163219820Sjeff		unsigned sl;
164219820Sjeff		char *end;
165219820Sjeff
166219820Sjeff		if (!val || !*val || (sl = strtoul(val, &end, 0)) > 15 ||
167219820Sjeff		    (*end && !isspace(*end)))
168219820Sjeff			OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
169219820Sjeff				"PARSE WARN: line %d: "
170219820Sjeff				"flag \'sl\' requires valid value"
171219820Sjeff				" - skipped\n", lineno);
172219820Sjeff		else
173219820Sjeff			conf->sl = sl;
174219820Sjeff	} else if (!strncmp(flag, "defmember", len)) {
175219820Sjeff		if (!val || (strncmp(val, "limited", strlen(val))
176219820Sjeff			     && strncmp(val, "full", strlen(val))))
177219820Sjeff			OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
178219820Sjeff				"PARSE WARN: line %d: "
179219820Sjeff				"flag \'defmember\' requires valid value (limited or full)"
180219820Sjeff				" - skipped\n", lineno);
181219820Sjeff		else
182219820Sjeff			conf->full = strncmp(val, "full", strlen(val)) == 0;
183219820Sjeff	} else {
184219820Sjeff		OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
185219820Sjeff			"PARSE WARN: line %d: "
186219820Sjeff			"unrecognized partition flag \'%s\'"
187219820Sjeff			" - ignored\n", lineno, flag);
188219820Sjeff	}
189219820Sjeff	return 0;
190219820Sjeff}
191219820Sjeff
192219820Sjeffstatic int partition_add_port(unsigned lineno, struct part_conf *conf,
193219820Sjeff			      char *name, char *flag)
194219820Sjeff{
195219820Sjeff	osm_prtn_t *p = conf->p_prtn;
196219820Sjeff	ib_net64_t guid;
197219820Sjeff	boolean_t full = conf->full;
198219820Sjeff
199219820Sjeff	if (!name || !*name || !strncmp(name, "NONE", strlen(name)))
200219820Sjeff		return 0;
201219820Sjeff
202219820Sjeff	if (flag) {
203219820Sjeff		/* reset default membership to limited */
204219820Sjeff		full = FALSE;
205219820Sjeff		if (!strncmp(flag, "full", strlen(flag)))
206219820Sjeff			full = TRUE;
207219820Sjeff		else if (strncmp(flag, "limited", strlen(flag))) {
208219820Sjeff			OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
209219820Sjeff				"PARSE WARN: line %d: "
210219820Sjeff				"unrecognized port flag \'%s\'."
211219820Sjeff				" Assume \'limited\'\n", lineno, flag);
212219820Sjeff		}
213219820Sjeff	}
214219820Sjeff
215219820Sjeff	if (!strncmp(name, "ALL", strlen(name))) {
216219820Sjeff		return osm_prtn_add_all(conf->p_log, conf->p_subn, p,
217219820Sjeff					full) == IB_SUCCESS ? 0 : -1;
218219820Sjeff	} else if (!strncmp(name, "SELF", strlen(name))) {
219219820Sjeff		guid = cl_ntoh64(conf->p_subn->sm_port_guid);
220219820Sjeff	} else {
221219820Sjeff		char *end;
222219820Sjeff		guid = strtoull(name, &end, 0);
223219820Sjeff		if (!guid || *end)
224219820Sjeff			return -1;
225219820Sjeff	}
226219820Sjeff
227219820Sjeff	if (osm_prtn_add_port(conf->p_log, conf->p_subn, p,
228219820Sjeff			      cl_hton64(guid), full) != IB_SUCCESS)
229219820Sjeff		return -1;
230219820Sjeff
231219820Sjeff	return 0;
232219820Sjeff}
233219820Sjeff
234219820Sjeff/* conf file parser */
235219820Sjeff
236219820Sjeff#define STRIP_HEAD_SPACES(p) while (*(p) == ' ' || *(p) == '\t' || \
237219820Sjeff		*(p) == '\n') { (p)++; }
238219820Sjeff#define STRIP_TAIL_SPACES(p) { char *q = (p) + strlen(p); \
239219820Sjeff				while ( q != (p) && ( *q == '\0' || \
240219820Sjeff					*q == ' ' || *q == '\t' || \
241219820Sjeff					*q == '\n')) { *q-- = '\0'; }; }
242219820Sjeff
243219820Sjeffstatic int parse_name_token(char *str, char **name, char **val)
244219820Sjeff{
245219820Sjeff	int len = 0;
246219820Sjeff	char *p, *q;
247219820Sjeff
248219820Sjeff	*name = *val = NULL;
249219820Sjeff
250219820Sjeff	p = str;
251219820Sjeff
252219820Sjeff	while (*p == ' ' || *p == '\t' || *p == '\n')
253219820Sjeff		p++;
254219820Sjeff
255219820Sjeff	q = strchr(p, '=');
256219820Sjeff	if (q)
257219820Sjeff		*q++ = '\0';
258219820Sjeff
259219820Sjeff	len = strlen(str) + 1;
260219820Sjeff	str = q;
261219820Sjeff
262219820Sjeff	q = p + strlen(p);
263219820Sjeff	while (q != p && (*q == '\0' || *q == ' ' || *q == '\t' || *q == '\n'))
264219820Sjeff		*q-- = '\0';
265219820Sjeff
266219820Sjeff	*name = p;
267219820Sjeff
268219820Sjeff	p = str;
269219820Sjeff	if (!p)
270219820Sjeff		return len;
271219820Sjeff
272219820Sjeff	while (*p == ' ' || *p == '\t' || *p == '\n')
273219820Sjeff		p++;
274219820Sjeff
275219820Sjeff	q = p + strlen(p);
276219820Sjeff	len += (int)(q - str) + 1;
277219820Sjeff	while (q != p && (*q == '\0' || *q == ' ' || *q == '\t' || *q == '\n'))
278219820Sjeff		*q-- = '\0';
279219820Sjeff	*val = p;
280219820Sjeff
281219820Sjeff	return len;
282219820Sjeff}
283219820Sjeff
284219820Sjeffstatic struct part_conf *new_part_conf(osm_log_t * p_log, osm_subn_t * p_subn)
285219820Sjeff{
286219820Sjeff	static struct part_conf part;
287219820Sjeff	struct part_conf *conf = &part;
288219820Sjeff
289219820Sjeff	memset(conf, 0, sizeof(*conf));
290219820Sjeff	conf->p_log = p_log;
291219820Sjeff	conf->p_subn = p_subn;
292219820Sjeff	conf->p_prtn = NULL;
293219820Sjeff	conf->is_ipoib = 0;
294219820Sjeff	conf->sl = OSM_DEFAULT_SL;
295219820Sjeff	conf->full = FALSE;
296219820Sjeff	return conf;
297219820Sjeff}
298219820Sjeff
299219820Sjeffstatic int flush_part_conf(struct part_conf *conf)
300219820Sjeff{
301219820Sjeff	memset(conf, 0, sizeof(*conf));
302219820Sjeff	return 0;
303219820Sjeff}
304219820Sjeff
305219820Sjeffstatic int parse_part_conf(struct part_conf *conf, char *str, int lineno)
306219820Sjeff{
307219820Sjeff	int ret, len = 0;
308219820Sjeff	char *name, *id, *flag, *flval;
309219820Sjeff	char *q, *p;
310219820Sjeff
311219820Sjeff	p = str;
312219820Sjeff	if (*p == '\t' || *p == '\0' || *p == '\n')
313219820Sjeff		p++;
314219820Sjeff
315219820Sjeff	len += (int)(p - str);
316219820Sjeff	str = p;
317219820Sjeff
318219820Sjeff	if (conf->p_prtn)
319219820Sjeff		goto skip_header;
320219820Sjeff
321219820Sjeff	q = strchr(p, ':');
322219820Sjeff	if (!q) {
323219820Sjeff		OSM_LOG(conf->p_log, OSM_LOG_ERROR, "PARSE ERROR: line %d: "
324219820Sjeff			"no partition definition found\n", lineno);
325219820Sjeff		fprintf(stderr, "\nPARSE ERROR: line %d: "
326219820Sjeff			"no partition definition found\n", lineno);
327219820Sjeff		return -1;
328219820Sjeff	}
329219820Sjeff
330219820Sjeff	*q++ = '\0';
331219820Sjeff	str = q;
332219820Sjeff
333219820Sjeff	name = id = flag = flval = NULL;
334219820Sjeff
335219820Sjeff	q = strchr(p, ',');
336219820Sjeff	if (q)
337219820Sjeff		*q = '\0';
338219820Sjeff
339219820Sjeff	ret = parse_name_token(p, &name, &id);
340219820Sjeff	p += ret;
341219820Sjeff	len += ret;
342219820Sjeff
343219820Sjeff	while (q) {
344219820Sjeff		flag = flval = NULL;
345219820Sjeff		q = strchr(p, ',');
346219820Sjeff		if (q)
347219820Sjeff			*q++ = '\0';
348219820Sjeff		ret = parse_name_token(p, &flag, &flval);
349219820Sjeff		if (!flag) {
350219820Sjeff			OSM_LOG(conf->p_log, OSM_LOG_ERROR,
351219820Sjeff				"PARSE ERROR: line %d: "
352219820Sjeff				"bad partition flags\n", lineno);
353219820Sjeff			fprintf(stderr, "\nPARSE ERROR: line %d: "
354219820Sjeff				"bad partition flags\n", lineno);
355219820Sjeff			return -1;
356219820Sjeff		}
357219820Sjeff		p += ret;
358219820Sjeff		len += ret;
359219820Sjeff		partition_add_flag(lineno, conf, flag, flval);
360219820Sjeff	}
361219820Sjeff
362219820Sjeff	if (p != str || (partition_create(lineno, conf,
363219820Sjeff					  name, id, flag, flval) < 0)) {
364219820Sjeff		OSM_LOG(conf->p_log, OSM_LOG_ERROR, "PARSE ERROR: line %d: "
365219820Sjeff			"bad partition definition\n", lineno);
366219820Sjeff		fprintf(stderr, "\nPARSE ERROR: line %d: "
367219820Sjeff			"bad partition definition\n", lineno);
368219820Sjeff		return -1;
369219820Sjeff	}
370219820Sjeff
371219820Sjeffskip_header:
372219820Sjeff	do {
373219820Sjeff		name = flag = NULL;
374219820Sjeff		q = strchr(p, ',');
375219820Sjeff		if (q)
376219820Sjeff			*q++ = '\0';
377219820Sjeff		ret = parse_name_token(p, &name, &flag);
378219820Sjeff		if (partition_add_port(lineno, conf, name, flag) < 0) {
379219820Sjeff			OSM_LOG(conf->p_log, OSM_LOG_ERROR,
380219820Sjeff				"PARSE ERROR: line %d: "
381219820Sjeff				"bad PortGUID\n", lineno);
382219820Sjeff			fprintf(stderr, "PARSE ERROR: line %d: "
383219820Sjeff				"bad PortGUID\n", lineno);
384219820Sjeff			return -1;
385219820Sjeff		}
386219820Sjeff		p += ret;
387219820Sjeff		len += ret;
388219820Sjeff	} while (q);
389219820Sjeff
390219820Sjeff	return len;
391219820Sjeff}
392219820Sjeff
393219820Sjeffint osm_prtn_config_parse_file(osm_log_t * p_log, osm_subn_t * p_subn,
394219820Sjeff			       const char *file_name)
395219820Sjeff{
396219820Sjeff	char line[1024];
397219820Sjeff	struct part_conf *conf = NULL;
398219820Sjeff	FILE *file;
399219820Sjeff	int lineno;
400219820Sjeff
401219820Sjeff	file = fopen(file_name, "r");
402219820Sjeff	if (!file) {
403219820Sjeff		OSM_LOG(p_log, OSM_LOG_VERBOSE,
404219820Sjeff			"Cannot open config file \'%s\': %s\n",
405219820Sjeff			file_name, strerror(errno));
406219820Sjeff		return -1;
407219820Sjeff	}
408219820Sjeff
409219820Sjeff	lineno = 0;
410219820Sjeff
411219820Sjeff	while (fgets(line, sizeof(line) - 1, file) != NULL) {
412219820Sjeff		char *q, *p = line;
413219820Sjeff
414219820Sjeff		lineno++;
415219820Sjeff
416219820Sjeff		p = line;
417219820Sjeff
418219820Sjeff		q = strchr(p, '#');
419219820Sjeff		if (q)
420219820Sjeff			*q = '\0';
421219820Sjeff
422219820Sjeff		do {
423219820Sjeff			int len;
424219820Sjeff			while (*p == ' ' || *p == '\t' || *p == '\n')
425219820Sjeff				p++;
426219820Sjeff			if (*p == '\0')
427219820Sjeff				break;
428219820Sjeff
429219820Sjeff			if (!conf && !(conf = new_part_conf(p_log, p_subn))) {
430219820Sjeff				OSM_LOG(conf->p_log, OSM_LOG_ERROR,
431219820Sjeff					"PARSE ERROR: line %d: "
432219820Sjeff					"internal: cannot create config\n",
433219820Sjeff					lineno);
434219820Sjeff				fprintf(stderr,
435219820Sjeff					"PARSE ERROR: line %d: "
436219820Sjeff					"internal: cannot create config\n",
437219820Sjeff					lineno);
438219820Sjeff				break;
439219820Sjeff			}
440219820Sjeff
441219820Sjeff			q = strchr(p, ';');
442219820Sjeff			if (q)
443219820Sjeff				*q = '\0';
444219820Sjeff
445219820Sjeff			len = parse_part_conf(conf, p, lineno);
446219820Sjeff			if (len < 0) {
447219820Sjeff				break;
448219820Sjeff			}
449219820Sjeff
450219820Sjeff			p += len;
451219820Sjeff
452219820Sjeff			if (q) {
453219820Sjeff				flush_part_conf(conf);
454219820Sjeff				conf = NULL;
455219820Sjeff			}
456219820Sjeff		} while (q);
457219820Sjeff	}
458219820Sjeff
459219820Sjeff	fclose(file);
460219820Sjeff
461219820Sjeff	return 0;
462219820Sjeff}
463