1/*
2 * Copyright (c) 2005-2009 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2009,2010 HNR Consulting. All rights reserved.
4 * Copyright (c) 2010,2011 Mellanox Technologies LTD. All rights reserved.
5 *
6 * This software is available to you under a choice of one of two
7 * licenses.  You may choose to be licensed under the terms of the GNU
8 * General Public License (GPL) Version 2, available from the file
9 * COPYING in the main directory of this source tree, or the
10 * OpenIB.org BSD license below:
11 *
12 *     Redistribution and use in source and binary forms, with or
13 *     without modification, are permitted provided that the following
14 *     conditions are met:
15 *
16 *      - Redistributions of source code must retain the above
17 *        copyright notice, this list of conditions and the following
18 *        disclaimer.
19 *
20 *      - Redistributions in binary form must reproduce the above
21 *        copyright notice, this list of conditions and the following
22 *        disclaimer in the documentation and/or other materials
23 *        provided with the distribution.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 * SOFTWARE.
33 *
34 */
35
36#if HAVE_CONFIG_H
37#  include <config.h>
38#endif				/* HAVE_CONFIG_H */
39
40#define	_WITH_GETLINE		/* for getline */
41#include <stdio.h>
42#include <stdlib.h>
43#include <sys/poll.h>
44#include <sys/types.h>
45#include <sys/socket.h>
46#include <netdb.h>
47#include <regex.h>
48#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
49#include <arpa/inet.h>
50#include <netinet/in.h>
51#include <sys/socket.h>
52#endif
53#include <unistd.h>
54#include <errno.h>
55#include <ctype.h>
56#include <sys/time.h>
57#include <opensm/osm_file_ids.h>
58#define FILE_ID OSM_FILE_CONSOLE_C
59#include <opensm/osm_console.h>
60#include <complib/cl_passivelock.h>
61#include <opensm/osm_perfmgr.h>
62#include <opensm/osm_subnet.h>
63
64extern void osm_update_node_desc(IN osm_opensm_t *osm);
65
66struct command {
67	const char *name;
68	void (*help_function) (FILE * out, int detail);
69	void (*parse_function) (char **p_last, osm_opensm_t * p_osm,
70				FILE * out);
71};
72
73static struct {
74	int on;
75	int delay_s;
76	time_t previous;
77	void (*loop_function) (osm_opensm_t * p_osm, FILE * out);
78} loop_command = {
79.on = 0, .delay_s = 2, .loop_function = NULL};
80
81static const struct command console_cmds[];
82
83static char *next_token(char **p_last)
84{
85	return strtok_r(NULL, " \t\n\r", p_last);
86}
87
88#ifdef ENABLE_OSM_PERF_MGR
89static char *name_token(char **p_last)
90{
91	return strtok_r(NULL, "\t\n\r", p_last);
92}
93#endif
94
95static void help_command(FILE * out, int detail)
96{
97	int i;
98
99	fprintf(out, "Supported commands and syntax:\n");
100	fprintf(out, "help [<command>]\n");
101	/* skip help command */
102	for (i = 1; console_cmds[i].name; i++)
103		console_cmds[i].help_function(out, 0);
104}
105
106static void help_quit(FILE * out, int detail)
107{
108	fprintf(out, "quit (not valid in local mode; use ctl-c)\n");
109}
110
111static void help_loglevel(FILE * out, int detail)
112{
113	fprintf(out, "loglevel [<log-level>]\n");
114	if (detail) {
115		fprintf(out, "   log-level is OR'ed from the following\n");
116		fprintf(out, "   OSM_LOG_NONE             0x%02X\n",
117			OSM_LOG_NONE);
118		fprintf(out, "   OSM_LOG_ERROR            0x%02X\n",
119			OSM_LOG_ERROR);
120		fprintf(out, "   OSM_LOG_INFO             0x%02X\n",
121			OSM_LOG_INFO);
122		fprintf(out, "   OSM_LOG_VERBOSE          0x%02X\n",
123			OSM_LOG_VERBOSE);
124		fprintf(out, "   OSM_LOG_DEBUG            0x%02X\n",
125			OSM_LOG_DEBUG);
126		fprintf(out, "   OSM_LOG_FUNCS            0x%02X\n",
127			OSM_LOG_FUNCS);
128		fprintf(out, "   OSM_LOG_FRAMES           0x%02X\n",
129			OSM_LOG_FRAMES);
130		fprintf(out, "   OSM_LOG_ROUTING          0x%02X\n",
131			OSM_LOG_ROUTING);
132		fprintf(out, "   OSM_LOG_SYS              0x%02X\n",
133			OSM_LOG_SYS);
134		fprintf(out, "\n");
135		fprintf(out, "   OSM_LOG_DEFAULT_LEVEL    0x%02X\n",
136			OSM_LOG_DEFAULT_LEVEL);
137	}
138}
139
140static void help_permodlog(FILE * out, int detail)
141{
142	fprintf(out, "permodlog\n");
143}
144
145static void help_priority(FILE * out, int detail)
146{
147	fprintf(out, "priority [<sm-priority>]\n");
148}
149
150static void help_resweep(FILE * out, int detail)
151{
152	fprintf(out, "resweep [heavy|light]\n");
153}
154
155static void help_reroute(FILE * out, int detail)
156{
157	fprintf(out, "reroute\n");
158	if (detail) {
159		fprintf(out, "reroute the fabric\n");
160	}
161}
162
163static void help_sweep(FILE * out, int detail)
164{
165	fprintf(out, "sweep [on|off]\n");
166	if (detail) {
167		fprintf(out, "enable or disable sweeping\n");
168		fprintf(out, "   [on] sweep normally\n");
169		fprintf(out, "   [off] inhibit all sweeping\n");
170	}
171}
172
173static void help_status(FILE * out, int detail)
174{
175	fprintf(out, "status [loop]\n");
176	if (detail) {
177		fprintf(out, "   loop -- type \"q<ret>\" to quit\n");
178	}
179}
180
181static void help_logflush(FILE * out, int detail)
182{
183	fprintf(out, "logflush [on|off] -- toggle opensm.log file flushing\n");
184}
185
186static void help_querylid(FILE * out, int detail)
187{
188	fprintf(out,
189		"querylid lid -- print internal information about the lid specified\n");
190}
191
192static void help_portstatus(FILE * out, int detail)
193{
194	fprintf(out, "portstatus [ca|switch|router]\n");
195	if (detail) {
196		fprintf(out, "summarize port status\n");
197		fprintf(out,
198			"   [ca|switch|router] -- limit the results to the node type specified\n");
199	}
200
201}
202
203static void help_switchbalance(FILE * out, int detail)
204{
205	fprintf(out, "switchbalance [verbose] [guid]\n");
206	if (detail) {
207		fprintf(out, "output switch balancing information\n");
208		fprintf(out,
209			"  [verbose] -- verbose output\n"
210			"  [guid] -- limit results to specified guid\n");
211	}
212}
213
214static void help_lidbalance(FILE * out, int detail)
215{
216	fprintf(out, "lidbalance [switchguid]\n");
217	if (detail) {
218		fprintf(out, "output lid balanced forwarding information\n");
219		fprintf(out,
220			"  [switchguid] -- limit results to specified switch guid\n");
221	}
222}
223
224static void help_dump_conf(FILE *out, int detail)
225{
226	fprintf(out, "dump_conf\n");
227	if (detail) {
228		fprintf(out, "dump current opensm configuration\n");
229	}
230}
231
232static void help_update_desc(FILE *out, int detail)
233{
234	fprintf(out, "update_desc\n");
235	if (detail) {
236		fprintf(out, "update node description for all nodes\n");
237	}
238}
239
240#ifdef ENABLE_OSM_PERF_MGR
241static void help_perfmgr(FILE * out, int detail)
242{
243	fprintf(out,
244		"perfmgr(pm) [enable|disable\n"
245		"             |clear_counters|dump_counters|print_counters(pc)|print_errors(pe)\n"
246		"             |set_rm_nodes|clear_rm_nodes|clear_inactive\n"
247		"             |set_query_cpi|clear_query_cpi\n"
248		"             |dump_redir|clear_redir\n"
249		"             |sweep|sweep_time[seconds]]\n");
250	if (detail) {
251		fprintf(out,
252			"perfmgr -- print the performance manager state\n");
253		fprintf(out,
254			"   [enable|disable] -- change the perfmgr state\n");
255		fprintf(out,
256			"   [sweep] -- Initiate a sweep of the fabric\n");
257		fprintf(out,
258			"   [sweep_time] -- change the perfmgr sweep time (requires [seconds] option)\n");
259		fprintf(out,
260			"   [clear_counters] -- clear the counters stored\n");
261		fprintf(out,
262			"   [dump_counters [mach]] -- dump the counters (optionally in [mach]ine readable format)\n");
263		fprintf(out,
264			"   [print_counters [<nodename|nodeguid>][:<port>]] -- print the internal counters\n"
265			"                                                      Optionally limit output by name, guid, or port\n");
266		fprintf(out,
267			"   [pc [<nodename|nodeguid>][:<port>]] -- same as print_counters\n");
268		fprintf(out,
269			"   [print_errors [<nodename|nodeguid>]] -- print only ports with errors\n"
270			"                                           Optionally limit output by name or guid\n");
271		fprintf(out,
272			"   [pe [<nodename|nodeguid>]] -- same as print_errors\n");
273		fprintf(out,
274			"   [dump_redir [<nodename|nodeguid>]] -- dump the redirection table\n");
275		fprintf(out,
276			"   [clear_redir [<nodename|nodeguid>]] -- clear the redirection table\n");
277		fprintf(out,
278			"   [[set|clear]_rm_nodes] -- enable/disable the removal of \"inactive\" nodes from the DB\n"
279			"                             Inactive nodes are those which no longer appear on the fabric\n");
280		fprintf(out,
281			"   [[set|clear]_query_cpi] -- enable/disable PerfMgrGet(ClassPortInfo)\n"
282			"                             ClassPortInfo indicates hardware support for extended attributes such as PortCountersExtended\n");
283		fprintf(out,
284			"   [clear_inactive] -- Delete inactive nodes from the DB\n");
285	}
286}
287static void help_pm(FILE *out, int detail)
288{
289	if (detail)
290		help_perfmgr(out, detail);
291}
292#endif				/* ENABLE_OSM_PERF_MGR */
293
294/* more help routines go here */
295
296static void help_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
297{
298	char *p_cmd;
299	int i, found = 0;
300
301	p_cmd = next_token(p_last);
302	if (!p_cmd)
303		help_command(out, 0);
304	else {
305		for (i = 1; console_cmds[i].name; i++) {
306			if (!strcmp(p_cmd, console_cmds[i].name)) {
307				found = 1;
308				console_cmds[i].help_function(out, 1);
309				break;
310			}
311		}
312		if (!found) {
313			fprintf(out, "%s : Command not found\n\n", p_cmd);
314			help_command(out, 0);
315		}
316	}
317}
318
319static void loglevel_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
320{
321	char *p_cmd;
322	int level;
323
324	p_cmd = next_token(p_last);
325	if (!p_cmd)
326		fprintf(out, "Current log level is 0x%x\n",
327			osm_log_get_level(&p_osm->log));
328	else {
329		/* Handle x, 0x, and decimal specification of log level */
330		if (!strncmp(p_cmd, "x", 1)) {
331			p_cmd++;
332			level = strtoul(p_cmd, NULL, 16);
333		} else {
334			if (!strncmp(p_cmd, "0x", 2)) {
335				p_cmd += 2;
336				level = strtoul(p_cmd, NULL, 16);
337			} else
338				level = strtol(p_cmd, NULL, 10);
339		}
340		if ((level >= 0) && (level < 256)) {
341			fprintf(out, "Setting log level to 0x%x\n", level);
342			osm_log_set_level(&p_osm->log, level);
343		} else
344			fprintf(out, "Invalid log level 0x%x\n", level);
345	}
346}
347
348static void permodlog_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
349{
350	FILE *fp;
351	char buf[1024];
352
353	if (p_osm->subn.opt.per_module_logging_file != NULL) {
354		fp = fopen(p_osm->subn.opt.per_module_logging_file, "r");
355		if (!fp) {
356			if (errno == ENOENT)
357				return;
358			fprintf(out, "fopen(%s) failed: %s\n",
359				p_osm->subn.opt.per_module_logging_file,
360				strerror(errno));
361			return;
362		}
363
364		fprintf(out, "Per module logging file: %s\n",
365			p_osm->subn.opt.per_module_logging_file);
366		while (fgets(buf, sizeof buf, fp) != NULL)
367			fprintf(out, "%s", buf);
368		fclose(fp);
369		fprintf(out, "\n");
370	}
371}
372
373static void priority_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
374{
375	char *p_cmd;
376	int priority;
377
378	p_cmd = next_token(p_last);
379	if (!p_cmd)
380		fprintf(out, "Current sm-priority is %d\n",
381			p_osm->subn.opt.sm_priority);
382	else {
383		priority = strtol(p_cmd, NULL, 0);
384		if (0 > priority || 15 < priority)
385			fprintf(out,
386				"Invalid sm-priority %d; must be between 0 and 15\n",
387				priority);
388		else {
389			fprintf(out, "Setting sm-priority to %d\n", priority);
390			osm_set_sm_priority(&p_osm->sm, (uint8_t)priority);
391		}
392	}
393}
394
395static const char *sm_state_str(int state)
396{
397	switch (state) {
398	case IB_SMINFO_STATE_DISCOVERING:
399		return "Discovering";
400	case IB_SMINFO_STATE_STANDBY:
401		return "Standby    ";
402	case IB_SMINFO_STATE_NOTACTIVE:
403		return "Not Active ";
404	case IB_SMINFO_STATE_MASTER:
405		return "Master     ";
406	}
407	return "UNKNOWN    ";
408}
409
410static const char *sa_state_str(osm_sa_state_t state)
411{
412	switch (state) {
413	case OSM_SA_STATE_INIT:
414		return "Init";
415	case OSM_SA_STATE_READY:
416		return "Ready";
417	}
418	return "UNKNOWN";
419}
420
421static void dump_sms(osm_opensm_t * p_osm, FILE * out)
422{
423	osm_subn_t *p_subn = &p_osm->subn;
424	osm_remote_sm_t *p_rsm;
425
426	fprintf(out, "\n   Known SMs\n"
427		     "   ---------\n");
428	fprintf(out, "   Port GUID       SM State    Priority\n");
429	fprintf(out, "   ---------       --------    --------\n");
430	fprintf(out, "   0x%" PRIx64 " %s %d        SELF\n",
431		cl_ntoh64(p_subn->sm_port_guid),
432		sm_state_str(p_subn->sm_state),
433		p_subn->opt.sm_priority);
434
435	CL_PLOCK_ACQUIRE(p_osm->sm.p_lock);
436	p_rsm = (osm_remote_sm_t *) cl_qmap_head(&p_subn->sm_guid_tbl);
437	while (p_rsm != (osm_remote_sm_t *) cl_qmap_end(&p_subn->sm_guid_tbl)) {
438		fprintf(out, "   0x%" PRIx64 " %s %d\n",
439			cl_ntoh64(p_rsm->smi.guid),
440			sm_state_str(ib_sminfo_get_state(&p_rsm->smi)),
441			ib_sminfo_get_priority(&p_rsm->smi));
442		p_rsm = (osm_remote_sm_t *) cl_qmap_next(&p_rsm->map_item);
443	}
444	CL_PLOCK_RELEASE(p_osm->sm.p_lock);
445}
446
447static void print_status(osm_opensm_t * p_osm, FILE * out)
448{
449	cl_list_item_t *item;
450
451	if (out) {
452		const char *re_str;
453
454		cl_plock_acquire(&p_osm->lock);
455		fprintf(out, "   OpenSM Version       : %s\n", p_osm->osm_version);
456		fprintf(out, "   SM State             : %s\n",
457			sm_state_str(p_osm->subn.sm_state));
458		fprintf(out, "   SM Priority          : %d\n",
459			p_osm->subn.opt.sm_priority);
460		fprintf(out, "   SA State             : %s\n",
461			sa_state_str(p_osm->sa.state));
462
463		re_str = p_osm->routing_engine_used ?
464			osm_routing_engine_type_str(p_osm->routing_engine_used->type) :
465			osm_routing_engine_type_str(OSM_ROUTING_ENGINE_TYPE_NONE);
466		fprintf(out, "   Routing Engine       : %s\n", re_str);
467
468		fprintf(out, "   Loaded event plugins :");
469		if (cl_qlist_head(&p_osm->plugin_list) ==
470			cl_qlist_end(&p_osm->plugin_list)) {
471			fprintf(out, " <none>");
472		}
473		for (item = cl_qlist_head(&p_osm->plugin_list);
474		     item != cl_qlist_end(&p_osm->plugin_list);
475		     item = cl_qlist_next(item))
476			fprintf(out, " %s",
477				((osm_epi_plugin_t *)item)->plugin_name);
478		fprintf(out, "\n");
479
480#ifdef ENABLE_OSM_PERF_MGR
481		fprintf(out, "\n   PerfMgr state/sweep state : %s/%s\n",
482			osm_perfmgr_get_state_str(&p_osm->perfmgr),
483			osm_perfmgr_get_sweep_state_str(&p_osm->perfmgr));
484#endif
485		fprintf(out, "\n   MAD stats\n"
486			"   ---------\n"
487			"   QP0 MADs outstanding           : %u\n"
488			"   QP0 MADs outstanding (on wire) : %u\n"
489			"   QP0 MADs rcvd                  : %u\n"
490			"   QP0 MADs sent                  : %u\n"
491			"   QP0 unicasts sent              : %u\n"
492			"   QP0 unknown MADs rcvd          : %u\n"
493			"   SA MADs outstanding            : %u\n"
494			"   SA MADs rcvd                   : %u\n"
495			"   SA MADs sent                   : %u\n"
496			"   SA unknown MADs rcvd           : %u\n"
497			"   SA MADs ignored                : %u\n",
498			(uint32_t)p_osm->stats.qp0_mads_outstanding,
499			(uint32_t)p_osm->stats.qp0_mads_outstanding_on_wire,
500			(uint32_t)p_osm->stats.qp0_mads_rcvd,
501			(uint32_t)p_osm->stats.qp0_mads_sent,
502			(uint32_t)p_osm->stats.qp0_unicasts_sent,
503			(uint32_t)p_osm->stats.qp0_mads_rcvd_unknown,
504			(uint32_t)p_osm->stats.sa_mads_outstanding,
505			(uint32_t)p_osm->stats.sa_mads_rcvd,
506			(uint32_t)p_osm->stats.sa_mads_sent,
507			(uint32_t)p_osm->stats.sa_mads_rcvd_unknown,
508			(uint32_t)p_osm->stats.sa_mads_ignored);
509		fprintf(out, "\n   Subnet flags\n"
510			"   ------------\n"
511			"   Sweeping enabled               : %d\n"
512			"   Sweep interval (seconds)       : %u\n"
513			"   Ignore existing lfts           : %d\n"
514			"   Subnet Init errors             : %d\n"
515			"   In sweep hop 0                 : %d\n"
516			"   First time master sweep        : %d\n"
517			"   Coming out of standby          : %d\n",
518			p_osm->subn.sweeping_enabled,
519			p_osm->subn.opt.sweep_interval,
520			p_osm->subn.ignore_existing_lfts,
521			p_osm->subn.subnet_initialization_error,
522			p_osm->subn.in_sweep_hop_0,
523			p_osm->subn.first_time_master_sweep,
524			p_osm->subn.coming_out_of_standby);
525		dump_sms(p_osm, out);
526		fprintf(out, "\n");
527		cl_plock_release(&p_osm->lock);
528	}
529}
530
531static int loop_command_check_time(void)
532{
533	time_t cur = time(NULL);
534	if ((loop_command.previous + loop_command.delay_s) < cur) {
535		loop_command.previous = cur;
536		return 1;
537	}
538	return 0;
539}
540
541static void status_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
542{
543	char *p_cmd;
544
545	p_cmd = next_token(p_last);
546	if (p_cmd) {
547		if (strcmp(p_cmd, "loop") == 0) {
548			fprintf(out, "Looping on status command...\n");
549			fflush(out);
550			loop_command.on = 1;
551			loop_command.previous = time(NULL);
552			loop_command.loop_function = print_status;
553		} else {
554			help_status(out, 1);
555			return;
556		}
557	}
558	print_status(p_osm, out);
559}
560
561static void resweep_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
562{
563	char *p_cmd;
564
565	p_cmd = next_token(p_last);
566	if (!p_cmd ||
567	    (strcmp(p_cmd, "heavy") != 0 && strcmp(p_cmd, "light") != 0)) {
568		fprintf(out, "Invalid resweep command\n");
569		help_resweep(out, 1);
570	} else {
571		if (strcmp(p_cmd, "heavy") == 0)
572			p_osm->subn.force_heavy_sweep = TRUE;
573		osm_opensm_sweep(p_osm);
574	}
575}
576
577static void reroute_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
578{
579	p_osm->subn.force_reroute = TRUE;
580	osm_opensm_sweep(p_osm);
581}
582
583static void sweep_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
584{
585	char *p_cmd;
586
587	p_cmd = next_token(p_last);
588	if (!p_cmd ||
589	    (strcmp(p_cmd, "on") != 0 && strcmp(p_cmd, "off") != 0)) {
590		fprintf(out, "Invalid sweep command\n");
591		help_sweep(out, 1);
592	} else {
593		if (strcmp(p_cmd, "on") == 0)
594			p_osm->subn.sweeping_enabled = TRUE;
595		else
596			p_osm->subn.sweeping_enabled = FALSE;
597	}
598}
599
600static void logflush_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
601{
602	char *p_cmd;
603
604	p_cmd = next_token(p_last);
605	if (!p_cmd ||
606	    (strcmp(p_cmd, "on") != 0 && strcmp(p_cmd, "off") != 0)) {
607		fprintf(out, "Invalid logflush command\n");
608		help_sweep(out, 1);
609	} else {
610		if (strcmp(p_cmd, "on") == 0) {
611			p_osm->log.flush = TRUE;
612	                fflush(p_osm->log.out_port);
613		} else
614			p_osm->log.flush = FALSE;
615	}
616}
617
618static void querylid_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
619{
620	unsigned int p = 0;
621	uint16_t lid = 0;
622	osm_port_t *p_port = NULL;
623	char *p_cmd = next_token(p_last);
624
625	if (!p_cmd) {
626		fprintf(out, "no LID specified\n");
627		help_querylid(out, 1);
628		return;
629	}
630
631	lid = (uint16_t) strtoul(p_cmd, NULL, 0);
632	cl_plock_acquire(&p_osm->lock);
633	p_port = osm_get_port_by_lid_ho(&p_osm->subn, lid);
634	if (!p_port)
635		goto invalid_lid;
636
637	fprintf(out, "Query results for LID %u\n", lid);
638	fprintf(out,
639		"   GUID                : 0x%016" PRIx64 "\n"
640		"   Node Desc           : %s\n"
641		"   Node Type           : %s\n"
642		"   Num Ports           : %d\n",
643		cl_ntoh64(p_port->guid),
644		p_port->p_node->print_desc,
645		ib_get_node_type_str(osm_node_get_type(p_port->p_node)),
646		p_port->p_node->node_info.num_ports);
647
648	if (p_port->p_node->sw)
649		p = 0;
650	else
651		p = 1;
652	for ( /* see above */ ; p < p_port->p_node->physp_tbl_size; p++) {
653		fprintf(out,
654			"   Port %u health       : %s\n",
655			p,
656			p_port->p_node->physp_table[p].
657			healthy ? "OK" : "ERROR");
658	}
659
660	cl_plock_release(&p_osm->lock);
661	return;
662
663invalid_lid:
664	cl_plock_release(&p_osm->lock);
665	fprintf(out, "Invalid lid %d\n", lid);
666	return;
667}
668
669/**
670 * Data structures for the portstatus command
671 */
672typedef struct _port_report {
673	struct _port_report *next;
674	uint64_t node_guid;
675	uint8_t port_num;
676	char print_desc[IB_NODE_DESCRIPTION_SIZE + 1];
677} port_report_t;
678
679static void
680__tag_port_report(port_report_t ** head, uint64_t node_guid,
681		  uint8_t port_num, char *print_desc)
682{
683	port_report_t *rep = malloc(sizeof(*rep));
684	if (!rep)
685		return;
686
687	rep->node_guid = node_guid;
688	rep->port_num = port_num;
689	memcpy(rep->print_desc, print_desc, IB_NODE_DESCRIPTION_SIZE + 1);
690	rep->next = NULL;
691	if (*head) {
692		rep->next = *head;
693		*head = rep;
694	} else
695		*head = rep;
696}
697
698static void __print_port_report(FILE * out, port_report_t * head)
699{
700	port_report_t *item = head;
701	while (item != NULL) {
702		fprintf(out, "      0x%016" PRIx64 " %d (%s)\n",
703			item->node_guid, item->port_num, item->print_desc);
704		port_report_t *next = item->next;
705		free(item);
706		item = next;
707	}
708}
709
710typedef struct {
711	uint8_t node_type_lim;	/* limit the results; 0 == ALL */
712	uint64_t total_nodes;
713	uint64_t total_ports;
714	uint64_t ports_down;
715	uint64_t ports_active;
716	uint64_t ports_disabled;
717	port_report_t *disabled_ports;
718	uint64_t ports_1X;
719	uint64_t ports_4X;
720	uint64_t ports_8X;
721	uint64_t ports_12X;
722	uint64_t ports_2X;
723	uint64_t ports_unknown_width;
724	port_report_t *unknown_width_ports;
725	uint64_t ports_unenabled_width;
726	port_report_t *unenabled_width_ports;
727	uint64_t ports_reduced_width;
728	port_report_t *reduced_width_ports;
729	uint64_t ports_sdr;
730	uint64_t ports_ddr;
731	uint64_t ports_qdr;
732	uint64_t ports_fdr10;
733	uint64_t ports_fdr;
734	uint64_t ports_edr;
735	uint64_t ports_unknown_speed;
736	port_report_t *unknown_speed_ports;
737	uint64_t ports_unenabled_speed;
738	port_report_t *unenabled_speed_ports;
739	uint64_t ports_reduced_speed;
740	port_report_t *reduced_speed_ports;
741} fabric_stats_t;
742
743/**
744 * iterator function to get portstatus on each node
745 */
746static void __get_stats(cl_map_item_t * const p_map_item, void *context)
747{
748	fabric_stats_t *fs = (fabric_stats_t *) context;
749	osm_node_t *node = (osm_node_t *) p_map_item;
750	osm_physp_t *physp0;
751	ib_port_info_t *pi0;
752	uint8_t num_ports = osm_node_get_num_physp(node);
753	uint8_t port = 0;
754
755	/* Skip nodes we are not interested in */
756	if (fs->node_type_lim != 0
757	    && fs->node_type_lim != node->node_info.node_type)
758		return;
759
760	fs->total_nodes++;
761
762	if (osm_node_get_type(node) == IB_NODE_TYPE_SWITCH) {
763		physp0 = osm_node_get_physp_ptr(node, 0);
764		pi0 = &physp0->port_info;
765	} else
766		pi0 = NULL;
767
768	for (port = 1; port < num_ports; port++) {
769		osm_physp_t *phys = osm_node_get_physp_ptr(node, port);
770		ib_port_info_t *pi = NULL;
771		ib_mlnx_ext_port_info_t *epi = NULL;
772		uint8_t active_speed = 0;
773		uint8_t enabled_speed = 0;
774		uint8_t active_width = 0;
775		uint8_t enabled_width = 0;
776		uint8_t port_state = 0;
777		uint8_t port_phys_state = 0;
778
779		if (!phys)
780			continue;
781
782		pi = &phys->port_info;
783		epi = &phys->ext_port_info;
784		if (!pi0)
785			pi0 = pi;
786		active_speed = ib_port_info_get_link_speed_active(pi);
787		enabled_speed = ib_port_info_get_link_speed_enabled(pi);
788		active_width = pi->link_width_active;
789		enabled_width = pi->link_width_enabled;
790		port_state = ib_port_info_get_port_state(pi);
791		port_phys_state = ib_port_info_get_port_phys_state(pi);
792
793		if (port_state == IB_LINK_DOWN)
794			fs->ports_down++;
795		else if (port_state == IB_LINK_ACTIVE)
796			fs->ports_active++;
797		if (port_phys_state == IB_PORT_PHYS_STATE_DISABLED) {
798			__tag_port_report(&(fs->disabled_ports),
799					  cl_ntoh64(node->node_info.node_guid),
800					  port, node->print_desc);
801			fs->ports_disabled++;
802		}
803
804		fs->total_ports++;
805
806		if (port_state == IB_LINK_DOWN)
807			continue;
808
809		if (!(active_width & enabled_width)) {
810			__tag_port_report(&(fs->unenabled_width_ports),
811					  cl_ntoh64(node->node_info.node_guid),
812					  port, node->print_desc);
813			fs->ports_unenabled_width++;
814		}
815		else if ((enabled_width ^ active_width) > active_width) {
816			__tag_port_report(&(fs->reduced_width_ports),
817					  cl_ntoh64(node->node_info.node_guid),
818					  port, node->print_desc);
819			fs->ports_reduced_width++;
820		}
821
822		/* unenabled speed usually due to problems with force_link_speed */
823		if (!(active_speed & enabled_speed)) {
824			__tag_port_report(&(fs->unenabled_speed_ports),
825					  cl_ntoh64(node->node_info.node_guid),
826					  port, node->print_desc);
827			fs->ports_unenabled_speed++;
828		}
829		else if ((enabled_speed ^ active_speed) > active_speed) {
830			__tag_port_report(&(fs->reduced_speed_ports),
831					  cl_ntoh64(node->node_info.node_guid),
832					  port, node->print_desc);
833			fs->ports_reduced_speed++;
834		}
835
836		switch (active_speed) {
837		case IB_LINK_SPEED_ACTIVE_2_5:
838			fs->ports_sdr++;
839			break;
840		case IB_LINK_SPEED_ACTIVE_5:
841			fs->ports_ddr++;
842			break;
843		case IB_LINK_SPEED_ACTIVE_10:
844			if (!(pi0->capability_mask & IB_PORT_CAP_HAS_EXT_SPEEDS) ||
845			    !ib_port_info_get_link_speed_ext_active(pi)) {
846				if (epi->link_speed_active & FDR10)
847					fs->ports_fdr10++;
848				else {
849					fs->ports_qdr++;
850					/* check for speed reduced from FDR10 */
851					if (epi->link_speed_enabled & FDR10) {
852						__tag_port_report(&(fs->reduced_speed_ports),
853								  cl_ntoh64(node->node_info.node_guid),
854								  port, node->print_desc);
855						fs->ports_reduced_speed++;
856					}
857				}
858			}
859			break;
860		case IB_LINK_SPEED_ACTIVE_EXTENDED:
861			break;
862		default:
863			__tag_port_report(&(fs->unknown_speed_ports),
864					  cl_ntoh64(node->node_info.node_guid),
865					  port, node->print_desc);
866			fs->ports_unknown_speed++;
867			break;
868		}
869		if (pi0->capability_mask & IB_PORT_CAP_HAS_EXT_SPEEDS &&
870		    ib_port_info_get_link_speed_ext_sup(pi) &&
871		    (enabled_speed = ib_port_info_get_link_speed_ext_enabled(pi)) != IB_LINK_SPEED_EXT_DISABLE &&
872		    active_speed == IB_LINK_SPEED_ACTIVE_10) {
873			active_speed = ib_port_info_get_link_speed_ext_active(pi);
874			if (!(active_speed & enabled_speed)) {
875				__tag_port_report(&(fs->unenabled_speed_ports),
876						  cl_ntoh64(node->node_info.node_guid),
877						  port, node->print_desc);
878				fs->ports_unenabled_speed++;
879			}
880			else if ((enabled_speed ^ active_speed) > active_speed) {
881				__tag_port_report(&(fs->reduced_speed_ports),
882						  cl_ntoh64(node->node_info.node_guid),
883						  port, node->print_desc);
884				fs->ports_reduced_speed++;
885			}
886			switch (active_speed) {
887			case IB_LINK_SPEED_EXT_ACTIVE_14:
888				fs->ports_fdr++;
889				break;
890			case IB_LINK_SPEED_EXT_ACTIVE_25:
891				fs->ports_edr++;
892				break;
893			case IB_LINK_SPEED_EXT_ACTIVE_NONE:
894				break;
895			default:
896				__tag_port_report(&(fs->unknown_speed_ports),
897						  cl_ntoh64(node->node_info.node_guid),
898						  port, node->print_desc);
899				fs->ports_unknown_speed++;
900				break;
901			}
902		}
903		switch (active_width) {
904		case IB_LINK_WIDTH_ACTIVE_1X:
905			fs->ports_1X++;
906			break;
907		case IB_LINK_WIDTH_ACTIVE_4X:
908			fs->ports_4X++;
909			break;
910		case IB_LINK_WIDTH_ACTIVE_8X:
911			fs->ports_8X++;
912			break;
913		case IB_LINK_WIDTH_ACTIVE_12X:
914			fs->ports_12X++;
915			break;
916		case IB_LINK_WIDTH_ACTIVE_2X:
917			fs->ports_2X++;
918			break;
919		default:
920			__tag_port_report(&(fs->unknown_width_ports),
921					  cl_ntoh64(node->node_info.node_guid),
922					  port, node->print_desc);
923			fs->ports_unknown_width++;
924			break;
925		}
926	}
927}
928
929static void portstatus_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
930{
931	fabric_stats_t fs;
932	struct timeval before, after;
933	char *p_cmd;
934
935	memset(&fs, 0, sizeof(fs));
936
937	p_cmd = next_token(p_last);
938	if (p_cmd) {
939		if (strcmp(p_cmd, "ca") == 0) {
940			fs.node_type_lim = IB_NODE_TYPE_CA;
941		} else if (strcmp(p_cmd, "switch") == 0) {
942			fs.node_type_lim = IB_NODE_TYPE_SWITCH;
943		} else if (strcmp(p_cmd, "router") == 0) {
944			fs.node_type_lim = IB_NODE_TYPE_ROUTER;
945		} else {
946			fprintf(out, "Node type not understood\n");
947			help_portstatus(out, 1);
948			return;
949		}
950	}
951
952	gettimeofday(&before, NULL);
953
954	/* for each node in the system gather the stats */
955	cl_plock_acquire(&p_osm->lock);
956	cl_qmap_apply_func(&(p_osm->subn.node_guid_tbl), __get_stats,
957			   (void *)&fs);
958	cl_plock_release(&p_osm->lock);
959
960	gettimeofday(&after, NULL);
961
962	/* report the stats */
963	fprintf(out, "\"%s\" port status:\n",
964		fs.node_type_lim ? ib_get_node_type_str(fs.
965							node_type_lim) : "ALL");
966	fprintf(out,
967		"   %" PRIu64 " port(s) scanned on %" PRIu64
968		" nodes in %lu us\n", fs.total_ports, fs.total_nodes,
969		after.tv_usec - before.tv_usec);
970
971	if (fs.ports_down)
972		fprintf(out, "   %" PRIu64 " down\n", fs.ports_down);
973	if (fs.ports_active)
974		fprintf(out, "   %" PRIu64 " active\n", fs.ports_active);
975	if (fs.ports_1X)
976		fprintf(out, "   %" PRIu64 " at 1X\n", fs.ports_1X);
977	if (fs.ports_4X)
978		fprintf(out, "   %" PRIu64 " at 4X\n", fs.ports_4X);
979	if (fs.ports_8X)
980		fprintf(out, "   %" PRIu64 " at 8X\n", fs.ports_8X);
981	if (fs.ports_12X)
982		fprintf(out, "   %" PRIu64 " at 12X\n", fs.ports_12X);
983
984	if (fs.ports_sdr)
985		fprintf(out, "   %" PRIu64 " at 2.5 Gbps\n", fs.ports_sdr);
986	if (fs.ports_ddr)
987		fprintf(out, "   %" PRIu64 " at 5.0 Gbps\n", fs.ports_ddr);
988	if (fs.ports_qdr)
989		fprintf(out, "   %" PRIu64 " at 10.0 Gbps\n", fs.ports_qdr);
990	if (fs.ports_fdr10)
991		fprintf(out, "   %" PRIu64 " at 10.0 Gbps (FDR10)\n", fs.ports_fdr10);
992	if (fs.ports_fdr)
993		fprintf(out, "   %" PRIu64 " at 14.0625 Gbps\n", fs.ports_fdr);
994	if (fs.ports_edr)
995		fprintf(out, "   %" PRIu64 " at 25.78125 Gbps\n", fs.ports_edr);
996
997	if (fs.ports_disabled + fs.ports_reduced_speed + fs.ports_reduced_width
998	    + fs.ports_unenabled_width + fs.ports_unenabled_speed
999	    + fs.ports_unknown_width + fs.ports_unknown_speed > 0) {
1000		fprintf(out, "\nPossible issues:\n");
1001	}
1002	if (fs.ports_disabled) {
1003		fprintf(out, "   %" PRIu64 " disabled\n", fs.ports_disabled);
1004		__print_port_report(out, fs.disabled_ports);
1005	}
1006	if (fs.ports_unenabled_speed) {
1007		fprintf(out, "   %" PRIu64 " with unenabled speed\n",
1008			fs.ports_unenabled_speed);
1009		__print_port_report(out, fs.unenabled_speed_ports);
1010	}
1011	if (fs.ports_reduced_speed) {
1012		fprintf(out, "   %" PRIu64 " with reduced speed\n",
1013			fs.ports_reduced_speed);
1014		__print_port_report(out, fs.reduced_speed_ports);
1015	}
1016	if (fs.ports_unknown_speed) {
1017		fprintf(out, "   %" PRIu64 " with unknown speed\n",
1018			fs.ports_unknown_speed);
1019		__print_port_report(out, fs.unknown_speed_ports);
1020	}
1021	if (fs.ports_unenabled_width) {
1022		fprintf(out, "   %" PRIu64 " with unenabled width\n",
1023			fs.ports_unenabled_width);
1024		__print_port_report(out, fs.unenabled_width_ports);
1025	}
1026	if (fs.ports_reduced_width) {
1027		fprintf(out, "   %" PRIu64 " with reduced width\n",
1028			fs.ports_reduced_width);
1029		__print_port_report(out, fs.reduced_width_ports);
1030	}
1031	if (fs.ports_unknown_width) {
1032		fprintf(out, "   %" PRIu64 " with unknown width\n",
1033			fs.ports_unknown_width);
1034		__print_port_report(out, fs.unknown_width_ports);
1035	}
1036	fprintf(out, "\n");
1037}
1038
1039static void switchbalance_check(osm_opensm_t * p_osm,
1040				osm_switch_t * p_sw, FILE * out, int verbose)
1041{
1042	uint8_t port_num;
1043	uint8_t num_ports;
1044	const cl_qmap_t *p_port_tbl;
1045	osm_port_t *p_port;
1046	osm_physp_t *p_physp;
1047	osm_physp_t *p_rem_physp;
1048	osm_node_t *p_rem_node;
1049	uint32_t count[255];	/* max ports is a uint8_t */
1050	uint8_t output_ports[255];
1051	uint8_t output_ports_count = 0;
1052	uint32_t min_count = 0xFFFFFFFF;
1053	uint32_t max_count = 0;
1054	unsigned int i;
1055
1056	memset(count, '\0', sizeof(uint32_t) * 255);
1057
1058	/* Count port usage */
1059	p_port_tbl = &p_osm->subn.port_guid_tbl;
1060	for (p_port = (osm_port_t *) cl_qmap_head(p_port_tbl);
1061	     p_port != (osm_port_t *) cl_qmap_end(p_port_tbl);
1062	     p_port = (osm_port_t *) cl_qmap_next(&p_port->map_item)) {
1063		uint16_t min_lid_ho;
1064		uint16_t max_lid_ho;
1065		uint16_t lid_ho;
1066
1067		/* Don't count switches in port usage */
1068		if (osm_node_get_type(p_port->p_node) == IB_NODE_TYPE_SWITCH)
1069			continue;
1070
1071		osm_port_get_lid_range_ho(p_port, &min_lid_ho, &max_lid_ho);
1072
1073		if (min_lid_ho == 0 || max_lid_ho == 0)
1074			continue;
1075
1076		for (lid_ho = min_lid_ho; lid_ho <= max_lid_ho; lid_ho++) {
1077			port_num = osm_switch_get_port_by_lid(p_sw, lid_ho,
1078							      OSM_NEW_LFT);
1079			if (port_num == OSM_NO_PATH)
1080				continue;
1081
1082			count[port_num]++;
1083		}
1084	}
1085
1086	num_ports = p_sw->num_ports;
1087	for (port_num = 1; port_num < num_ports; port_num++) {
1088		p_physp = osm_node_get_physp_ptr(p_sw->p_node, port_num);
1089
1090		/* if port is down/unhealthy, don't consider it in
1091		 * min/max calculations
1092		 */
1093		if (!p_physp || !osm_physp_is_healthy(p_physp)
1094		    || !osm_physp_get_remote(p_physp))
1095			continue;
1096
1097		p_rem_physp = osm_physp_get_remote(p_physp);
1098		p_rem_node = osm_physp_get_node_ptr(p_rem_physp);
1099
1100		/* If we are directly connected to a CA/router, its not really
1101		 * up for balancing consideration.
1102		 */
1103		if (osm_node_get_type(p_rem_node) != IB_NODE_TYPE_SWITCH)
1104			continue;
1105
1106		output_ports[output_ports_count] = port_num;
1107		output_ports_count++;
1108
1109		if (count[port_num] < min_count)
1110			min_count = count[port_num];
1111		if (count[port_num] > max_count)
1112			max_count = count[port_num];
1113	}
1114
1115	if (verbose || ((max_count - min_count) > 1)) {
1116		if ((max_count - min_count) > 1)
1117			fprintf(out,
1118				"Unbalanced Switch: 0x%016" PRIx64 " (%s)\n",
1119				cl_ntoh64(p_sw->p_node->node_info.node_guid),
1120				p_sw->p_node->print_desc);
1121		else
1122			fprintf(out,
1123				"Switch: 0x%016" PRIx64 " (%s)\n",
1124				cl_ntoh64(p_sw->p_node->node_info.node_guid),
1125				p_sw->p_node->print_desc);
1126
1127		for (i = 0; i < output_ports_count; i++) {
1128			fprintf(out,
1129				"Port %d: %d\n",
1130				output_ports[i], count[output_ports[i]]);
1131		}
1132	}
1133}
1134
1135static void switchbalance_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
1136{
1137	char *p_cmd;
1138	uint64_t guid = 0;
1139	osm_switch_t *p_sw;
1140	int verbose = 0;
1141
1142	p_cmd = next_token(p_last);
1143	if (p_cmd) {
1144		char *p_end;
1145
1146		if (strcmp(p_cmd, "verbose") == 0) {
1147			verbose++;
1148			p_cmd = next_token(p_last);
1149		}
1150
1151		if (p_cmd) {
1152			guid = strtoull(p_cmd, &p_end, 0);
1153			if (!guid || *p_end != '\0') {
1154				fprintf(out, "Invalid guid specified\n");
1155				help_switchbalance(out, 1);
1156				return;
1157			}
1158		}
1159	}
1160
1161	cl_plock_acquire(&p_osm->lock);
1162	if (guid) {
1163		p_sw = osm_get_switch_by_guid(&p_osm->subn, cl_hton64(guid));
1164		if (!p_sw) {
1165			fprintf(out, "guid not found\n");
1166			goto lock_exit;
1167		}
1168
1169		switchbalance_check(p_osm, p_sw, out, verbose);
1170	} else {
1171		cl_qmap_t *p_sw_guid_tbl = &p_osm->subn.sw_guid_tbl;
1172		for (p_sw = (osm_switch_t *) cl_qmap_head(p_sw_guid_tbl);
1173		     p_sw != (osm_switch_t *) cl_qmap_end(p_sw_guid_tbl);
1174		     p_sw = (osm_switch_t *) cl_qmap_next(&p_sw->map_item))
1175			switchbalance_check(p_osm, p_sw, out, verbose);
1176	}
1177lock_exit:
1178	cl_plock_release(&p_osm->lock);
1179	return;
1180}
1181
1182static void lidbalance_check(osm_opensm_t * p_osm,
1183			     osm_switch_t * p_sw, FILE * out)
1184{
1185	uint8_t port_num;
1186	const cl_qmap_t *p_port_tbl;
1187	osm_port_t *p_port;
1188
1189	p_port_tbl = &p_osm->subn.port_guid_tbl;
1190	for (p_port = (osm_port_t *) cl_qmap_head(p_port_tbl);
1191	     p_port != (osm_port_t *) cl_qmap_end(p_port_tbl);
1192	     p_port = (osm_port_t *) cl_qmap_next(&p_port->map_item)) {
1193		uint32_t port_count[255];	/* max ports is a uint8_t */
1194		osm_node_t *rem_node[255];
1195		uint32_t rem_node_count;
1196		uint32_t rem_count[255];
1197		osm_physp_t *p_physp;
1198		osm_physp_t *p_rem_physp;
1199		osm_node_t *p_rem_node;
1200		uint32_t port_min_count = 0xFFFFFFFF;
1201		uint32_t port_max_count = 0;
1202		uint32_t rem_min_count = 0xFFFFFFFF;
1203		uint32_t rem_max_count = 0;
1204		uint16_t min_lid_ho;
1205		uint16_t max_lid_ho;
1206		uint16_t lid_ho;
1207		uint8_t num_ports;
1208		unsigned int i;
1209
1210		/* we only care about non-switches */
1211		if (osm_node_get_type(p_port->p_node) == IB_NODE_TYPE_SWITCH)
1212			continue;
1213
1214		osm_port_get_lid_range_ho(p_port, &min_lid_ho, &max_lid_ho);
1215
1216		if (min_lid_ho == 0 || max_lid_ho == 0)
1217			continue;
1218
1219		memset(port_count, '\0', sizeof(uint32_t) * 255);
1220		memset(rem_node, '\0', sizeof(osm_node_t *) * 255);
1221		rem_node_count = 0;
1222		memset(rem_count, '\0', sizeof(uint32_t) * 255);
1223
1224		for (lid_ho = min_lid_ho; lid_ho <= max_lid_ho; lid_ho++) {
1225			boolean_t rem_node_found = FALSE;
1226			unsigned int indx = 0;
1227
1228			port_num = osm_switch_get_port_by_lid(p_sw, lid_ho,
1229							      OSM_NEW_LFT);
1230			if (port_num == OSM_NO_PATH)
1231				continue;
1232
1233			p_physp =
1234			    osm_node_get_physp_ptr(p_sw->p_node, port_num);
1235
1236			/* if port is down/unhealthy, can't calculate */
1237			if (!p_physp || !osm_physp_is_healthy(p_physp)
1238			    || !osm_physp_get_remote(p_physp))
1239				continue;
1240
1241			p_rem_physp = osm_physp_get_remote(p_physp);
1242			p_rem_node = osm_physp_get_node_ptr(p_rem_physp);
1243
1244			/* determine if we've seen this remote node before.
1245			 * If not, store it.  If yes, update the counter
1246			 */
1247			for (i = 0; i < rem_node_count; i++) {
1248				if (rem_node[i] == p_rem_node) {
1249					rem_node_found = TRUE;
1250					indx = i;
1251					break;
1252				}
1253			}
1254
1255			if (!rem_node_found) {
1256				rem_node[rem_node_count] = p_rem_node;
1257				rem_count[rem_node_count]++;
1258				indx = rem_node_count;
1259				rem_node_count++;
1260			} else
1261				rem_count[indx]++;
1262
1263			port_count[port_num]++;
1264		}
1265
1266		if (!rem_node_count)
1267			continue;
1268
1269		for (i = 0; i < rem_node_count; i++) {
1270			if (rem_count[i] < rem_min_count)
1271				rem_min_count = rem_count[i];
1272			if (rem_count[i] > rem_max_count)
1273				rem_max_count = rem_count[i];
1274		}
1275
1276		num_ports = p_sw->num_ports;
1277		for (i = 0; i < num_ports; i++) {
1278			if (!port_count[i])
1279				continue;
1280			if (port_count[i] < port_min_count)
1281				port_min_count = port_count[i];
1282			if (port_count[i] > port_max_count)
1283				port_max_count = port_count[i];
1284		}
1285
1286		/* Output if this CA/router is being forwarded an unbalanced number of
1287		 * times to a destination.
1288		 */
1289		if ((rem_max_count - rem_min_count) > 1) {
1290			fprintf(out,
1291				"Unbalanced Remote Forwarding: Switch 0x%016"
1292				PRIx64 " (%s): ",
1293				cl_ntoh64(p_sw->p_node->node_info.node_guid),
1294				p_sw->p_node->print_desc);
1295			if (osm_node_get_type(p_port->p_node) ==
1296			    IB_NODE_TYPE_CA)
1297				fprintf(out, "CA");
1298			else if (osm_node_get_type(p_port->p_node) ==
1299				 IB_NODE_TYPE_ROUTER)
1300				fprintf(out, "Router");
1301			fprintf(out, " 0x%016" PRIx64 " (%s): ",
1302				cl_ntoh64(p_port->p_node->node_info.node_guid),
1303				p_port->p_node->print_desc);
1304			for (i = 0; i < rem_node_count; i++) {
1305				fprintf(out,
1306					"Dest 0x%016" PRIx64 "(%s) - %u ",
1307					cl_ntoh64(rem_node[i]->node_info.
1308						  node_guid),
1309					rem_node[i]->print_desc, rem_count[i]);
1310			}
1311			fprintf(out, "\n");
1312		}
1313
1314		/* Output if this CA/router is being forwarded through a port
1315		 * an unbalanced number of times.
1316		 */
1317		if ((port_max_count - port_min_count) > 1) {
1318			fprintf(out,
1319				"Unbalanced Port Forwarding: Switch 0x%016"
1320				PRIx64 " (%s): ",
1321				cl_ntoh64(p_sw->p_node->node_info.node_guid),
1322				p_sw->p_node->print_desc);
1323			if (osm_node_get_type(p_port->p_node) ==
1324			    IB_NODE_TYPE_CA)
1325				fprintf(out, "CA");
1326			else if (osm_node_get_type(p_port->p_node) ==
1327				 IB_NODE_TYPE_ROUTER)
1328				fprintf(out, "Router");
1329			fprintf(out, " 0x%016" PRIx64 " (%s): ",
1330				cl_ntoh64(p_port->p_node->node_info.node_guid),
1331				p_port->p_node->print_desc);
1332			for (i = 0; i < num_ports; i++) {
1333				if (!port_count[i])
1334					continue;
1335				fprintf(out, "Port %u - %u: ", i,
1336					port_count[i]);
1337			}
1338			fprintf(out, "\n");
1339		}
1340	}
1341}
1342
1343static void lidbalance_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
1344{
1345	char *p_cmd;
1346	uint64_t guid = 0;
1347	osm_switch_t *p_sw;
1348
1349	p_cmd = next_token(p_last);
1350	if (p_cmd) {
1351		char *p_end;
1352
1353		guid = strtoull(p_cmd, &p_end, 0);
1354		if (!guid || *p_end != '\0') {
1355			fprintf(out, "Invalid switchguid specified\n");
1356			help_lidbalance(out, 1);
1357			return;
1358		}
1359	}
1360
1361	cl_plock_acquire(&p_osm->lock);
1362	if (guid) {
1363		p_sw = osm_get_switch_by_guid(&p_osm->subn, cl_hton64(guid));
1364		if (!p_sw) {
1365			fprintf(out, "switchguid not found\n");
1366			goto lock_exit;
1367		}
1368		lidbalance_check(p_osm, p_sw, out);
1369	} else {
1370		cl_qmap_t *p_sw_guid_tbl = &p_osm->subn.sw_guid_tbl;
1371		for (p_sw = (osm_switch_t *) cl_qmap_head(p_sw_guid_tbl);
1372		     p_sw != (osm_switch_t *) cl_qmap_end(p_sw_guid_tbl);
1373		     p_sw = (osm_switch_t *) cl_qmap_next(&p_sw->map_item))
1374			lidbalance_check(p_osm, p_sw, out);
1375	}
1376
1377lock_exit:
1378	cl_plock_release(&p_osm->lock);
1379	return;
1380}
1381
1382static void dump_conf_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
1383{
1384	osm_subn_output_conf(out, &p_osm->subn.opt);
1385}
1386
1387static void update_desc_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
1388{
1389	osm_update_node_desc(p_osm);
1390}
1391
1392#ifdef ENABLE_OSM_PERF_MGR
1393static monitored_node_t *find_node_by_name(osm_opensm_t * p_osm,
1394					   char *nodename)
1395{
1396	cl_map_item_t *item;
1397	monitored_node_t *node;
1398
1399	item = cl_qmap_head(&p_osm->perfmgr.monitored_map);
1400	while (item != cl_qmap_end(&p_osm->perfmgr.monitored_map)) {
1401		node = (monitored_node_t *)item;
1402		if (strcmp(node->name, nodename) == 0)
1403			return node;
1404		item = cl_qmap_next(item);
1405	}
1406
1407	return NULL;
1408}
1409
1410static monitored_node_t *find_node_by_guid(osm_opensm_t * p_osm,
1411					   uint64_t guid)
1412{
1413	cl_map_item_t *node;
1414
1415	node = cl_qmap_get(&p_osm->perfmgr.monitored_map, guid);
1416	if (node != cl_qmap_end(&p_osm->perfmgr.monitored_map))
1417		return (monitored_node_t *)node;
1418
1419	return NULL;
1420}
1421
1422static void dump_redir_entry(monitored_node_t *p_mon_node, FILE * out)
1423{
1424	int port, redir;
1425
1426	/* only display monitored nodes with redirection info */
1427	redir = 0;
1428	for (port = (p_mon_node->esp0) ? 0 : 1;
1429	     port < p_mon_node->num_ports; port++) {
1430		if (p_mon_node->port[port].redirection) {
1431			if (!redir) {
1432				fprintf(out, "   Node GUID       ESP0   Name\n");
1433				fprintf(out, "   ---------       ----   ----\n");
1434				fprintf(out, "   0x%" PRIx64 " %d      %s\n",
1435					p_mon_node->guid, p_mon_node->esp0,
1436					p_mon_node->name);
1437				fprintf(out, "\n   Port Valid  LIDs     PKey  QP    PKey Index\n");
1438				fprintf(out, "   ---- -----  ----     ----  --    ----------\n");
1439				redir = 1;
1440			}
1441			fprintf(out, "   %d    %d      %u->%u  0x%x 0x%x   %d\n",
1442				port, p_mon_node->port[port].valid,
1443				cl_ntoh16(p_mon_node->port[port].orig_lid),
1444				cl_ntoh16(p_mon_node->port[port].lid),
1445				cl_ntoh16(p_mon_node->port[port].pkey),
1446				cl_ntoh32(p_mon_node->port[port].qp),
1447				p_mon_node->port[port].pkey_ix);
1448		}
1449	}
1450	if (redir)
1451		fprintf(out, "\n");
1452}
1453
1454static void dump_redir(osm_opensm_t * p_osm, char *nodename, FILE * out)
1455{
1456	monitored_node_t *p_mon_node;
1457	uint64_t guid;
1458
1459	if (!p_osm->subn.opt.perfmgr_redir)
1460		fprintf(out, "Perfmgr redirection not enabled\n");
1461
1462	fprintf(out, "\nRedirection Table\n");
1463	fprintf(out, "-----------------\n");
1464	cl_plock_acquire(&p_osm->lock);
1465	if (nodename) {
1466		guid = strtoull(nodename, NULL, 0);
1467		if (guid == 0 && errno)
1468			p_mon_node = find_node_by_name(p_osm, nodename);
1469		else
1470			p_mon_node = find_node_by_guid(p_osm, guid);
1471		if (p_mon_node)
1472			dump_redir_entry(p_mon_node, out);
1473		else {
1474			if (guid == 0 && errno)
1475				fprintf(out, "Node %s not found...\n", nodename);
1476			else
1477				fprintf(out, "Node 0x%" PRIx64 " not found...\n", guid);
1478		}
1479	} else {
1480		p_mon_node = (monitored_node_t *) cl_qmap_head(&p_osm->perfmgr.monitored_map);
1481		while (p_mon_node != (monitored_node_t *) cl_qmap_end(&p_osm->perfmgr.monitored_map)) {
1482			dump_redir_entry(p_mon_node, out);
1483			p_mon_node = (monitored_node_t *) cl_qmap_next((const cl_map_item_t *)p_mon_node);
1484		}
1485	}
1486	cl_plock_release(&p_osm->lock);
1487}
1488
1489static void clear_redir_entry(monitored_node_t *p_mon_node)
1490{
1491	int port;
1492	ib_net16_t orig_lid;
1493
1494	for (port = (p_mon_node->esp0) ? 0 : 1;
1495	     port < p_mon_node->num_ports; port++) {
1496		if (p_mon_node->port[port].redirection) {
1497			orig_lid = p_mon_node->port[port].orig_lid;
1498			memset(&p_mon_node->port[port], 0,
1499			       sizeof(monitored_port_t));
1500			p_mon_node->port[port].valid = TRUE;
1501			p_mon_node->port[port].orig_lid = orig_lid;
1502		}
1503	}
1504}
1505
1506static void clear_redir(osm_opensm_t * p_osm, char *nodename, FILE * out)
1507{
1508	monitored_node_t *p_mon_node;
1509	uint64_t guid;
1510
1511	if (!p_osm->subn.opt.perfmgr_redir)
1512		fprintf(out, "Perfmgr redirection not enabled\n");
1513
1514	cl_plock_acquire(&p_osm->lock);
1515	if (nodename) {
1516		guid = strtoull(nodename, NULL, 0);
1517		if (guid == 0 && errno)
1518			p_mon_node = find_node_by_name(p_osm, nodename);
1519		else
1520			p_mon_node = find_node_by_guid(p_osm, guid);
1521		if (p_mon_node)
1522			clear_redir_entry(p_mon_node);
1523		else {
1524			if (guid == 0 && errno)
1525				fprintf(out, "Node %s not found...\n", nodename);
1526			else
1527				fprintf(out, "Node 0x%" PRIx64 " not found...\n", guid);
1528		}
1529	} else {
1530		p_mon_node = (monitored_node_t *) cl_qmap_head(&p_osm->perfmgr.monitored_map);
1531		while (p_mon_node != (monitored_node_t *) cl_qmap_end(&p_osm->perfmgr.monitored_map)) {
1532			clear_redir_entry(p_mon_node);
1533			p_mon_node = (monitored_node_t *) cl_qmap_next((const cl_map_item_t *)p_mon_node);
1534		}
1535	}
1536	cl_plock_release(&p_osm->lock);
1537}
1538
1539static void perfmgr_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
1540{
1541	char *p_cmd;
1542
1543	p_cmd = next_token(p_last);
1544	if (p_cmd) {
1545		if (strcmp(p_cmd, "enable") == 0) {
1546			osm_perfmgr_set_state(&p_osm->perfmgr,
1547					      PERFMGR_STATE_ENABLED);
1548		} else if (strcmp(p_cmd, "disable") == 0) {
1549			osm_perfmgr_set_state(&p_osm->perfmgr,
1550					      PERFMGR_STATE_DISABLE);
1551		} else if (strcmp(p_cmd, "clear_counters") == 0) {
1552			osm_perfmgr_clear_counters(&p_osm->perfmgr);
1553		} else if (strcmp(p_cmd, "set_rm_nodes") == 0) {
1554			osm_perfmgr_set_rm_nodes(&p_osm->perfmgr, 1);
1555		} else if (strcmp(p_cmd, "clear_rm_nodes") == 0) {
1556			osm_perfmgr_set_rm_nodes(&p_osm->perfmgr, 0);
1557		} else if (strcmp(p_cmd, "set_query_cpi") == 0) {
1558			osm_perfmgr_set_query_cpi(&p_osm->perfmgr, 1);
1559		} else if (strcmp(p_cmd, "clear_query_cpi") == 0) {
1560			osm_perfmgr_set_query_cpi(&p_osm->perfmgr, 0);
1561		} else if (strcmp(p_cmd, "dump_counters") == 0) {
1562			p_cmd = next_token(p_last);
1563			if (p_cmd && (strcmp(p_cmd, "mach") == 0)) {
1564				osm_perfmgr_dump_counters(&p_osm->perfmgr,
1565							  PERFMGR_EVENT_DB_DUMP_MR);
1566			} else {
1567				osm_perfmgr_dump_counters(&p_osm->perfmgr,
1568							  PERFMGR_EVENT_DB_DUMP_HR);
1569			}
1570		} else if (strcmp(p_cmd, "clear_inactive") == 0) {
1571			unsigned cnt = osm_perfmgr_delete_inactive(&p_osm->perfmgr);
1572			fprintf(out, "Removed %u nodes from Database\n", cnt);
1573		} else if (strcmp(p_cmd, "print_counters") == 0 ||
1574			   strcmp(p_cmd, "pc") == 0) {
1575			char *port = NULL;
1576			p_cmd = name_token(p_last);
1577			if (p_cmd) {
1578				port = strchr(p_cmd, ':');
1579				if (port) {
1580					*port = '\0';
1581					port++;
1582				}
1583			}
1584			osm_perfmgr_print_counters(&p_osm->perfmgr, p_cmd,
1585						   out, port, 0);
1586		} else if (strcmp(p_cmd, "print_errors") == 0 ||
1587			   strcmp(p_cmd, "pe") == 0) {
1588			p_cmd = name_token(p_last);
1589			osm_perfmgr_print_counters(&p_osm->perfmgr, p_cmd,
1590						   out, NULL, 1);
1591		} else if (strcmp(p_cmd, "dump_redir") == 0) {
1592			p_cmd = name_token(p_last);
1593			dump_redir(p_osm, p_cmd, out);
1594		} else if (strcmp(p_cmd, "clear_redir") == 0) {
1595			p_cmd = name_token(p_last);
1596			clear_redir(p_osm, p_cmd, out);
1597		} else if (strcmp(p_cmd, "sweep_time") == 0) {
1598			p_cmd = next_token(p_last);
1599			if (p_cmd) {
1600				uint16_t time_s = atoi(p_cmd);
1601				if (time_s < 1)
1602					fprintf(out,
1603						"sweep_time requires a "
1604						"positive time period "
1605						"(in seconds) to be "
1606						"specified\n");
1607				else
1608					osm_perfmgr_set_sweep_time_s(
1609							&p_osm->perfmgr,
1610							time_s);
1611			} else {
1612				fprintf(out,
1613					"sweep_time requires a time period "
1614					"(in seconds) to be specified\n");
1615			}
1616		} else if (strcmp(p_cmd, "sweep") == 0) {
1617			osm_sm_signal(&p_osm->sm, OSM_SIGNAL_PERFMGR_SWEEP);
1618			fprintf(out, "sweep initiated...\n");
1619		} else {
1620			fprintf(out, "\"%s\" option not found\n", p_cmd);
1621		}
1622	} else {
1623		fprintf(out, "Performance Manager status:\n"
1624			"state                        : %s\n"
1625			"sweep state                  : %s\n"
1626			"sweep time                   : %us\n"
1627			"outstanding queries/max      : %d/%u\n"
1628			"remove missing nodes from DB : %s\n"
1629			"query ClassPortInfo          : %s\n",
1630			osm_perfmgr_get_state_str(&p_osm->perfmgr),
1631			osm_perfmgr_get_sweep_state_str(&p_osm->perfmgr),
1632			osm_perfmgr_get_sweep_time_s(&p_osm->perfmgr),
1633			p_osm->perfmgr.outstanding_queries,
1634			p_osm->perfmgr.max_outstanding_queries,
1635			osm_perfmgr_get_rm_nodes(&p_osm->perfmgr)
1636						 ? "TRUE" : "FALSE",
1637			osm_perfmgr_get_query_cpi(&p_osm->perfmgr)
1638						 ? "TRUE" : "FALSE");
1639	}
1640}
1641#endif				/* ENABLE_OSM_PERF_MGR */
1642
1643static void quit_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
1644{
1645	cio_close(&p_osm->console, &p_osm->log);
1646}
1647
1648static void help_version(FILE * out, int detail)
1649{
1650	fprintf(out, "version -- print the OSM version\n");
1651}
1652
1653static void version_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
1654{
1655	fprintf(out, "%s build %s %s\n", p_osm->osm_version, __DATE__, __TIME__);
1656}
1657
1658/* more parse routines go here */
1659typedef struct _regexp_list {
1660	regex_t exp;
1661	struct _regexp_list *next;
1662} regexp_list_t;
1663
1664static void dump_portguid_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
1665{
1666	cl_qmap_t *p_alias_port_guid_tbl;
1667	osm_alias_guid_t *p_alias_guid, *p_next_alias_guid;
1668	regexp_list_t *p_regexp, *p_head_regexp = NULL;
1669	FILE *output = out;
1670
1671	while (1) {
1672		char *p_cmd = next_token(p_last);
1673		if (!p_cmd)
1674			break;
1675
1676		if (strcmp(p_cmd, "file") == 0) {
1677			p_cmd = next_token(p_last);
1678			if (p_cmd) {
1679				output = fopen(p_cmd, "w+");
1680				if (output == NULL) {
1681					fprintf(out,
1682						"Could not open file %s: %s\n",
1683						p_cmd, strerror(errno));
1684					output = out;
1685				}
1686			} else
1687				fprintf(out, "No file name passed\n");
1688		} else if (!(p_regexp = malloc(sizeof(*p_regexp)))) {
1689			fprintf(out, "No memory\n");
1690			break;
1691		} else if (regcomp(&p_regexp->exp, p_cmd,
1692				   REG_NOSUB | REG_EXTENDED) != 0) {
1693			fprintf(out, "Cannot parse regular expression \'%s\'."
1694				" Skipping\n", p_cmd);
1695			free(p_regexp);
1696			continue;
1697		} else {
1698			p_regexp->next = p_head_regexp;
1699			p_head_regexp = p_regexp;
1700		}
1701	}
1702
1703	/* Check we have at least one expression to match */
1704	if (p_head_regexp == NULL) {
1705		fprintf(out, "No valid expression provided. Aborting\n");
1706		goto Exit;
1707	}
1708
1709	if (p_osm->sm.p_subn->need_update != 0) {
1710		fprintf(out, "Subnet is not ready yet. Try again later\n");
1711		goto Free_and_exit;
1712	}
1713
1714	/* Subnet doesn't need to be updated so we can carry on */
1715
1716	p_alias_port_guid_tbl = &(p_osm->sm.p_subn->alias_port_guid_tbl);
1717	CL_PLOCK_ACQUIRE(p_osm->sm.p_lock);
1718
1719	p_next_alias_guid = (osm_alias_guid_t *) cl_qmap_head(p_alias_port_guid_tbl);
1720	while (p_next_alias_guid != (osm_alias_guid_t *) cl_qmap_end(p_alias_port_guid_tbl)) {
1721
1722		p_alias_guid = p_next_alias_guid;
1723		p_next_alias_guid =
1724		    (osm_alias_guid_t *) cl_qmap_next(&p_next_alias_guid->map_item);
1725
1726		for (p_regexp = p_head_regexp; p_regexp != NULL;
1727		     p_regexp = p_regexp->next)
1728			if (regexec(&p_regexp->exp,
1729				    p_alias_guid->p_base_port->p_node->print_desc,
1730				    0, NULL, 0) == 0) {
1731				fprintf(output, "0x%" PRIxLEAST64 "\n",
1732					cl_ntoh64(p_alias_guid->alias_guid));
1733				break;
1734			}
1735	}
1736
1737	CL_PLOCK_RELEASE(p_osm->sm.p_lock);
1738
1739Free_and_exit:
1740	for (; p_head_regexp; p_head_regexp = p_regexp) {
1741		p_regexp = p_head_regexp->next;
1742		regfree(&p_head_regexp->exp);
1743		free(p_head_regexp);
1744	}
1745Exit:
1746	if (output != out)
1747		fclose(output);
1748}
1749
1750static void help_dump_portguid(FILE * out, int detail)
1751{
1752	fprintf(out,
1753		"dump_portguid [file filename] regexp1 [regexp2 [regexp3 ...]] -- Dump port GUID matching a regexp \n");
1754	if (detail) {
1755		fprintf(out,
1756			"getguidgetguid  -- Dump all the port GUID whom node_desc matches one of the provided regexp\n");
1757		fprintf(out,
1758			"   [file filename] -- Send the port GUID list to the specified file instead of regular output\n");
1759	}
1760
1761}
1762
1763static const struct command console_cmds[] = {
1764	{"help", &help_command, &help_parse},
1765	{"quit", &help_quit, &quit_parse},
1766	{"loglevel", &help_loglevel, &loglevel_parse},
1767	{"permodlog", &help_permodlog, &permodlog_parse},
1768	{"priority", &help_priority, &priority_parse},
1769	{"resweep", &help_resweep, &resweep_parse},
1770	{"reroute", &help_reroute, &reroute_parse},
1771	{"sweep", &help_sweep, &sweep_parse},
1772	{"status", &help_status, &status_parse},
1773	{"logflush", &help_logflush, &logflush_parse},
1774	{"querylid", &help_querylid, &querylid_parse},
1775	{"portstatus", &help_portstatus, &portstatus_parse},
1776	{"switchbalance", &help_switchbalance, &switchbalance_parse},
1777	{"lidbalance", &help_lidbalance, &lidbalance_parse},
1778	{"dump_conf", &help_dump_conf, &dump_conf_parse},
1779	{"update_desc", &help_update_desc, &update_desc_parse},
1780	{"version", &help_version, &version_parse},
1781#ifdef ENABLE_OSM_PERF_MGR
1782	{"perfmgr", &help_perfmgr, &perfmgr_parse},
1783	{"pm", &help_pm, &perfmgr_parse},
1784#endif				/* ENABLE_OSM_PERF_MGR */
1785	{"dump_portguid", &help_dump_portguid, &dump_portguid_parse},
1786	{NULL, NULL, NULL}	/* end of array */
1787};
1788
1789static void parse_cmd_line(char *line, osm_opensm_t * p_osm)
1790{
1791	char *p_cmd, *p_last;
1792	int i, found = 0;
1793	FILE *out = p_osm->console.out;
1794
1795	while (isspace(*line))
1796		line++;
1797	if (!*line)
1798		return;
1799
1800	/* find first token which is the command */
1801	p_cmd = strtok_r(line, " \t\n\r", &p_last);
1802	if (p_cmd) {
1803		for (i = 0; console_cmds[i].name; i++) {
1804			if (loop_command.on) {
1805				if (!strcmp(p_cmd, "q")) {
1806					loop_command.on = 0;
1807				}
1808				found = 1;
1809				break;
1810			}
1811			if (!strcmp(p_cmd, console_cmds[i].name)) {
1812				found = 1;
1813				console_cmds[i].parse_function(&p_last, p_osm,
1814							       out);
1815				break;
1816			}
1817		}
1818		if (!found) {
1819			fprintf(out, "%s : Command not found\n\n", p_cmd);
1820			help_command(out, 0);
1821		}
1822	} else {
1823		fprintf(out, "Error parsing command line: `%s'\n", line);
1824	}
1825	if (loop_command.on) {
1826		fprintf(out, "use \"q<ret>\" to quit loop\n");
1827		fflush(out);
1828	}
1829}
1830
1831int osm_console(osm_opensm_t * p_osm)
1832{
1833	struct pollfd pollfd[2];
1834	char *p_line;
1835	size_t len;
1836	ssize_t n;
1837	struct pollfd *fds;
1838	nfds_t nfds;
1839	osm_console_t *p_oct = &p_osm->console;
1840
1841	pollfd[0].fd = p_oct->socket;
1842	pollfd[0].events = POLLIN;
1843	pollfd[0].revents = 0;
1844
1845	pollfd[1].fd = p_oct->in_fd;
1846	pollfd[1].events = POLLIN;
1847	pollfd[1].revents = 0;
1848
1849	fds = p_oct->socket < 0 ? &pollfd[1] : pollfd;
1850	nfds = p_oct->socket < 0 || pollfd[1].fd < 0 ? 1 : 2;
1851
1852	if (loop_command.on && loop_command_check_time() &&
1853	    loop_command.loop_function) {
1854		if (p_oct->out) {
1855			loop_command.loop_function(p_osm, p_oct->out);
1856			fflush(p_oct->out);
1857		} else {
1858			loop_command.on = 0;
1859		}
1860	}
1861
1862	if (poll(fds, nfds, 1000) <= 0)
1863		return 0;
1864
1865#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
1866	if (pollfd[0].revents & POLLIN) {
1867		int new_fd = 0;
1868		struct sockaddr_in sin;
1869		socklen_t len = sizeof(sin);
1870		struct hostent *hent;
1871		if ((new_fd = accept(p_oct->socket, (struct sockaddr *)&sin, &len)) < 0) {
1872			OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
1873				"ERR 4B04: Failed to accept console socket: %s\n",
1874				strerror(errno));
1875			p_oct->in_fd = -1;
1876			return 0;
1877		}
1878		if (inet_ntop
1879		    (AF_INET, &sin.sin_addr, p_oct->client_ip,
1880		     sizeof(p_oct->client_ip)) == NULL) {
1881			snprintf(p_oct->client_ip, sizeof(p_oct->client_ip),
1882				 "STRING_UNKNOWN");
1883		}
1884		if ((hent = gethostbyaddr((const char *)&sin.sin_addr,
1885					  sizeof(struct in_addr),
1886					  AF_INET)) == NULL) {
1887			snprintf(p_oct->client_hn, sizeof(p_oct->client_hn),
1888				 "STRING_UNKNOWN");
1889		} else {
1890			snprintf(p_oct->client_hn, sizeof(p_oct->client_hn),
1891				 "%s", hent->h_name);
1892		}
1893		if (is_authorized(p_oct)) {
1894			cio_open(p_oct, new_fd, &p_osm->log);
1895		} else {
1896			OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
1897				"ERR 4B05: Console connection denied: %s (%s)\n",
1898				p_oct->client_hn, p_oct->client_ip);
1899			close(new_fd);
1900		}
1901		return 0;
1902	}
1903#endif
1904
1905	if (pollfd[1].revents & POLLIN) {
1906		p_line = NULL;
1907		/* Get input line */
1908		n = getline(&p_line, &len, p_oct->in);
1909		if (n > 0) {
1910			/* Parse and act on input */
1911			parse_cmd_line(p_line, p_osm);
1912			if (!loop_command.on) {
1913				osm_console_prompt(p_oct->out);
1914			}
1915		} else
1916			cio_close(p_oct, &p_osm->log);
1917		if (p_line)
1918			free(p_line);
1919		return 0;
1920	}
1921	/* input fd is closed (hanged up) */
1922	if (pollfd[1].revents & POLLHUP) {
1923#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
1924		/* If we are using a socket, we close the current connection */
1925		if (p_oct->socket >= 0) {
1926			cio_close(p_oct, &p_osm->log);
1927			return 0;
1928		}
1929#endif
1930		/* If we use a local console, stdin is closed (most probable is pipe ended)
1931		 * so we close the local console */
1932		return -1;
1933	}
1934
1935	return 0;
1936}
1937