15115240jeff/*
2c187222hselasky * Copyright (c) 2005-2009 Voltaire, Inc. All rights reserved.
35115240jeff * Copyright (c) 2008 HNR Consulting. All rights reserved.
4c187222hselasky * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
55115240jeff *
65115240jeff * This software is available to you under a choice of one of two
75115240jeff * licenses.  You may choose to be licensed under the terms of the GNU
85115240jeff * General Public License (GPL) Version 2, available from the file
95115240jeff * COPYING in the main directory of this source tree, or the
105115240jeff * OpenIB.org BSD license below:
115115240jeff *
125115240jeff *     Redistribution and use in source and binary forms, with or
135115240jeff *     without modification, are permitted provided that the following
145115240jeff *     conditions are met:
155115240jeff *
165115240jeff *      - Redistributions of source code must retain the above
175115240jeff *        copyright notice, this list of conditions and the following
185115240jeff *        disclaimer.
195115240jeff *
205115240jeff *      - Redistributions in binary form must reproduce the above
215115240jeff *        copyright notice, this list of conditions and the following
225115240jeff *        disclaimer in the documentation and/or other materials
235115240jeff *        provided with the distribution.
245115240jeff *
255115240jeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
265115240jeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
275115240jeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
285115240jeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
295115240jeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
305115240jeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
315115240jeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
325115240jeff * SOFTWARE.
335115240jeff *
345115240jeff */
355115240jeff
365115240jeff/*
375115240jeff * Abstract:
385115240jeff *    Provide a framework for the Console which decouples the connection
395115240jeff *    or I/O from the functionality, or commands.
405115240jeff *
415115240jeff *    Extensible - allows a variety of connection methods independent of
425115240jeff *    the console commands.
435115240jeff */
445115240jeff
455115240jeff#if HAVE_CONFIG_H
465115240jeff#  include <config.h>
475115240jeff#endif				/* HAVE_CONFIG_H */
485115240jeff
49c187222hselasky#define _WITH_GETLINE		/* for getline */
50c187222hselasky#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
515115240jeff#include <tcpd.h>
525115240jeff#include <arpa/inet.h>
535115240jeff#include <netinet/in.h>
545115240jeff#include <sys/socket.h>
555115240jeff#endif
56c187222hselasky
575115240jeff#include <unistd.h>
585115240jeff#include <errno.h>
595115240jeff#include <signal.h>
60c187222hselasky#include <opensm/osm_file_ids.h>
61c187222hselasky#define FILE_ID OSM_FILE_CONSOLE_IO_C
625115240jeff#include <opensm/osm_console_io.h>
63c187222hselasky#include <stdlib.h>
645115240jeff
655115240jeffstatic int is_local(char *str)
665115240jeff{
67c187222hselasky	/* convenience - checks if just stdin/stdout */
685115240jeff	if (str)
695115240jeff		return (strcmp(str, OSM_LOCAL_CONSOLE) == 0);
705115240jeff	return 0;
715115240jeff}
725115240jeff
73c187222hselasky#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
745115240jeffstatic int is_loopback(char *str)
755115240jeff{
76c187222hselasky	/* convenience - checks if socket based connection */
775115240jeff	if (str)
785115240jeff		return (strcmp(str, OSM_LOOPBACK_CONSOLE) == 0);
795115240jeff	return 0;
805115240jeff}
81c187222hselasky#else
82c187222hselasky#define is_loopback is_local
83c187222hselasky#endif
845115240jeff
85c187222hselasky#ifdef ENABLE_OSM_CONSOLE_SOCKET
865115240jeffstatic int is_remote(char *str)
875115240jeff{
88c187222hselasky	/* convenience - checks if socket based connection */
895115240jeff	if (str)
90c187222hselasky		return strcmp(str, OSM_REMOTE_CONSOLE) == 0 || is_loopback(str);
915115240jeff	return 0;
925115240jeff}
93c187222hselasky#else
94c187222hselasky#define is_remote is_loopback
95c187222hselasky#endif
965115240jeff
975115240jeffint is_console_enabled(osm_subn_opt_t * p_opt)
985115240jeff{
99c187222hselasky	/* checks for a variety of types of consoles - default is off or 0 */
1005115240jeff	if (p_opt)
101c187222hselasky		return is_local(p_opt->console) || is_loopback(p_opt->console)
102c187222hselasky			|| is_remote(p_opt->console);
1035115240jeff	return 0;
1045115240jeff}
1055115240jeff
1065115240jeff
107c187222hselasky#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
108c187222hselaskyint cio_close(osm_console_t * p_oct, osm_log_t * p_log)
1095115240jeff{
1105115240jeff	int rtnval = -1;
111c187222hselasky	if (p_oct && p_oct->in_fd > 0) {
112c187222hselasky		OSM_LOG(p_log, OSM_LOG_VERBOSE,
113c187222hselasky			"Console connection closed: %s (%s)\n",
114c187222hselasky			p_oct->client_hn, p_oct->client_ip);
115c187222hselasky		rtnval = fclose(p_oct->in);
1165115240jeff		p_oct->in_fd = -1;
1175115240jeff		p_oct->out_fd = -1;
1185115240jeff		p_oct->in = NULL;
1195115240jeff		p_oct->out = NULL;
1205115240jeff	}
1215115240jeff	return rtnval;
1225115240jeff}
1235115240jeff
124c187222hselaskyint cio_open(osm_console_t * p_oct, int new_fd, osm_log_t * p_log)
1255115240jeff{
126c187222hselasky	/* returns zero if opened fine, -1 otherwise */
127c187222hselasky	char *p_line;
128c187222hselasky	size_t len;
129c187222hselasky	ssize_t n;
130c187222hselasky
131c187222hselasky	if (p_oct->in_fd >= 0) {
132c187222hselasky		FILE *file = fdopen(new_fd, "w+");
133c187222hselasky
134c187222hselasky		fprintf(file, "OpenSM Console connection already in use\n"
135c187222hselasky			"   kill other session (y/n)? ");
136c187222hselasky		fflush(file);
137c187222hselasky		p_line = NULL;
138c187222hselasky		n = getline(&p_line, &len, file);
139c187222hselasky		if (n > 0 && (p_line[0] == 'y' || p_line[0] == 'Y'))
140c187222hselasky			cio_close(p_oct, p_log);
141c187222hselasky		else {
142c187222hselasky			OSM_LOG(p_log, OSM_LOG_INFO,
143c187222hselasky				"Console connection aborted: %s (%s) - "
144c187222hselasky				"already in use\n",
145c187222hselasky				p_oct->client_hn, p_oct->client_ip);
146c187222hselasky			fclose(file);
147c187222hselasky			free(p_line);
148c187222hselasky			return -1;
149c187222hselasky		}
150c187222hselasky		free(p_line);
1515115240jeff	}
152c187222hselasky	p_oct->in_fd = new_fd;
153c187222hselasky	p_oct->out_fd = p_oct->in_fd;
154c187222hselasky	p_oct->in = fdopen(p_oct->in_fd, "w+");
155c187222hselasky	p_oct->out = p_oct->in;
156c187222hselasky	osm_console_prompt(p_oct->out);
157c187222hselasky	OSM_LOG(p_log, OSM_LOG_VERBOSE, "Console connection accepted: %s (%s)\n",
158c187222hselasky		p_oct->client_hn, p_oct->client_ip);
1595115240jeff
160c187222hselasky	return (p_oct->in == NULL) ? -1 : 0;
161c187222hselasky}
1625115240jeff
1635115240jeff/**********************************************************************
1645115240jeff * Do authentication & authorization check
1655115240jeff **********************************************************************/
1665115240jeffint is_authorized(osm_console_t * p_oct)
1675115240jeff{
1685115240jeff	/* allowed to use the console? */
1695115240jeff	p_oct->authorized = !is_remote(p_oct->client_type) ||
170c187222hselasky	    hosts_ctl((char *)OSM_DAEMON_NAME, p_oct->client_hn, p_oct->client_ip,
171c187222hselasky		      (char *)STRING_UNKNOWN);
1725115240jeff	return p_oct->authorized;
1735115240jeff}
1745115240jeff#endif
1755115240jeff
1765115240jeffvoid osm_console_prompt(FILE * out)
1775115240jeff{
1785115240jeff	if (out) {
1795115240jeff		fprintf(out, "OpenSM %s", OSM_COMMAND_PROMPT);
1805115240jeff		fflush(out);
1815115240jeff	}
1825115240jeff}
1835115240jeff
1845115240jeffint osm_console_init(osm_subn_opt_t * opt, osm_console_t * p_oct, osm_log_t * p_log)
1855115240jeff{
1865115240jeff	p_oct->socket = -1;
1875115240jeff	strncpy(p_oct->client_type, opt->console, sizeof(p_oct->client_type));
1885115240jeff
1895115240jeff	/* set up the file descriptors for the console */
1905115240jeff	if (strcmp(opt->console, OSM_LOCAL_CONSOLE) == 0) {
1915115240jeff		p_oct->in = stdin;
1925115240jeff		p_oct->out = stdout;
1935115240jeff		p_oct->in_fd = fileno(stdin);
1945115240jeff		p_oct->out_fd = fileno(stdout);
1955115240jeff
1965115240jeff		osm_console_prompt(p_oct->out);
197c187222hselasky#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
198c187222hselasky	} else if (strcmp(opt->console, OSM_LOOPBACK_CONSOLE) == 0
1995115240jeff#ifdef ENABLE_OSM_CONSOLE_SOCKET
200c187222hselasky		   || strcmp(opt->console, OSM_REMOTE_CONSOLE) == 0
201c187222hselasky#endif
202c187222hselasky		   ) {
2035115240jeff		struct sockaddr_in sin;
2045115240jeff		int optval = 1;
2055115240jeff
2065115240jeff		if ((p_oct->socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
2075115240jeff			OSM_LOG(p_log, OSM_LOG_ERROR,
2085115240jeff				"ERR 4B01: Failed to open console socket: %s\n",
2095115240jeff				strerror(errno));
2105115240jeff			return -1;
2115115240jeff		}
212c187222hselasky
213c187222hselasky		if (setsockopt(p_oct->socket, SOL_SOCKET, SO_REUSEADDR,
214c187222hselasky			       &optval, sizeof(optval))) {
215c187222hselasky			OSM_LOG(p_log, OSM_LOG_ERROR,
216c187222hselasky		                "ERR 4B06: Failed to set socket option: %s\n",
217c187222hselasky		                strerror(errno));
218c187222hselasky		        return -1;
219c187222hselasky		}
220c187222hselasky
2215115240jeff		sin.sin_family = AF_INET;
2225115240jeff		sin.sin_port = htons(opt->console_port);
223c187222hselasky#ifdef ENABLE_OSM_CONSOLE_SOCKET
2245115240jeff		if (strcmp(opt->console, OSM_REMOTE_CONSOLE) == 0)
2255115240jeff			sin.sin_addr.s_addr = htonl(INADDR_ANY);
2265115240jeff		else
227c187222hselasky#endif
2285115240jeff			sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
229c187222hselasky		if (bind(p_oct->socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
2305115240jeff			OSM_LOG(p_log, OSM_LOG_ERROR,
2315115240jeff				"ERR 4B02: Failed to bind console socket: %s\n",
2325115240jeff				strerror(errno));
2335115240jeff			return -1;
2345115240jeff		}
2355115240jeff		if (listen(p_oct->socket, 1) < 0) {
2365115240jeff			OSM_LOG(p_log, OSM_LOG_ERROR,
237c187222hselasky				"ERR 4B03: Failed to listen on console socket: %s\n",
2385115240jeff				strerror(errno));
2395115240jeff			return -1;
2405115240jeff		}
2415115240jeff
2425115240jeff		signal(SIGPIPE, SIG_IGN);	/* protect ourselves from closed pipes */
2435115240jeff		p_oct->in = NULL;
2445115240jeff		p_oct->out = NULL;
2455115240jeff		p_oct->in_fd = -1;
2465115240jeff		p_oct->out_fd = -1;
2475115240jeff		OSM_LOG(p_log, OSM_LOG_INFO,
2485115240jeff			"Console listening on port %d\n", opt->console_port);
2495115240jeff#endif
2505115240jeff	}
2515115240jeff
2525115240jeff	return 0;
2535115240jeff}
2545115240jeff
2555115240jeff/* clean up and release resources */
2565115240jeffvoid osm_console_exit(osm_console_t * p_oct, osm_log_t * p_log)
2575115240jeff{
258c187222hselasky#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
259c187222hselasky	cio_close(p_oct, p_log);
260c187222hselasky	if (p_oct->socket > 0) {
261c187222hselasky		OSM_LOG(p_log, OSM_LOG_INFO, "Closing console socket\n");
262c187222hselasky		close(p_oct->socket);
263c187222hselasky		p_oct->socket = -1;
2645115240jeff	}
2655115240jeff#endif
266c187222hselasky}
267