1/*
2 * Copyright (c) 2005-2007 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2008 HNR Consulting. All rights reserved.
4 *
5 * This software is available to you under a choice of one of two
6 * licenses.  You may choose to be licensed under the terms of the GNU
7 * General Public License (GPL) Version 2, available from the file
8 * COPYING in the main directory of this source tree, or the
9 * OpenIB.org BSD license below:
10 *
11 *     Redistribution and use in source and binary forms, with or
12 *     without modification, are permitted provided that the following
13 *     conditions are met:
14 *
15 *      - Redistributions of source code must retain the above
16 *        copyright notice, this list of conditions and the following
17 *        disclaimer.
18 *
19 *      - Redistributions in binary form must reproduce the above
20 *        copyright notice, this list of conditions and the following
21 *        disclaimer in the documentation and/or other materials
22 *        provided with the distribution.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 * SOFTWARE.
32 *
33 */
34
35/*
36 * Abstract:
37 *    Provide a framework for the Console which decouples the connection
38 *    or I/O from the functionality, or commands.
39 *
40 *    Extensible - allows a variety of connection methods independent of
41 *    the console commands.
42 */
43
44#if HAVE_CONFIG_H
45#  include <config.h>
46#endif				/* HAVE_CONFIG_H */
47
48#define _GNU_SOURCE		/* for getline */
49#ifdef ENABLE_OSM_CONSOLE_SOCKET
50#include <tcpd.h>
51#include <arpa/inet.h>
52#include <netinet/in.h>
53#include <sys/socket.h>
54#endif
55#include <unistd.h>
56#include <errno.h>
57#include <signal.h>
58#include <opensm/osm_console_io.h>
59
60static int is_local(char *str)
61{
62	// convenience - checks if just stdin/stdout
63	if (str)
64		return (strcmp(str, OSM_LOCAL_CONSOLE) == 0);
65	return 0;
66}
67
68static int is_loopback(char *str)
69{
70	// convenience - checks if socket based connection
71	if (str)
72		return (strcmp(str, OSM_LOOPBACK_CONSOLE) == 0);
73	return 0;
74}
75
76static int is_remote(char *str)
77{
78	// convenience - checks if socket based connection
79	if (str)
80		return (strcmp(str, OSM_REMOTE_CONSOLE) == 0)
81		    || is_loopback(str);
82	return 0;
83}
84
85int is_console_enabled(osm_subn_opt_t * p_opt)
86{
87	// checks for a variety of types of consoles - default is off or 0
88	if (p_opt)
89		return (is_local(p_opt->console)
90			|| is_loopback(p_opt->console)
91			|| is_remote(p_opt->console));
92	return 0;
93}
94
95
96#ifdef ENABLE_OSM_CONSOLE_SOCKET
97static int cio_close(osm_console_t * p_oct)
98{
99	int rtnval = -1;
100	if (p_oct && (p_oct->in_fd > 0)) {
101		rtnval = close(p_oct->in_fd);
102		p_oct->in_fd = -1;
103		p_oct->out_fd = -1;
104		p_oct->in = NULL;
105		p_oct->out = NULL;
106	}
107	return rtnval;
108}
109#endif
110
111/* close the connection */
112static void osm_console_close(osm_console_t * p_oct, osm_log_t * p_log)
113{
114#ifdef ENABLE_OSM_CONSOLE_SOCKET
115	if ((p_oct->socket > 0) && (p_oct->in_fd != -1)) {
116		OSM_LOG(p_log, OSM_LOG_INFO,
117			"Console connection closed: %s (%s)\n",
118			p_oct->client_hn, p_oct->client_ip);
119		cio_close(p_oct);
120	}
121	if (p_oct->socket > 0) {
122		close(p_oct->socket);
123		p_oct->socket = -1;
124	}
125#endif
126}
127
128
129/**********************************************************************
130 * Do authentication & authorization check
131 **********************************************************************/
132#ifdef ENABLE_OSM_CONSOLE_SOCKET
133int is_authorized(osm_console_t * p_oct)
134{
135	/* allowed to use the console? */
136	p_oct->authorized = !is_remote(p_oct->client_type) ||
137	    hosts_ctl(OSM_DAEMON_NAME, p_oct->client_hn, p_oct->client_ip,
138		      "STRING_UNKNOWN");
139	return p_oct->authorized;
140}
141#endif
142
143void osm_console_prompt(FILE * out)
144{
145	if (out) {
146		fprintf(out, "OpenSM %s", OSM_COMMAND_PROMPT);
147		fflush(out);
148	}
149}
150
151int osm_console_init(osm_subn_opt_t * opt, osm_console_t * p_oct, osm_log_t * p_log)
152{
153	p_oct->socket = -1;
154	strncpy(p_oct->client_type, opt->console, sizeof(p_oct->client_type));
155
156	/* set up the file descriptors for the console */
157	if (strcmp(opt->console, OSM_LOCAL_CONSOLE) == 0) {
158		p_oct->in = stdin;
159		p_oct->out = stdout;
160		p_oct->in_fd = fileno(stdin);
161		p_oct->out_fd = fileno(stdout);
162
163		osm_console_prompt(p_oct->out);
164#ifdef ENABLE_OSM_CONSOLE_SOCKET
165	} else if (strcmp(opt->console, OSM_REMOTE_CONSOLE) == 0
166		   || strcmp(opt->console, OSM_LOOPBACK_CONSOLE) == 0) {
167		struct sockaddr_in sin;
168		int optval = 1;
169
170		if ((p_oct->socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
171			OSM_LOG(p_log, OSM_LOG_ERROR,
172				"ERR 4B01: Failed to open console socket: %s\n",
173				strerror(errno));
174			return -1;
175		}
176		setsockopt(p_oct->socket, SOL_SOCKET, SO_REUSEADDR,
177			   &optval, sizeof(optval));
178		sin.sin_family = AF_INET;
179		sin.sin_port = htons(opt->console_port);
180		if (strcmp(opt->console, OSM_REMOTE_CONSOLE) == 0)
181			sin.sin_addr.s_addr = htonl(INADDR_ANY);
182		else
183			sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
184		if (bind(p_oct->socket, &sin, sizeof(sin)) < 0) {
185			OSM_LOG(p_log, OSM_LOG_ERROR,
186				"ERR 4B02: Failed to bind console socket: %s\n",
187				strerror(errno));
188			return -1;
189		}
190		if (listen(p_oct->socket, 1) < 0) {
191			OSM_LOG(p_log, OSM_LOG_ERROR,
192				"ERR 4B03: Failed to listen on socket: %s\n",
193				strerror(errno));
194			return -1;
195		}
196
197		signal(SIGPIPE, SIG_IGN);	/* protect ourselves from closed pipes */
198		p_oct->in = NULL;
199		p_oct->out = NULL;
200		p_oct->in_fd = -1;
201		p_oct->out_fd = -1;
202		OSM_LOG(p_log, OSM_LOG_INFO,
203			"Console listening on port %d\n", opt->console_port);
204#endif
205	}
206
207	return 0;
208}
209
210/* clean up and release resources */
211void osm_console_exit(osm_console_t * p_oct, osm_log_t * p_log)
212{
213	// clean up and release resources, currently just close the socket
214	osm_console_close(p_oct, p_log);
215}
216
217#ifdef ENABLE_OSM_CONSOLE_SOCKET
218int cio_open(osm_console_t * p_oct, int new_fd, osm_log_t * p_log)
219{
220	// returns zero if opened fine, -1 otherwise
221	char *p_line;
222	size_t len;
223	ssize_t n;
224
225	if (p_oct->in_fd >= 0) {
226		FILE *file = fdopen(new_fd, "w+");
227
228		fprintf(file, "OpenSM Console connection already in use\n"
229			"   kill other session (y/n)? ");
230		fflush(file);
231		p_line = NULL;
232		n = getline(&p_line, &len, file);
233		if (n > 0 && (p_line[0] == 'y' || p_line[0] == 'Y')) {
234			osm_console_close(p_oct, p_log);
235		} else {
236			OSM_LOG(p_log, OSM_LOG_INFO,
237				"Console connection aborted: %s (%s)\n",
238				p_oct->client_hn, p_oct->client_ip);
239			close(new_fd);
240			return -1;
241		}
242	}
243	p_oct->in_fd = new_fd;
244	p_oct->out_fd = p_oct->in_fd;
245	p_oct->in = fdopen(p_oct->in_fd, "w+");
246	p_oct->out = p_oct->in;
247	osm_console_prompt(p_oct->out);
248	OSM_LOG(p_log, OSM_LOG_INFO,
249		"Console connection accepted: %s (%s)\n",
250		p_oct->client_hn, p_oct->client_ip);
251
252	return (p_oct->in == NULL) ? -1 : 0;
253}
254#endif
255