1/*
2 * Copyright (c) 2005-2008 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#if HAVE_CONFIG_H
35#  include <config.h>
36#endif				/* HAVE_CONFIG_H */
37
38#define _GNU_SOURCE		/* for getline */
39#include <stdio.h>
40#include <stdlib.h>
41#include <sys/poll.h>
42#include <sys/types.h>
43#include <sys/socket.h>
44#include <netdb.h>
45#ifdef ENABLE_OSM_CONSOLE_SOCKET
46#include <arpa/inet.h>
47#endif
48#include <unistd.h>
49#include <errno.h>
50#include <ctype.h>
51#include <sys/time.h>
52#include <opensm/osm_console.h>
53#include <complib/cl_passivelock.h>
54#include <opensm/osm_perfmgr.h>
55#include <opensm/osm_subnet.h>
56
57struct command {
58	char *name;
59	void (*help_function) (FILE * out, int detail);
60	void (*parse_function) (char **p_last, osm_opensm_t * p_osm,
61				FILE * out);
62};
63
64static struct {
65	int on;
66	int delay_s;
67	time_t previous;
68	void (*loop_function) (osm_opensm_t * p_osm, FILE * out);
69} loop_command = {
70on: 0, delay_s: 2, loop_function:NULL};
71
72static const struct command console_cmds[];
73
74static inline char *next_token(char **p_last)
75{
76	return strtok_r(NULL, " \t\n\r", p_last);
77}
78
79static void help_command(FILE * out, int detail)
80{
81	int i;
82
83	fprintf(out, "Supported commands and syntax:\n");
84	fprintf(out, "help [<command>]\n");
85	/* skip help command */
86	for (i = 1; console_cmds[i].name; i++)
87		console_cmds[i].help_function(out, 0);
88}
89
90static void help_quit(FILE * out, int detail)
91{
92	fprintf(out, "quit (not valid in local mode; use ctl-c)\n");
93}
94
95static void help_loglevel(FILE * out, int detail)
96{
97	fprintf(out, "loglevel [<log-level>]\n");
98	if (detail) {
99		fprintf(out, "   log-level is OR'ed from the following\n");
100		fprintf(out, "   OSM_LOG_NONE             0x%02X\n",
101			OSM_LOG_NONE);
102		fprintf(out, "   OSM_LOG_ERROR            0x%02X\n",
103			OSM_LOG_ERROR);
104		fprintf(out, "   OSM_LOG_INFO             0x%02X\n",
105			OSM_LOG_INFO);
106		fprintf(out, "   OSM_LOG_VERBOSE          0x%02X\n",
107			OSM_LOG_VERBOSE);
108		fprintf(out, "   OSM_LOG_DEBUG            0x%02X\n",
109			OSM_LOG_DEBUG);
110		fprintf(out, "   OSM_LOG_FUNCS            0x%02X\n",
111			OSM_LOG_FUNCS);
112		fprintf(out, "   OSM_LOG_FRAMES           0x%02X\n",
113			OSM_LOG_FRAMES);
114		fprintf(out, "   OSM_LOG_ROUTING          0x%02X\n",
115			OSM_LOG_ROUTING);
116		fprintf(out, "   OSM_LOG_SYS              0x%02X\n",
117			OSM_LOG_SYS);
118		fprintf(out, "\n");
119		fprintf(out, "   OSM_LOG_DEFAULT_LEVEL    0x%02X\n",
120			OSM_LOG_DEFAULT_LEVEL);
121	}
122}
123
124static void help_priority(FILE * out, int detail)
125{
126	fprintf(out, "priority [<sm-priority>]\n");
127}
128
129static void help_resweep(FILE * out, int detail)
130{
131	fprintf(out, "resweep [heavy|light]\n");
132}
133
134static void help_reroute(FILE * out, int detail)
135{
136	fprintf(out, "reroute\n");
137	if (detail) {
138		fprintf(out, "reroute the fabric\n");
139	}
140}
141
142static void help_status(FILE * out, int detail)
143{
144	fprintf(out, "status [loop]\n");
145	if (detail) {
146		fprintf(out, "   loop -- type \"q<ret>\" to quit\n");
147	}
148}
149
150static void help_logflush(FILE * out, int detail)
151{
152	fprintf(out, "logflush -- flush the opensm.log file\n");
153}
154
155static void help_querylid(FILE * out, int detail)
156{
157	fprintf(out,
158		"querylid lid -- print internal information about the lid specified\n");
159}
160
161static void help_portstatus(FILE * out, int detail)
162{
163	fprintf(out, "portstatus [ca|switch|router]\n");
164	if (detail) {
165		fprintf(out, "summarize port status\n");
166		fprintf(out,
167			"   [ca|switch|router] -- limit the results to the node type specified\n");
168	}
169
170}
171
172static void help_switchbalance(FILE * out, int detail)
173{
174	fprintf(out, "switchbalance [verbose] [guid]\n");
175	if (detail) {
176		fprintf(out, "output switch balancing information\n");
177		fprintf(out,
178			"  [verbose] -- verbose output\n"
179			"  [guid] -- limit results to specified guid\n");
180	}
181}
182
183static void help_lidbalance(FILE * out, int detail)
184{
185	fprintf(out, "lidbalance [switchguid]\n");
186	if (detail) {
187		fprintf(out, "output lid balanced forwarding information\n");
188		fprintf(out,
189			"  [switchguid] -- limit results to specified switch guid\n");
190	}
191}
192
193static void help_dump_conf(FILE *out, int detail)
194{
195	fprintf(out, "dump_conf\n");
196	if (detail) {
197		fprintf(out, "dump current opensm configuration\n");
198	}
199}
200
201#ifdef ENABLE_OSM_PERF_MGR
202static void help_perfmgr(FILE * out, int detail)
203{
204	fprintf(out,
205		"perfmgr [enable|disable|clear_counters|dump_counters|sweep_time[seconds]]\n");
206	if (detail) {
207		fprintf(out,
208			"perfmgr -- print the performance manager state\n");
209		fprintf(out,
210			"   [enable|disable] -- change the perfmgr state\n");
211		fprintf(out,
212			"   [sweep_time] -- change the perfmgr sweep time (requires [seconds] option)\n");
213		fprintf(out,
214			"   [clear_counters] -- clear the counters stored\n");
215		fprintf(out,
216			"   [dump_counters [mach]] -- dump the counters (optionally in [mach]ine readable format)\n");
217		fprintf(out,
218			"   [print_counters <nodename|nodeguid>] -- print the counters for the specified node\n");
219	}
220}
221#endif				/* ENABLE_OSM_PERF_MGR */
222
223/* more help routines go here */
224
225static void help_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
226{
227	char *p_cmd;
228	int i, found = 0;
229
230	p_cmd = next_token(p_last);
231	if (!p_cmd)
232		help_command(out, 0);
233	else {
234		for (i = 1; console_cmds[i].name; i++) {
235			if (!strcmp(p_cmd, console_cmds[i].name)) {
236				found = 1;
237				console_cmds[i].help_function(out, 1);
238				break;
239			}
240		}
241		if (!found) {
242			fprintf(out, "%s : Command not found\n\n", p_cmd);
243			help_command(out, 0);
244		}
245	}
246}
247
248static void loglevel_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
249{
250	char *p_cmd;
251	int level;
252
253	p_cmd = next_token(p_last);
254	if (!p_cmd)
255		fprintf(out, "Current log level is 0x%x\n",
256			osm_log_get_level(&p_osm->log));
257	else {
258		/* Handle x, 0x, and decimal specification of log level */
259		if (!strncmp(p_cmd, "x", 1)) {
260			p_cmd++;
261			level = strtoul(p_cmd, NULL, 16);
262		} else {
263			if (!strncmp(p_cmd, "0x", 2)) {
264				p_cmd += 2;
265				level = strtoul(p_cmd, NULL, 16);
266			} else
267				level = strtol(p_cmd, NULL, 10);
268		}
269		if ((level >= 0) && (level < 256)) {
270			fprintf(out, "Setting log level to 0x%x\n", level);
271			osm_log_set_level(&p_osm->log, level);
272		} else
273			fprintf(out, "Invalid log level 0x%x\n", level);
274	}
275}
276
277static void priority_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
278{
279	char *p_cmd;
280	int priority;
281
282	p_cmd = next_token(p_last);
283	if (!p_cmd)
284		fprintf(out, "Current sm-priority is %d\n",
285			p_osm->subn.opt.sm_priority);
286	else {
287		priority = strtol(p_cmd, NULL, 0);
288		if (0 > priority || 15 < priority)
289			fprintf(out,
290				"Invalid sm-priority %d; must be between 0 and 15\n",
291				priority);
292		else {
293			fprintf(out, "Setting sm-priority to %d\n", priority);
294			osm_set_sm_priority(&p_osm->sm, (uint8_t)priority);
295		}
296	}
297}
298
299static char *sm_state_str(int state)
300{
301	switch (state) {
302	case IB_SMINFO_STATE_DISCOVERING:
303		return ("Discovering");
304	case IB_SMINFO_STATE_STANDBY:
305		return ("Standby");
306	case IB_SMINFO_STATE_NOTACTIVE:
307		return ("Not Active");
308	case IB_SMINFO_STATE_MASTER:
309		return ("Master");
310	}
311	return ("UNKNOWN");
312}
313
314static char *sa_state_str(osm_sa_state_t state)
315{
316	switch (state) {
317	case OSM_SA_STATE_INIT:
318		return ("Init");
319	case OSM_SA_STATE_READY:
320		return ("Ready");
321	}
322	return ("UNKNOWN");
323}
324
325static void print_status(osm_opensm_t * p_osm, FILE * out)
326{
327	cl_list_item_t *item;
328
329	if (out) {
330		cl_plock_acquire(&p_osm->lock);
331		fprintf(out, "   OpenSM Version       : %s\n", p_osm->osm_version);
332		fprintf(out, "   SM State             : %s\n",
333			sm_state_str(p_osm->subn.sm_state));
334		fprintf(out, "   SA State             : %s\n",
335			sa_state_str(p_osm->sa.state));
336		fprintf(out, "   Routing Engine       : %s\n",
337			osm_routing_engine_type_str(p_osm->
338						    routing_engine_used));
339
340		fprintf(out, "   Loaded event plugins :");
341		if (cl_qlist_head(&p_osm->plugin_list) ==
342			cl_qlist_end(&p_osm->plugin_list)) {
343			fprintf(out, " <none>");
344		}
345		for (item = cl_qlist_head(&p_osm->plugin_list);
346		     item != cl_qlist_end(&p_osm->plugin_list);
347		     item = cl_qlist_next(item))
348			fprintf(out, " %s",
349				((osm_epi_plugin_t *)item)->plugin_name);
350		fprintf(out, "\n");
351
352#ifdef ENABLE_OSM_PERF_MGR
353		fprintf(out, "\n   PerfMgr state/sweep state : %s/%s\n",
354			osm_perfmgr_get_state_str(&(p_osm->perfmgr)),
355			osm_perfmgr_get_sweep_state_str(&(p_osm->perfmgr)));
356#endif
357		fprintf(out, "\n   MAD stats\n"
358			"   ---------\n"
359			"   QP0 MADs outstanding           : %d\n"
360			"   QP0 MADs outstanding (on wire) : %d\n"
361			"   QP0 MADs rcvd                  : %d\n"
362			"   QP0 MADs sent                  : %d\n"
363			"   QP0 unicasts sent              : %d\n"
364			"   QP0 unknown MADs rcvd          : %d\n"
365			"   SA MADs outstanding            : %d\n"
366			"   SA MADs rcvd                   : %d\n"
367			"   SA MADs sent                   : %d\n"
368			"   SA unknown MADs rcvd           : %d\n"
369			"   SA MADs ignored                : %d\n",
370			p_osm->stats.qp0_mads_outstanding,
371			p_osm->stats.qp0_mads_outstanding_on_wire,
372			p_osm->stats.qp0_mads_rcvd,
373			p_osm->stats.qp0_mads_sent,
374			p_osm->stats.qp0_unicasts_sent,
375			p_osm->stats.qp0_mads_rcvd_unknown,
376			p_osm->stats.sa_mads_outstanding,
377			p_osm->stats.sa_mads_rcvd,
378			p_osm->stats.sa_mads_sent,
379			p_osm->stats.sa_mads_rcvd_unknown,
380			p_osm->stats.sa_mads_ignored);
381		fprintf(out, "\n   Subnet flags\n"
382			"   ------------\n"
383			"   Ignore existing lfts           : %d\n"
384			"   Subnet Init errors             : %d\n"
385			"   In sweep hop 0                 : %d\n"
386			"   First time master sweep        : %d\n"
387			"   Coming out of standby          : %d\n",
388			p_osm->subn.ignore_existing_lfts,
389			p_osm->subn.subnet_initialization_error,
390			p_osm->subn.in_sweep_hop_0,
391			p_osm->subn.first_time_master_sweep,
392			p_osm->subn.coming_out_of_standby);
393		fprintf(out, "\n");
394		cl_plock_release(&p_osm->lock);
395	}
396}
397
398static int loop_command_check_time(void)
399{
400	time_t cur = time(NULL);
401	if ((loop_command.previous + loop_command.delay_s) < cur) {
402		loop_command.previous = cur;
403		return (1);
404	}
405	return (0);
406}
407
408static void status_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
409{
410	char *p_cmd;
411
412	p_cmd = next_token(p_last);
413	if (p_cmd) {
414		if (strcmp(p_cmd, "loop") == 0) {
415			fprintf(out, "Looping on status command...\n");
416			fflush(out);
417			loop_command.on = 1;
418			loop_command.previous = time(NULL);
419			loop_command.loop_function = print_status;
420		} else {
421			help_status(out, 1);
422			return;
423		}
424	}
425	print_status(p_osm, out);
426}
427
428static void resweep_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
429{
430	char *p_cmd;
431
432	p_cmd = next_token(p_last);
433	if (!p_cmd ||
434	    (strcmp(p_cmd, "heavy") != 0 && strcmp(p_cmd, "light") != 0)) {
435		fprintf(out, "Invalid resweep command\n");
436		help_resweep(out, 1);
437	} else {
438		if (strcmp(p_cmd, "heavy") == 0)
439			p_osm->subn.force_heavy_sweep = TRUE;
440		osm_opensm_sweep(p_osm);
441	}
442}
443
444static void reroute_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
445{
446	p_osm->subn.force_reroute = TRUE;
447	osm_opensm_sweep(p_osm);
448}
449
450static void logflush_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
451{
452	fflush(p_osm->log.out_port);
453}
454
455static void querylid_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
456{
457	int p = 0;
458	uint16_t lid = 0;
459	osm_port_t *p_port = NULL;
460	char *p_cmd = next_token(p_last);
461
462	if (!p_cmd) {
463		fprintf(out, "no LID specified\n");
464		help_querylid(out, 1);
465		return;
466	}
467
468	lid = (uint16_t) strtoul(p_cmd, NULL, 0);
469	cl_plock_acquire(&p_osm->lock);
470	if (lid > cl_ptr_vector_get_capacity(&(p_osm->subn.port_lid_tbl)))
471		goto invalid_lid;
472	p_port = cl_ptr_vector_get(&(p_osm->subn.port_lid_tbl), lid);
473	if (!p_port)
474		goto invalid_lid;
475
476	fprintf(out, "Query results for LID %u\n", lid);
477	fprintf(out,
478		"   GUID                : 0x%016" PRIx64 "\n"
479		"   Node Desc           : %s\n"
480		"   Node Type           : %s\n"
481		"   Num Ports           : %d\n",
482		cl_ntoh64(p_port->guid),
483		p_port->p_node->print_desc,
484		ib_get_node_type_str(osm_node_get_type(p_port->p_node)),
485		p_port->p_node->node_info.num_ports);
486
487	if (p_port->p_node->sw)
488		p = 0;
489	else
490		p = 1;
491	for ( /* see above */ ; p < p_port->p_node->physp_tbl_size; p++) {
492		fprintf(out,
493			"   Port %d health       : %s\n",
494			p,
495			p_port->p_node->physp_table[p].
496			healthy ? "OK" : "ERROR");
497	}
498
499	cl_plock_release(&p_osm->lock);
500	return;
501
502invalid_lid:
503	cl_plock_release(&p_osm->lock);
504	fprintf(out, "Invalid lid %d\n", lid);
505	return;
506}
507
508/**
509 * Data structures for the portstatus command
510 */
511typedef struct _port_report {
512	struct _port_report *next;
513	uint64_t node_guid;
514	uint8_t port_num;
515	char print_desc[IB_NODE_DESCRIPTION_SIZE + 1];
516} port_report_t;
517
518static void
519__tag_port_report(port_report_t ** head, uint64_t node_guid,
520		  uint8_t port_num, char *print_desc)
521{
522	port_report_t *rep = malloc(sizeof(*rep));
523	if (!rep)
524		return;
525
526	rep->node_guid = node_guid;
527	rep->port_num = port_num;
528	memcpy(rep->print_desc, print_desc, IB_NODE_DESCRIPTION_SIZE + 1);
529	rep->next = NULL;
530	if (*head) {
531		rep->next = *head;
532		*head = rep;
533	} else
534		*head = rep;
535}
536
537static void __print_port_report(FILE * out, port_report_t * head)
538{
539	port_report_t *item = head;
540	while (item != NULL) {
541		fprintf(out, "      0x%016" PRIx64 " %d (%s)\n",
542			item->node_guid, item->port_num, item->print_desc);
543		port_report_t *next = item->next;
544		free(item);
545		item = next;
546	}
547}
548
549typedef struct {
550	uint8_t node_type_lim;	/* limit the results; 0 == ALL */
551	uint64_t total_nodes;
552	uint64_t total_ports;
553	uint64_t ports_down;
554	uint64_t ports_active;
555	uint64_t ports_disabled;
556	port_report_t *disabled_ports;
557	uint64_t ports_1X;
558	uint64_t ports_4X;
559	uint64_t ports_8X;
560	uint64_t ports_12X;
561	uint64_t ports_unknown_width;
562	uint64_t ports_reduced_width;
563	port_report_t *reduced_width_ports;
564	uint64_t ports_sdr;
565	uint64_t ports_ddr;
566	uint64_t ports_qdr;
567	uint64_t ports_unknown_speed;
568	uint64_t ports_reduced_speed;
569	port_report_t *reduced_speed_ports;
570} fabric_stats_t;
571
572/**
573 * iterator function to get portstatus on each node
574 */
575static void __get_stats(cl_map_item_t * const p_map_item, void *context)
576{
577	fabric_stats_t *fs = (fabric_stats_t *) context;
578	osm_node_t *node = (osm_node_t *) p_map_item;
579	uint8_t num_ports = osm_node_get_num_physp(node);
580	uint8_t port = 0;
581
582	/* Skip nodes we are not interested in */
583	if (fs->node_type_lim != 0
584	    && fs->node_type_lim != node->node_info.node_type)
585		return;
586
587	fs->total_nodes++;
588
589	for (port = 1; port < num_ports; port++) {
590		osm_physp_t *phys = osm_node_get_physp_ptr(node, port);
591		ib_port_info_t *pi = NULL;
592		uint8_t active_speed = 0;
593		uint8_t enabled_speed = 0;
594		uint8_t active_width = 0;
595		uint8_t enabled_width = 0;
596		uint8_t port_state = 0;
597		uint8_t port_phys_state = 0;
598
599		if (!phys)
600			continue;
601
602		pi = &(phys->port_info);
603		active_speed = ib_port_info_get_link_speed_active(pi);
604		enabled_speed = ib_port_info_get_link_speed_enabled(pi);
605		active_width = pi->link_width_active;
606		enabled_width = pi->link_width_enabled;
607		port_state = ib_port_info_get_port_state(pi);
608		port_phys_state = ib_port_info_get_port_phys_state(pi);
609
610		if ((enabled_width ^ active_width) > active_width) {
611			__tag_port_report(&(fs->reduced_width_ports),
612					  cl_ntoh64(node->node_info.node_guid),
613					  port, node->print_desc);
614			fs->ports_reduced_width++;
615		}
616
617		if ((enabled_speed ^ active_speed) > active_speed) {
618			__tag_port_report(&(fs->reduced_speed_ports),
619					  cl_ntoh64(node->node_info.node_guid),
620					  port, node->print_desc);
621			fs->ports_reduced_speed++;
622		}
623
624		switch (active_speed) {
625		case IB_LINK_SPEED_ACTIVE_2_5:
626			fs->ports_sdr++;
627			break;
628		case IB_LINK_SPEED_ACTIVE_5:
629			fs->ports_ddr++;
630			break;
631		case IB_LINK_SPEED_ACTIVE_10:
632			fs->ports_qdr++;
633			break;
634		default:
635			fs->ports_unknown_speed++;
636			break;
637		}
638		switch (active_width) {
639		case IB_LINK_WIDTH_ACTIVE_1X:
640			fs->ports_1X++;
641			break;
642		case IB_LINK_WIDTH_ACTIVE_4X:
643			fs->ports_4X++;
644			break;
645		case IB_LINK_WIDTH_ACTIVE_8X:
646			fs->ports_8X++;
647			break;
648		case IB_LINK_WIDTH_ACTIVE_12X:
649			fs->ports_12X++;
650			break;
651		default:
652			fs->ports_unknown_width++;
653			break;
654		}
655		if (port_state == IB_LINK_DOWN)
656			fs->ports_down++;
657		else if (port_state == IB_LINK_ACTIVE)
658			fs->ports_active++;
659		if (port_phys_state == IB_PORT_PHYS_STATE_DISABLED) {
660			__tag_port_report(&(fs->disabled_ports),
661					  cl_ntoh64(node->node_info.node_guid),
662					  port, node->print_desc);
663			fs->ports_disabled++;
664		}
665
666		fs->total_ports++;
667	}
668}
669
670static void portstatus_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
671{
672	fabric_stats_t fs;
673	struct timeval before, after;
674	char *p_cmd;
675
676	memset(&fs, 0, sizeof(fs));
677
678	p_cmd = next_token(p_last);
679	if (p_cmd) {
680		if (strcmp(p_cmd, "ca") == 0) {
681			fs.node_type_lim = IB_NODE_TYPE_CA;
682		} else if (strcmp(p_cmd, "switch") == 0) {
683			fs.node_type_lim = IB_NODE_TYPE_SWITCH;
684		} else if (strcmp(p_cmd, "router") == 0) {
685			fs.node_type_lim = IB_NODE_TYPE_ROUTER;
686		} else {
687			fprintf(out, "Node type not understood\n");
688			help_portstatus(out, 1);
689			return;
690		}
691	}
692
693	gettimeofday(&before, NULL);
694
695	/* for each node in the system gather the stats */
696	cl_plock_acquire(&p_osm->lock);
697	cl_qmap_apply_func(&(p_osm->subn.node_guid_tbl), __get_stats,
698			   (void *)&fs);
699	cl_plock_release(&p_osm->lock);
700
701	gettimeofday(&after, NULL);
702
703	/* report the stats */
704	fprintf(out, "\"%s\" port status:\n",
705		fs.node_type_lim ? ib_get_node_type_str(fs.
706							node_type_lim) : "ALL");
707	fprintf(out,
708		"   %" PRIu64 " port(s) scanned on %" PRIu64
709		" nodes in %lu us\n", fs.total_ports, fs.total_nodes,
710		after.tv_usec - before.tv_usec);
711
712	if (fs.ports_down)
713		fprintf(out, "   %" PRIu64 " down\n", fs.ports_down);
714	if (fs.ports_active)
715		fprintf(out, "   %" PRIu64 " active\n", fs.ports_active);
716	if (fs.ports_1X)
717		fprintf(out, "   %" PRIu64 " at 1X\n", fs.ports_1X);
718	if (fs.ports_4X)
719		fprintf(out, "   %" PRIu64 " at 4X\n", fs.ports_4X);
720	if (fs.ports_8X)
721		fprintf(out, "   %" PRIu64 " at 8X\n", fs.ports_8X);
722	if (fs.ports_12X)
723		fprintf(out, "   %" PRIu64 " at 12X\n", fs.ports_12X);
724
725	if (fs.ports_sdr)
726		fprintf(out, "   %" PRIu64 " at 2.5 Gbps\n", fs.ports_sdr);
727	if (fs.ports_ddr)
728		fprintf(out, "   %" PRIu64 " at 5.0 Gbps\n", fs.ports_ddr);
729	if (fs.ports_qdr)
730		fprintf(out, "   %" PRIu64 " at 10.0 Gbps\n", fs.ports_qdr);
731
732	if (fs.ports_disabled + fs.ports_reduced_speed + fs.ports_reduced_width
733	    > 0) {
734		fprintf(out, "\nPossible issues:\n");
735	}
736	if (fs.ports_disabled) {
737		fprintf(out, "   %" PRIu64 " disabled\n", fs.ports_disabled);
738		__print_port_report(out, fs.disabled_ports);
739	}
740	if (fs.ports_reduced_speed) {
741		fprintf(out, "   %" PRIu64 " with reduced speed\n",
742			fs.ports_reduced_speed);
743		__print_port_report(out, fs.reduced_speed_ports);
744	}
745	if (fs.ports_reduced_width) {
746		fprintf(out, "   %" PRIu64 " with reduced width\n",
747			fs.ports_reduced_width);
748		__print_port_report(out, fs.reduced_width_ports);
749	}
750	fprintf(out, "\n");
751}
752
753static void switchbalance_check(osm_opensm_t * p_osm,
754				osm_switch_t * p_sw, FILE * out, int verbose)
755{
756	uint8_t port_num;
757	uint8_t num_ports;
758	const cl_qmap_t *p_port_tbl;
759	osm_port_t *p_port;
760	osm_physp_t *p_physp;
761	osm_physp_t *p_rem_physp;
762	osm_node_t *p_rem_node;
763	uint32_t count[255];	/* max ports is a uint8_t */
764	uint8_t output_ports[255];
765	uint8_t output_ports_count = 0;
766	uint32_t min_count = 0xFFFFFFFF;
767	uint32_t max_count = 0;
768	unsigned int i;
769
770	memset(count, '\0', sizeof(uint32_t) * 255);
771
772	/* Count port usage */
773	p_port_tbl = &p_osm->subn.port_guid_tbl;
774	for (p_port = (osm_port_t *) cl_qmap_head(p_port_tbl);
775	     p_port != (osm_port_t *) cl_qmap_end(p_port_tbl);
776	     p_port = (osm_port_t *) cl_qmap_next(&p_port->map_item)) {
777		uint16_t min_lid_ho;
778		uint16_t max_lid_ho;
779		uint16_t lid_ho;
780
781		/* Don't count switches in port usage */
782		if (osm_node_get_type(p_port->p_node) == IB_NODE_TYPE_SWITCH)
783			continue;
784
785		osm_port_get_lid_range_ho(p_port, &min_lid_ho, &max_lid_ho);
786
787		if (min_lid_ho == 0 || max_lid_ho == 0)
788			continue;
789
790		for (lid_ho = min_lid_ho; lid_ho <= max_lid_ho; lid_ho++) {
791			port_num = osm_switch_get_port_by_lid(p_sw, lid_ho);
792			if (port_num == OSM_NO_PATH)
793				continue;
794
795			count[port_num]++;
796		}
797	}
798
799	num_ports = p_sw->num_ports;
800	for (port_num = 1; port_num < num_ports; port_num++) {
801		p_physp = osm_node_get_physp_ptr(p_sw->p_node, port_num);
802
803		/* if port is down/unhealthy, don't consider it in
804		 * min/max calculations
805		 */
806		if (!p_physp || !osm_physp_is_healthy(p_physp)
807		    || !osm_physp_get_remote(p_physp))
808			continue;
809
810		p_rem_physp = osm_physp_get_remote(p_physp);
811		p_rem_node = osm_physp_get_node_ptr(p_rem_physp);
812
813		/* If we are directly connected to a CA/router, its not really
814		 * up for balancing consideration.
815		 */
816		if (osm_node_get_type(p_rem_node) != IB_NODE_TYPE_SWITCH)
817			continue;
818
819		output_ports[output_ports_count] = port_num;
820		output_ports_count++;
821
822		if (count[port_num] < min_count)
823			min_count = count[port_num];
824		if (count[port_num] > max_count)
825			max_count = count[port_num];
826	}
827
828	if (verbose || ((max_count - min_count) > 1)) {
829		if ((max_count - min_count) > 1)
830			fprintf(out,
831				"Unbalanced Switch: 0x%016" PRIx64 " (%s)\n",
832				cl_ntoh64(p_sw->p_node->node_info.node_guid),
833				p_sw->p_node->print_desc);
834		else
835			fprintf(out,
836				"Switch: 0x%016" PRIx64 " (%s)\n",
837				cl_ntoh64(p_sw->p_node->node_info.node_guid),
838				p_sw->p_node->print_desc);
839
840		for (i = 0; i < output_ports_count; i++) {
841			fprintf(out,
842				"Port %d: %d\n",
843				output_ports[i], count[output_ports[i]]);
844		}
845	}
846}
847
848static void switchbalance_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
849{
850	char *p_cmd;
851	uint64_t guid = 0;
852	osm_switch_t *p_sw;
853	int verbose = 0;
854
855	p_cmd = next_token(p_last);
856	if (p_cmd) {
857		char *p_end;
858
859		if (strcmp(p_cmd, "verbose") == 0) {
860			verbose++;
861			p_cmd = next_token(p_last);
862		}
863
864		if (p_cmd) {
865			guid = strtoull(p_cmd, &p_end, 0);
866			if (!guid || *p_end != '\0') {
867				fprintf(out, "Invalid guid specified\n");
868				help_switchbalance(out, 1);
869				return;
870			}
871		}
872	}
873
874	cl_plock_acquire(&p_osm->lock);
875	if (guid) {
876		p_sw = osm_get_switch_by_guid(&p_osm->subn, cl_hton64(guid));
877		if (!p_sw) {
878			fprintf(out, "guid not found\n");
879			goto lock_exit;
880		}
881
882		switchbalance_check(p_osm, p_sw, out, verbose);
883	} else {
884		cl_qmap_t *p_sw_guid_tbl = &p_osm->subn.sw_guid_tbl;
885		for (p_sw = (osm_switch_t *) cl_qmap_head(p_sw_guid_tbl);
886		     p_sw != (osm_switch_t *) cl_qmap_end(p_sw_guid_tbl);
887		     p_sw = (osm_switch_t *) cl_qmap_next(&p_sw->map_item))
888			switchbalance_check(p_osm, p_sw, out, verbose);
889	}
890lock_exit:
891	cl_plock_release(&p_osm->lock);
892	return;
893}
894
895static void lidbalance_check(osm_opensm_t * p_osm,
896			     osm_switch_t * p_sw, FILE * out)
897{
898	uint8_t port_num;
899	const cl_qmap_t *p_port_tbl;
900	osm_port_t *p_port;
901
902	p_port_tbl = &p_osm->subn.port_guid_tbl;
903	for (p_port = (osm_port_t *) cl_qmap_head(p_port_tbl);
904	     p_port != (osm_port_t *) cl_qmap_end(p_port_tbl);
905	     p_port = (osm_port_t *) cl_qmap_next(&p_port->map_item)) {
906		uint32_t port_count[255];	/* max ports is a uint8_t */
907		osm_node_t *rem_node[255];
908		uint32_t rem_node_count;
909		uint32_t rem_count[255];
910		osm_physp_t *p_physp;
911		osm_physp_t *p_rem_physp;
912		osm_node_t *p_rem_node;
913		uint32_t port_min_count = 0xFFFFFFFF;
914		uint32_t port_max_count = 0;
915		uint32_t rem_min_count = 0xFFFFFFFF;
916		uint32_t rem_max_count = 0;
917		uint16_t min_lid_ho;
918		uint16_t max_lid_ho;
919		uint16_t lid_ho;
920		uint8_t num_ports;
921		unsigned int i;
922
923		/* we only care about non-switches */
924		if (osm_node_get_type(p_port->p_node) == IB_NODE_TYPE_SWITCH)
925			continue;
926
927		osm_port_get_lid_range_ho(p_port, &min_lid_ho, &max_lid_ho);
928
929		if (min_lid_ho == 0 || max_lid_ho == 0)
930			continue;
931
932		memset(port_count, '\0', sizeof(uint32_t) * 255);
933		memset(rem_node, '\0', sizeof(osm_node_t *) * 255);
934		rem_node_count = 0;
935		memset(rem_count, '\0', sizeof(uint32_t) * 255);
936
937		for (lid_ho = min_lid_ho; lid_ho <= max_lid_ho; lid_ho++) {
938			boolean_t rem_node_found = FALSE;
939			unsigned int indx = 0;
940
941			port_num = osm_switch_get_port_by_lid(p_sw, lid_ho);
942			if (port_num == OSM_NO_PATH)
943				continue;
944
945			p_physp =
946			    osm_node_get_physp_ptr(p_sw->p_node, port_num);
947
948			/* if port is down/unhealthy, can't calculate */
949			if (!p_physp || !osm_physp_is_healthy(p_physp)
950			    || !osm_physp_get_remote(p_physp))
951				continue;
952
953			p_rem_physp = osm_physp_get_remote(p_physp);
954			p_rem_node = osm_physp_get_node_ptr(p_rem_physp);
955
956			/* determine if we've seen this remote node before.
957			 * If not, store it.  If yes, update the counter
958			 */
959			for (i = 0; i < rem_node_count; i++) {
960				if (rem_node[i] == p_rem_node) {
961					rem_node_found = TRUE;
962					indx = i;
963					break;
964				}
965			}
966
967			if (!rem_node_found) {
968				rem_node[rem_node_count] = p_rem_node;
969				rem_count[rem_node_count]++;
970				indx = rem_node_count;
971				rem_node_count++;
972			} else
973				rem_count[indx]++;
974
975			port_count[port_num]++;
976		}
977
978		if (!rem_node_count)
979			continue;
980
981		for (i = 0; i < rem_node_count; i++) {
982			if (rem_count[i] < rem_min_count)
983				rem_min_count = rem_count[i];
984			if (rem_count[i] > rem_max_count)
985				rem_max_count = rem_count[i];
986		}
987
988		num_ports = p_sw->num_ports;
989		for (i = 0; i < num_ports; i++) {
990			if (!port_count[i])
991				continue;
992			if (port_count[i] < port_min_count)
993				port_min_count = port_count[i];
994			if (port_count[i] > port_max_count)
995				port_max_count = port_count[i];
996		}
997
998		/* Output if this CA/router is being forwarded an unbalanced number of
999		 * times to a destination.
1000		 */
1001		if ((rem_max_count - rem_min_count) > 1) {
1002			fprintf(out,
1003				"Unbalanced Remote Forwarding: Switch 0x%016"
1004				PRIx64 " (%s): ",
1005				cl_ntoh64(p_sw->p_node->node_info.node_guid),
1006				p_sw->p_node->print_desc);
1007			if (osm_node_get_type(p_port->p_node) ==
1008			    IB_NODE_TYPE_CA)
1009				fprintf(out, "CA");
1010			else if (osm_node_get_type(p_port->p_node) ==
1011				 IB_NODE_TYPE_ROUTER)
1012				fprintf(out, "Router");
1013			fprintf(out, " 0x%016" PRIx64 " (%s): ",
1014				cl_ntoh64(p_port->p_node->node_info.node_guid),
1015				p_port->p_node->print_desc);
1016			for (i = 0; i < rem_node_count; i++) {
1017				fprintf(out,
1018					"Dest 0x%016" PRIx64 "(%s) - %u ",
1019					cl_ntoh64(rem_node[i]->node_info.
1020						  node_guid),
1021					rem_node[i]->print_desc, rem_count[i]);
1022			}
1023			fprintf(out, "\n");
1024		}
1025
1026		/* Output if this CA/router is being forwarded through a port
1027		 * an unbalanced number of times.
1028		 */
1029		if ((port_max_count - port_min_count) > 1) {
1030			fprintf(out,
1031				"Unbalanced Port Forwarding: Switch 0x%016"
1032				PRIx64 " (%s): ",
1033				cl_ntoh64(p_sw->p_node->node_info.node_guid),
1034				p_sw->p_node->print_desc);
1035			if (osm_node_get_type(p_port->p_node) ==
1036			    IB_NODE_TYPE_CA)
1037				fprintf(out, "CA");
1038			else if (osm_node_get_type(p_port->p_node) ==
1039				 IB_NODE_TYPE_ROUTER)
1040				fprintf(out, "Router");
1041			fprintf(out, " 0x%016" PRIx64 " (%s): ",
1042				cl_ntoh64(p_port->p_node->node_info.node_guid),
1043				p_port->p_node->print_desc);
1044			for (i = 0; i < num_ports; i++) {
1045				if (!port_count[i])
1046					continue;
1047				fprintf(out, "Port %u - %u: ", i,
1048					port_count[i]);
1049			}
1050			fprintf(out, "\n");
1051		}
1052	}
1053}
1054
1055static void lidbalance_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
1056{
1057	char *p_cmd;
1058	uint64_t guid = 0;
1059	osm_switch_t *p_sw;
1060
1061	p_cmd = next_token(p_last);
1062	if (p_cmd) {
1063		char *p_end;
1064
1065		guid = strtoull(p_cmd, &p_end, 0);
1066		if (!guid || *p_end != '\0') {
1067			fprintf(out, "Invalid switchguid specified\n");
1068			help_lidbalance(out, 1);
1069			return;
1070		}
1071	}
1072
1073	cl_plock_acquire(&p_osm->lock);
1074	if (guid) {
1075		p_sw = osm_get_switch_by_guid(&p_osm->subn, cl_hton64(guid));
1076		if (!p_sw) {
1077			fprintf(out, "switchguid not found\n");
1078			goto lock_exit;
1079		}
1080		lidbalance_check(p_osm, p_sw, out);
1081	} else {
1082		cl_qmap_t *p_sw_guid_tbl = &p_osm->subn.sw_guid_tbl;
1083		for (p_sw = (osm_switch_t *) cl_qmap_head(p_sw_guid_tbl);
1084		     p_sw != (osm_switch_t *) cl_qmap_end(p_sw_guid_tbl);
1085		     p_sw = (osm_switch_t *) cl_qmap_next(&p_sw->map_item))
1086			lidbalance_check(p_osm, p_sw, out);
1087	}
1088
1089lock_exit:
1090	cl_plock_release(&p_osm->lock);
1091	return;
1092}
1093
1094static void dump_conf_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
1095{
1096	osm_subn_output_conf(out, &p_osm->subn.opt);
1097}
1098
1099#ifdef ENABLE_OSM_PERF_MGR
1100static void perfmgr_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
1101{
1102	char *p_cmd;
1103
1104	p_cmd = next_token(p_last);
1105	if (p_cmd) {
1106		if (strcmp(p_cmd, "enable") == 0) {
1107			osm_perfmgr_set_state(&(p_osm->perfmgr),
1108					      PERFMGR_STATE_ENABLED);
1109		} else if (strcmp(p_cmd, "disable") == 0) {
1110			osm_perfmgr_set_state(&(p_osm->perfmgr),
1111					      PERFMGR_STATE_DISABLE);
1112		} else if (strcmp(p_cmd, "clear_counters") == 0) {
1113			osm_perfmgr_clear_counters(&(p_osm->perfmgr));
1114		} else if (strcmp(p_cmd, "dump_counters") == 0) {
1115			p_cmd = next_token(p_last);
1116			if (p_cmd && (strcmp(p_cmd, "mach") == 0)) {
1117				osm_perfmgr_dump_counters(&(p_osm->perfmgr),
1118							  PERFMGR_EVENT_DB_DUMP_MR);
1119			} else {
1120				osm_perfmgr_dump_counters(&(p_osm->perfmgr),
1121							  PERFMGR_EVENT_DB_DUMP_HR);
1122			}
1123		} else if (strcmp(p_cmd, "print_counters") == 0) {
1124			p_cmd = next_token(p_last);
1125			if (p_cmd) {
1126				osm_perfmgr_print_counters(&(p_osm->perfmgr),
1127							   p_cmd, out);
1128			} else {
1129				fprintf(out,
1130					"print_counters requires a node name to be specified\n");
1131			}
1132		} else if (strcmp(p_cmd, "sweep_time") == 0) {
1133			p_cmd = next_token(p_last);
1134			if (p_cmd) {
1135				uint16_t time_s = atoi(p_cmd);
1136				osm_perfmgr_set_sweep_time_s(&(p_osm->perfmgr),
1137							     time_s);
1138			} else {
1139				fprintf(out,
1140					"sweep_time requires a time period (in seconds) to be specified\n");
1141			}
1142		} else {
1143			fprintf(out, "\"%s\" option not found\n", p_cmd);
1144		}
1145	} else {
1146		fprintf(out, "Performance Manager status:\n"
1147			"state                   : %s\n"
1148			"sweep state             : %s\n"
1149			"sweep time              : %us\n"
1150			"outstanding queries/max : %d/%u\n",
1151			osm_perfmgr_get_state_str(&(p_osm->perfmgr)),
1152			osm_perfmgr_get_sweep_state_str(&(p_osm->perfmgr)),
1153			osm_perfmgr_get_sweep_time_s(&(p_osm->perfmgr)),
1154			p_osm->perfmgr.outstanding_queries,
1155			p_osm->perfmgr.max_outstanding_queries);
1156	}
1157}
1158#endif				/* ENABLE_OSM_PERF_MGR */
1159
1160static void quit_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
1161{
1162	osm_console_exit(&p_osm->console, &p_osm->log);
1163}
1164
1165static void help_version(FILE * out, int detail)
1166{
1167	fprintf(out, "version -- print the OSM version\n");
1168}
1169
1170static void version_parse(char **p_last, osm_opensm_t * p_osm, FILE * out)
1171{
1172	fprintf(out, "%s build %s %s\n", p_osm->osm_version, __DATE__, __TIME__);
1173}
1174
1175/* more parse routines go here */
1176
1177static const struct command console_cmds[] = {
1178	{"help", &help_command, &help_parse},
1179	{"quit", &help_quit, &quit_parse},
1180	{"loglevel", &help_loglevel, &loglevel_parse},
1181	{"priority", &help_priority, &priority_parse},
1182	{"resweep", &help_resweep, &resweep_parse},
1183	{"reroute", &help_reroute, &reroute_parse},
1184	{"status", &help_status, &status_parse},
1185	{"logflush", &help_logflush, &logflush_parse},
1186	{"querylid", &help_querylid, &querylid_parse},
1187	{"portstatus", &help_portstatus, &portstatus_parse},
1188	{"switchbalance", &help_switchbalance, &switchbalance_parse},
1189	{"lidbalance", &help_lidbalance, &lidbalance_parse},
1190	{"dump_conf", &help_dump_conf, &dump_conf_parse},
1191	{"version", &help_version, &version_parse},
1192#ifdef ENABLE_OSM_PERF_MGR
1193	{"perfmgr", &help_perfmgr, &perfmgr_parse},
1194#endif				/* ENABLE_OSM_PERF_MGR */
1195	{NULL, NULL, NULL}	/* end of array */
1196};
1197
1198static void parse_cmd_line(char *line, osm_opensm_t * p_osm)
1199{
1200	char *p_cmd, *p_last;
1201	int i, found = 0;
1202	FILE *out = p_osm->console.out;
1203
1204	while (isspace(*line))
1205		line++;
1206	if (!*line)
1207		return;
1208
1209	/* find first token which is the command */
1210	p_cmd = strtok_r(line, " \t\n\r", &p_last);
1211	if (p_cmd) {
1212		for (i = 0; console_cmds[i].name; i++) {
1213			if (loop_command.on) {
1214				if (!strcmp(p_cmd, "q")) {
1215					loop_command.on = 0;
1216				}
1217				found = 1;
1218				break;
1219			}
1220			if (!strcmp(p_cmd, console_cmds[i].name)) {
1221				found = 1;
1222				console_cmds[i].parse_function(&p_last, p_osm,
1223							       out);
1224				break;
1225			}
1226		}
1227		if (!found) {
1228			fprintf(out, "%s : Command not found\n\n", p_cmd);
1229			help_command(out, 0);
1230		}
1231	} else {
1232		fprintf(out, "Error parsing command line: `%s'\n", line);
1233	}
1234	if (loop_command.on) {
1235		fprintf(out, "use \"q<ret>\" to quit loop\n");
1236		fflush(out);
1237	}
1238}
1239
1240void osm_console(osm_opensm_t * p_osm)
1241{
1242	struct pollfd pollfd[2];
1243	char *p_line;
1244	size_t len;
1245	ssize_t n;
1246	struct pollfd *fds;
1247	nfds_t nfds;
1248	osm_console_t *p_oct = &p_osm->console;
1249	osm_log_t *p_log = &p_osm->log;
1250
1251	pollfd[0].fd = p_oct->socket;
1252	pollfd[0].events = POLLIN;
1253	pollfd[0].revents = 0;
1254
1255	pollfd[1].fd = p_oct->in_fd;
1256	pollfd[1].events = POLLIN;
1257	pollfd[1].revents = 0;
1258
1259	fds = p_oct->socket < 0 ? &pollfd[1] : pollfd;
1260	nfds = p_oct->socket < 0 || pollfd[1].fd < 0 ? 1 : 2;
1261
1262	if (loop_command.on && loop_command_check_time() &&
1263	    loop_command.loop_function) {
1264		if (p_oct->out) {
1265			loop_command.loop_function(p_osm, p_oct->out);
1266			fflush(p_oct->out);
1267		} else {
1268			loop_command.on = 0;
1269		}
1270	}
1271
1272	if (poll(fds, nfds, 1000) <= 0)
1273		return;
1274
1275#ifdef ENABLE_OSM_CONSOLE_SOCKET
1276	if (pollfd[0].revents & POLLIN) {
1277		int new_fd = 0;
1278		struct sockaddr_in sin;
1279		socklen_t len = sizeof(sin);
1280		struct hostent *hent;
1281		if ((new_fd = accept(p_oct->socket, &sin, &len)) < 0) {
1282			OSM_LOG(p_log, OSM_LOG_ERROR,
1283				"ERR 4B04: Failed to accept console socket: %s\n",
1284				strerror(errno));
1285			p_oct->in_fd = -1;
1286			return;
1287		}
1288		if (inet_ntop
1289		    (AF_INET, &sin.sin_addr, p_oct->client_ip,
1290		     sizeof(p_oct->client_ip)) == NULL) {
1291			snprintf(p_oct->client_ip, 64, "STRING_UNKNOWN");
1292		}
1293		if ((hent = gethostbyaddr((const char *)&sin.sin_addr,
1294					  sizeof(struct in_addr),
1295					  AF_INET)) == NULL) {
1296			snprintf(p_oct->client_hn, 128, "STRING_UNKNOWN");
1297		} else {
1298			snprintf(p_oct->client_hn, 128, "%s", hent->h_name);
1299		}
1300		if (is_authorized(p_oct)) {
1301			cio_open(p_oct, new_fd, p_log);
1302		} else {
1303			OSM_LOG(p_log, OSM_LOG_ERROR,
1304				"ERR 4B05: Console connection denied: %s (%s)\n",
1305				p_oct->client_hn, p_oct->client_ip);
1306			close(new_fd);
1307		}
1308		return;
1309	}
1310#endif
1311
1312	if (pollfd[1].revents & POLLIN) {
1313		p_line = NULL;
1314		/* Get input line */
1315		n = getline(&p_line, &len, p_oct->in);
1316		if (n > 0) {
1317			/* Parse and act on input */
1318			parse_cmd_line(p_line, p_osm);
1319			if (!loop_command.on) {
1320				osm_console_prompt(p_oct->out);
1321			}
1322		} else
1323			osm_console_exit(p_oct, p_log);
1324		if (p_line)
1325			free(p_line);
1326	}
1327}
1328