1/*
2 * Copyright (c) 2005-2009 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2008 HNR Consulting. All rights reserved.
4 * Copyright (c) 2013 Oracle and/or its affiliates. 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/*
37 * Abstract:
38 *    Provide a framework for the Console which decouples the connection
39 *    or I/O from the functionality, or commands.
40 *
41 *    Extensible - allows a variety of connection methods independent of
42 *    the console commands.
43 */
44
45#if HAVE_CONFIG_H
46#  include <config.h>
47#endif				/* HAVE_CONFIG_H */
48
49#define _WITH_GETLINE		/* for getline */
50#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
51#include <tcpd.h>
52#include <arpa/inet.h>
53#include <netinet/in.h>
54#include <sys/socket.h>
55#endif
56
57#include <unistd.h>
58#include <errno.h>
59#include <signal.h>
60#include <opensm/osm_file_ids.h>
61#define FILE_ID OSM_FILE_CONSOLE_IO_C
62#include <opensm/osm_console_io.h>
63#include <stdlib.h>
64
65static int is_local(char *str)
66{
67	/* convenience - checks if just stdin/stdout */
68	if (str)
69		return (strcmp(str, OSM_LOCAL_CONSOLE) == 0);
70	return 0;
71}
72
73#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
74static int is_loopback(char *str)
75{
76	/* convenience - checks if socket based connection */
77	if (str)
78		return (strcmp(str, OSM_LOOPBACK_CONSOLE) == 0);
79	return 0;
80}
81#else
82#define is_loopback is_local
83#endif
84
85#ifdef ENABLE_OSM_CONSOLE_SOCKET
86static int is_remote(char *str)
87{
88	/* convenience - checks if socket based connection */
89	if (str)
90		return strcmp(str, OSM_REMOTE_CONSOLE) == 0 || is_loopback(str);
91	return 0;
92}
93#else
94#define is_remote is_loopback
95#endif
96
97int is_console_enabled(osm_subn_opt_t * p_opt)
98{
99	/* checks for a variety of types of consoles - default is off or 0 */
100	if (p_opt)
101		return is_local(p_opt->console) || is_loopback(p_opt->console)
102			|| is_remote(p_opt->console);
103	return 0;
104}
105
106
107#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
108int cio_close(osm_console_t * p_oct, osm_log_t * p_log)
109{
110	int rtnval = -1;
111	if (p_oct && p_oct->in_fd > 0) {
112		OSM_LOG(p_log, OSM_LOG_VERBOSE,
113			"Console connection closed: %s (%s)\n",
114			p_oct->client_hn, p_oct->client_ip);
115		rtnval = fclose(p_oct->in);
116		p_oct->in_fd = -1;
117		p_oct->out_fd = -1;
118		p_oct->in = NULL;
119		p_oct->out = NULL;
120	}
121	return rtnval;
122}
123
124int cio_open(osm_console_t * p_oct, int new_fd, osm_log_t * p_log)
125{
126	/* returns zero if opened fine, -1 otherwise */
127	char *p_line;
128	size_t len;
129	ssize_t n;
130
131	if (p_oct->in_fd >= 0) {
132		FILE *file = fdopen(new_fd, "w+");
133
134		fprintf(file, "OpenSM Console connection already in use\n"
135			"   kill other session (y/n)? ");
136		fflush(file);
137		p_line = NULL;
138		n = getline(&p_line, &len, file);
139		if (n > 0 && (p_line[0] == 'y' || p_line[0] == 'Y'))
140			cio_close(p_oct, p_log);
141		else {
142			OSM_LOG(p_log, OSM_LOG_INFO,
143				"Console connection aborted: %s (%s) - "
144				"already in use\n",
145				p_oct->client_hn, p_oct->client_ip);
146			fclose(file);
147			free(p_line);
148			return -1;
149		}
150		free(p_line);
151	}
152	p_oct->in_fd = new_fd;
153	p_oct->out_fd = p_oct->in_fd;
154	p_oct->in = fdopen(p_oct->in_fd, "w+");
155	p_oct->out = p_oct->in;
156	osm_console_prompt(p_oct->out);
157	OSM_LOG(p_log, OSM_LOG_VERBOSE, "Console connection accepted: %s (%s)\n",
158		p_oct->client_hn, p_oct->client_ip);
159
160	return (p_oct->in == NULL) ? -1 : 0;
161}
162
163/**********************************************************************
164 * Do authentication & authorization check
165 **********************************************************************/
166int is_authorized(osm_console_t * p_oct)
167{
168	/* allowed to use the console? */
169	p_oct->authorized = !is_remote(p_oct->client_type) ||
170	    hosts_ctl((char *)OSM_DAEMON_NAME, p_oct->client_hn, p_oct->client_ip,
171		      (char *)STRING_UNKNOWN);
172	return p_oct->authorized;
173}
174#endif
175
176void osm_console_prompt(FILE * out)
177{
178	if (out) {
179		fprintf(out, "OpenSM %s", OSM_COMMAND_PROMPT);
180		fflush(out);
181	}
182}
183
184int osm_console_init(osm_subn_opt_t * opt, osm_console_t * p_oct, osm_log_t * p_log)
185{
186	p_oct->socket = -1;
187	strncpy(p_oct->client_type, opt->console, sizeof(p_oct->client_type));
188
189	/* set up the file descriptors for the console */
190	if (strcmp(opt->console, OSM_LOCAL_CONSOLE) == 0) {
191		p_oct->in = stdin;
192		p_oct->out = stdout;
193		p_oct->in_fd = fileno(stdin);
194		p_oct->out_fd = fileno(stdout);
195
196		osm_console_prompt(p_oct->out);
197#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
198	} else if (strcmp(opt->console, OSM_LOOPBACK_CONSOLE) == 0
199#ifdef ENABLE_OSM_CONSOLE_SOCKET
200		   || strcmp(opt->console, OSM_REMOTE_CONSOLE) == 0
201#endif
202		   ) {
203		struct sockaddr_in sin;
204		int optval = 1;
205
206		if ((p_oct->socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
207			OSM_LOG(p_log, OSM_LOG_ERROR,
208				"ERR 4B01: Failed to open console socket: %s\n",
209				strerror(errno));
210			return -1;
211		}
212
213		if (setsockopt(p_oct->socket, SOL_SOCKET, SO_REUSEADDR,
214			       &optval, sizeof(optval))) {
215			OSM_LOG(p_log, OSM_LOG_ERROR,
216		                "ERR 4B06: Failed to set socket option: %s\n",
217		                strerror(errno));
218		        return -1;
219		}
220
221		sin.sin_family = AF_INET;
222		sin.sin_port = htons(opt->console_port);
223#ifdef ENABLE_OSM_CONSOLE_SOCKET
224		if (strcmp(opt->console, OSM_REMOTE_CONSOLE) == 0)
225			sin.sin_addr.s_addr = htonl(INADDR_ANY);
226		else
227#endif
228			sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
229		if (bind(p_oct->socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
230			OSM_LOG(p_log, OSM_LOG_ERROR,
231				"ERR 4B02: Failed to bind console socket: %s\n",
232				strerror(errno));
233			return -1;
234		}
235		if (listen(p_oct->socket, 1) < 0) {
236			OSM_LOG(p_log, OSM_LOG_ERROR,
237				"ERR 4B03: Failed to listen on console socket: %s\n",
238				strerror(errno));
239			return -1;
240		}
241
242		signal(SIGPIPE, SIG_IGN);	/* protect ourselves from closed pipes */
243		p_oct->in = NULL;
244		p_oct->out = NULL;
245		p_oct->in_fd = -1;
246		p_oct->out_fd = -1;
247		OSM_LOG(p_log, OSM_LOG_INFO,
248			"Console listening on port %d\n", opt->console_port);
249#endif
250	}
251
252	return 0;
253}
254
255/* clean up and release resources */
256void osm_console_exit(osm_console_t * p_oct, osm_log_t * p_log)
257{
258#ifdef ENABLE_OSM_CONSOLE_LOOPBACK
259	cio_close(p_oct, p_log);
260	if (p_oct->socket > 0) {
261		OSM_LOG(p_log, OSM_LOG_INFO, "Closing console socket\n");
262		close(p_oct->socket);
263		p_oct->socket = -1;
264	}
265#endif
266}
267