1321936Shselasky/*
2321936Shselasky * Copyright (c) 2005-2009 Voltaire, Inc. All rights reserved.
3321936Shselasky * Copyright (c) 2008 HNR Consulting. All rights reserved.
4321936Shselasky * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
5321936Shselasky *
6321936Shselasky * This software is available to you under a choice of one of two
7321936Shselasky * licenses.  You may choose to be licensed under the terms of the GNU
8321936Shselasky * General Public License (GPL) Version 2, available from the file
9321936Shselasky * COPYING in the main directory of this source tree, or the
10321936Shselasky * OpenIB.org BSD license below:
11321936Shselasky *
12321936Shselasky *     Redistribution and use in source and binary forms, with or
13321936Shselasky *     without modification, are permitted provided that the following
14321936Shselasky *     conditions are met:
15321936Shselasky *
16321936Shselasky *      - Redistributions of source code must retain the above
17321936Shselasky *        copyright notice, this list of conditions and the following
18321936Shselasky *        disclaimer.
19321936Shselasky *
20321936Shselasky *      - Redistributions in binary form must reproduce the above
21321936Shselasky *        copyright notice, this list of conditions and the following
22321936Shselasky *        disclaimer in the documentation and/or other materials
23321936Shselasky *        provided with the distribution.
24321936Shselasky *
25321936Shselasky * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26321936Shselasky * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27321936Shselasky * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28321936Shselasky * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29321936Shselasky * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30321936Shselasky * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31321936Shselasky * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32321936Shselasky * SOFTWARE.
33321936Shselasky *
34321936Shselasky */
35321936Shselasky
36321936Shselasky/*
37321936Shselasky * Abstract:
38321936Shselasky *    Provide a framework for the Console which decouples the connection
39321936Shselasky *    or I/O from the functionality, or commands.
40321936Shselasky *
41321936Shselasky *    Extensible - allows a variety of connection methods independent of
42321936Shselasky *    the console commands.
43321936Shselasky */
44321936Shselasky
45321936Shselasky#if HAVE_CONFIG_H
46321936Shselasky#  include <config.h>
47321936Shselasky#endif				/* HAVE_CONFIG_H */
48321936Shselasky
49321936Shselasky#define _WITH_GETLINE		/* for getline */
50321936Shselasky#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
51321936Shselasky#include <tcpd.h>
52321936Shselasky#include <arpa/inet.h>
53321936Shselasky#include <netinet/in.h>
54321936Shselasky#include <sys/socket.h>
55321936Shselasky#endif
56321936Shselasky
57321936Shselasky#include <unistd.h>
58321936Shselasky#include <errno.h>
59321936Shselasky#include <signal.h>
60321936Shselasky#include <opensm/osm_file_ids.h>
61321936Shselasky#define FILE_ID OSM_FILE_CONSOLE_IO_C
62321936Shselasky#include <opensm/osm_console_io.h>
63321936Shselasky#include <stdlib.h>
64321936Shselasky
65321936Shselaskystatic int is_local(char *str)
66321936Shselasky{
67321936Shselasky	/* convenience - checks if just stdin/stdout */
68321936Shselasky	if (str)
69321936Shselasky		return (strcmp(str, OSM_LOCAL_CONSOLE) == 0);
70321936Shselasky	return 0;
71321936Shselasky}
72321936Shselasky
73321936Shselasky#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
74321936Shselaskystatic int is_loopback(char *str)
75321936Shselasky{
76321936Shselasky	/* convenience - checks if socket based connection */
77321936Shselasky	if (str)
78321936Shselasky		return (strcmp(str, OSM_LOOPBACK_CONSOLE) == 0);
79321936Shselasky	return 0;
80321936Shselasky}
81321936Shselasky#else
82321936Shselasky#define is_loopback is_local
83321936Shselasky#endif
84321936Shselasky
85321936Shselasky#ifdef ENABLE_OSM_CONSOLE_SOCKET
86321936Shselaskystatic int is_remote(char *str)
87321936Shselasky{
88321936Shselasky	/* convenience - checks if socket based connection */
89321936Shselasky	if (str)
90321936Shselasky		return strcmp(str, OSM_REMOTE_CONSOLE) == 0 || is_loopback(str);
91321936Shselasky	return 0;
92321936Shselasky}
93321936Shselasky#else
94321936Shselasky#define is_remote is_loopback
95321936Shselasky#endif
96321936Shselasky
97321936Shselaskyint is_console_enabled(osm_subn_opt_t * p_opt)
98321936Shselasky{
99321936Shselasky	/* checks for a variety of types of consoles - default is off or 0 */
100321936Shselasky	if (p_opt)
101321936Shselasky		return is_local(p_opt->console) || is_loopback(p_opt->console)
102321936Shselasky			|| is_remote(p_opt->console);
103321936Shselasky	return 0;
104321936Shselasky}
105321936Shselasky
106321936Shselasky
107321936Shselasky#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
108321936Shselaskyint cio_close(osm_console_t * p_oct, osm_log_t * p_log)
109321936Shselasky{
110321936Shselasky	int rtnval = -1;
111321936Shselasky	if (p_oct && p_oct->in_fd > 0) {
112321936Shselasky		OSM_LOG(p_log, OSM_LOG_VERBOSE,
113321936Shselasky			"Console connection closed: %s (%s)\n",
114321936Shselasky			p_oct->client_hn, p_oct->client_ip);
115321936Shselasky		rtnval = fclose(p_oct->in);
116321936Shselasky		p_oct->in_fd = -1;
117321936Shselasky		p_oct->out_fd = -1;
118321936Shselasky		p_oct->in = NULL;
119321936Shselasky		p_oct->out = NULL;
120321936Shselasky	}
121321936Shselasky	return rtnval;
122321936Shselasky}
123321936Shselasky
124321936Shselaskyint cio_open(osm_console_t * p_oct, int new_fd, osm_log_t * p_log)
125321936Shselasky{
126321936Shselasky	/* returns zero if opened fine, -1 otherwise */
127321936Shselasky	char *p_line;
128321936Shselasky	size_t len;
129321936Shselasky	ssize_t n;
130321936Shselasky
131321936Shselasky	if (p_oct->in_fd >= 0) {
132321936Shselasky		FILE *file = fdopen(new_fd, "w+");
133321936Shselasky
134321936Shselasky		fprintf(file, "OpenSM Console connection already in use\n"
135321936Shselasky			"   kill other session (y/n)? ");
136321936Shselasky		fflush(file);
137321936Shselasky		p_line = NULL;
138321936Shselasky		n = getline(&p_line, &len, file);
139321936Shselasky		if (n > 0 && (p_line[0] == 'y' || p_line[0] == 'Y'))
140321936Shselasky			cio_close(p_oct, p_log);
141321936Shselasky		else {
142321936Shselasky			OSM_LOG(p_log, OSM_LOG_INFO,
143321936Shselasky				"Console connection aborted: %s (%s) - "
144321936Shselasky				"already in use\n",
145321936Shselasky				p_oct->client_hn, p_oct->client_ip);
146321936Shselasky			fclose(file);
147321936Shselasky			free(p_line);
148321936Shselasky			return -1;
149321936Shselasky		}
150321936Shselasky		free(p_line);
151321936Shselasky	}
152321936Shselasky	p_oct->in_fd = new_fd;
153321936Shselasky	p_oct->out_fd = p_oct->in_fd;
154321936Shselasky	p_oct->in = fdopen(p_oct->in_fd, "w+");
155321936Shselasky	p_oct->out = p_oct->in;
156321936Shselasky	osm_console_prompt(p_oct->out);
157321936Shselasky	OSM_LOG(p_log, OSM_LOG_VERBOSE, "Console connection accepted: %s (%s)\n",
158321936Shselasky		p_oct->client_hn, p_oct->client_ip);
159321936Shselasky
160321936Shselasky	return (p_oct->in == NULL) ? -1 : 0;
161321936Shselasky}
162321936Shselasky
163321936Shselasky/**********************************************************************
164321936Shselasky * Do authentication & authorization check
165321936Shselasky **********************************************************************/
166321936Shselaskyint is_authorized(osm_console_t * p_oct)
167321936Shselasky{
168321936Shselasky	/* allowed to use the console? */
169321936Shselasky	p_oct->authorized = !is_remote(p_oct->client_type) ||
170321936Shselasky	    hosts_ctl((char *)OSM_DAEMON_NAME, p_oct->client_hn, p_oct->client_ip,
171321936Shselasky		      (char *)STRING_UNKNOWN);
172321936Shselasky	return p_oct->authorized;
173321936Shselasky}
174321936Shselasky#endif
175321936Shselasky
176321936Shselaskyvoid osm_console_prompt(FILE * out)
177321936Shselasky{
178321936Shselasky	if (out) {
179321936Shselasky		fprintf(out, "OpenSM %s", OSM_COMMAND_PROMPT);
180321936Shselasky		fflush(out);
181321936Shselasky	}
182321936Shselasky}
183321936Shselasky
184321936Shselaskyint osm_console_init(osm_subn_opt_t * opt, osm_console_t * p_oct, osm_log_t * p_log)
185321936Shselasky{
186321936Shselasky	p_oct->socket = -1;
187321936Shselasky	strncpy(p_oct->client_type, opt->console, sizeof(p_oct->client_type));
188321936Shselasky
189321936Shselasky	/* set up the file descriptors for the console */
190321936Shselasky	if (strcmp(opt->console, OSM_LOCAL_CONSOLE) == 0) {
191321936Shselasky		p_oct->in = stdin;
192321936Shselasky		p_oct->out = stdout;
193321936Shselasky		p_oct->in_fd = fileno(stdin);
194321936Shselasky		p_oct->out_fd = fileno(stdout);
195321936Shselasky
196321936Shselasky		osm_console_prompt(p_oct->out);
197321936Shselasky#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
198321936Shselasky	} else if (strcmp(opt->console, OSM_LOOPBACK_CONSOLE) == 0
199321936Shselasky#ifdef ENABLE_OSM_CONSOLE_SOCKET
200321936Shselasky		   || strcmp(opt->console, OSM_REMOTE_CONSOLE) == 0
201321936Shselasky#endif
202321936Shselasky		   ) {
203321936Shselasky		struct sockaddr_in sin;
204321936Shselasky		int optval = 1;
205321936Shselasky
206321936Shselasky		if ((p_oct->socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
207321936Shselasky			OSM_LOG(p_log, OSM_LOG_ERROR,
208321936Shselasky				"ERR 4B01: Failed to open console socket: %s\n",
209321936Shselasky				strerror(errno));
210321936Shselasky			return -1;
211321936Shselasky		}
212321936Shselasky
213321936Shselasky		if (setsockopt(p_oct->socket, SOL_SOCKET, SO_REUSEADDR,
214321936Shselasky			       &optval, sizeof(optval))) {
215321936Shselasky			OSM_LOG(p_log, OSM_LOG_ERROR,
216321936Shselasky		                "ERR 4B06: Failed to set socket option: %s\n",
217321936Shselasky		                strerror(errno));
218321936Shselasky		        return -1;
219321936Shselasky		}
220321936Shselasky
221321936Shselasky		sin.sin_family = AF_INET;
222321936Shselasky		sin.sin_port = htons(opt->console_port);
223321936Shselasky#ifdef ENABLE_OSM_CONSOLE_SOCKET
224321936Shselasky		if (strcmp(opt->console, OSM_REMOTE_CONSOLE) == 0)
225321936Shselasky			sin.sin_addr.s_addr = htonl(INADDR_ANY);
226321936Shselasky		else
227321936Shselasky#endif
228321936Shselasky			sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
229321936Shselasky		if (bind(p_oct->socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
230321936Shselasky			OSM_LOG(p_log, OSM_LOG_ERROR,
231321936Shselasky				"ERR 4B02: Failed to bind console socket: %s\n",
232321936Shselasky				strerror(errno));
233321936Shselasky			return -1;
234321936Shselasky		}
235321936Shselasky		if (listen(p_oct->socket, 1) < 0) {
236321936Shselasky			OSM_LOG(p_log, OSM_LOG_ERROR,
237321936Shselasky				"ERR 4B03: Failed to listen on console socket: %s\n",
238321936Shselasky				strerror(errno));
239321936Shselasky			return -1;
240321936Shselasky		}
241321936Shselasky
242321936Shselasky		signal(SIGPIPE, SIG_IGN);	/* protect ourselves from closed pipes */
243321936Shselasky		p_oct->in = NULL;
244321936Shselasky		p_oct->out = NULL;
245321936Shselasky		p_oct->in_fd = -1;
246321936Shselasky		p_oct->out_fd = -1;
247321936Shselasky		OSM_LOG(p_log, OSM_LOG_INFO,
248321936Shselasky			"Console listening on port %d\n", opt->console_port);
249321936Shselasky#endif
250321936Shselasky	}
251321936Shselasky
252321936Shselasky	return 0;
253321936Shselasky}
254321936Shselasky
255321936Shselasky/* clean up and release resources */
256321936Shselaskyvoid osm_console_exit(osm_console_t * p_oct, osm_log_t * p_log)
257321936Shselasky{
258321936Shselasky#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
259321936Shselasky	cio_close(p_oct, p_log);
260321936Shselasky	if (p_oct->socket > 0) {
261321936Shselasky		OSM_LOG(p_log, OSM_LOG_INFO, "Closing console socket\n");
262321936Shselasky		close(p_oct->socket);
263321936Shselasky		p_oct->socket = -1;
264321936Shselasky	}
265321936Shselasky#endif
266321936Shselasky}
267