1219820Sjeff/*
2219820Sjeff * Copyright (c) 2005-2007 Voltaire, Inc. All rights reserved.
3219820Sjeff * Copyright (c) 2008 HNR Consulting. All rights reserved.
4219820Sjeff *
5219820Sjeff * This software is available to you under a choice of one of two
6219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
7219820Sjeff * General Public License (GPL) Version 2, available from the file
8219820Sjeff * COPYING in the main directory of this source tree, or the
9219820Sjeff * OpenIB.org BSD license below:
10219820Sjeff *
11219820Sjeff *     Redistribution and use in source and binary forms, with or
12219820Sjeff *     without modification, are permitted provided that the following
13219820Sjeff *     conditions are met:
14219820Sjeff *
15219820Sjeff *      - Redistributions of source code must retain the above
16219820Sjeff *        copyright notice, this list of conditions and the following
17219820Sjeff *        disclaimer.
18219820Sjeff *
19219820Sjeff *      - Redistributions in binary form must reproduce the above
20219820Sjeff *        copyright notice, this list of conditions and the following
21219820Sjeff *        disclaimer in the documentation and/or other materials
22219820Sjeff *        provided with the distribution.
23219820Sjeff *
24219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31219820Sjeff * SOFTWARE.
32219820Sjeff *
33219820Sjeff */
34219820Sjeff
35219820Sjeff/*
36219820Sjeff * Abstract:
37219820Sjeff *    Provide a framework for the Console which decouples the connection
38219820Sjeff *    or I/O from the functionality, or commands.
39219820Sjeff *
40219820Sjeff *    Extensible - allows a variety of connection methods independent of
41219820Sjeff *    the console commands.
42219820Sjeff */
43219820Sjeff
44219820Sjeff#if HAVE_CONFIG_H
45219820Sjeff#  include <config.h>
46219820Sjeff#endif				/* HAVE_CONFIG_H */
47219820Sjeff
48219820Sjeff#define _GNU_SOURCE		/* for getline */
49219820Sjeff#ifdef ENABLE_OSM_CONSOLE_SOCKET
50219820Sjeff#include <tcpd.h>
51219820Sjeff#include <arpa/inet.h>
52219820Sjeff#include <netinet/in.h>
53219820Sjeff#include <sys/socket.h>
54219820Sjeff#endif
55219820Sjeff#include <unistd.h>
56219820Sjeff#include <errno.h>
57219820Sjeff#include <signal.h>
58219820Sjeff#include <opensm/osm_console_io.h>
59219820Sjeff
60219820Sjeffstatic int is_local(char *str)
61219820Sjeff{
62219820Sjeff	// convenience - checks if just stdin/stdout
63219820Sjeff	if (str)
64219820Sjeff		return (strcmp(str, OSM_LOCAL_CONSOLE) == 0);
65219820Sjeff	return 0;
66219820Sjeff}
67219820Sjeff
68219820Sjeffstatic int is_loopback(char *str)
69219820Sjeff{
70219820Sjeff	// convenience - checks if socket based connection
71219820Sjeff	if (str)
72219820Sjeff		return (strcmp(str, OSM_LOOPBACK_CONSOLE) == 0);
73219820Sjeff	return 0;
74219820Sjeff}
75219820Sjeff
76219820Sjeffstatic int is_remote(char *str)
77219820Sjeff{
78219820Sjeff	// convenience - checks if socket based connection
79219820Sjeff	if (str)
80219820Sjeff		return (strcmp(str, OSM_REMOTE_CONSOLE) == 0)
81219820Sjeff		    || is_loopback(str);
82219820Sjeff	return 0;
83219820Sjeff}
84219820Sjeff
85219820Sjeffint is_console_enabled(osm_subn_opt_t * p_opt)
86219820Sjeff{
87219820Sjeff	// checks for a variety of types of consoles - default is off or 0
88219820Sjeff	if (p_opt)
89219820Sjeff		return (is_local(p_opt->console)
90219820Sjeff			|| is_loopback(p_opt->console)
91219820Sjeff			|| is_remote(p_opt->console));
92219820Sjeff	return 0;
93219820Sjeff}
94219820Sjeff
95219820Sjeff
96219820Sjeff#ifdef ENABLE_OSM_CONSOLE_SOCKET
97219820Sjeffstatic int cio_close(osm_console_t * p_oct)
98219820Sjeff{
99219820Sjeff	int rtnval = -1;
100219820Sjeff	if (p_oct && (p_oct->in_fd > 0)) {
101219820Sjeff		rtnval = close(p_oct->in_fd);
102219820Sjeff		p_oct->in_fd = -1;
103219820Sjeff		p_oct->out_fd = -1;
104219820Sjeff		p_oct->in = NULL;
105219820Sjeff		p_oct->out = NULL;
106219820Sjeff	}
107219820Sjeff	return rtnval;
108219820Sjeff}
109219820Sjeff#endif
110219820Sjeff
111219820Sjeff/* close the connection */
112219820Sjeffstatic void osm_console_close(osm_console_t * p_oct, osm_log_t * p_log)
113219820Sjeff{
114219820Sjeff#ifdef ENABLE_OSM_CONSOLE_SOCKET
115219820Sjeff	if ((p_oct->socket > 0) && (p_oct->in_fd != -1)) {
116219820Sjeff		OSM_LOG(p_log, OSM_LOG_INFO,
117219820Sjeff			"Console connection closed: %s (%s)\n",
118219820Sjeff			p_oct->client_hn, p_oct->client_ip);
119219820Sjeff		cio_close(p_oct);
120219820Sjeff	}
121219820Sjeff	if (p_oct->socket > 0) {
122219820Sjeff		close(p_oct->socket);
123219820Sjeff		p_oct->socket = -1;
124219820Sjeff	}
125219820Sjeff#endif
126219820Sjeff}
127219820Sjeff
128219820Sjeff
129219820Sjeff/**********************************************************************
130219820Sjeff * Do authentication & authorization check
131219820Sjeff **********************************************************************/
132219820Sjeff#ifdef ENABLE_OSM_CONSOLE_SOCKET
133219820Sjeffint is_authorized(osm_console_t * p_oct)
134219820Sjeff{
135219820Sjeff	/* allowed to use the console? */
136219820Sjeff	p_oct->authorized = !is_remote(p_oct->client_type) ||
137219820Sjeff	    hosts_ctl(OSM_DAEMON_NAME, p_oct->client_hn, p_oct->client_ip,
138219820Sjeff		      "STRING_UNKNOWN");
139219820Sjeff	return p_oct->authorized;
140219820Sjeff}
141219820Sjeff#endif
142219820Sjeff
143219820Sjeffvoid osm_console_prompt(FILE * out)
144219820Sjeff{
145219820Sjeff	if (out) {
146219820Sjeff		fprintf(out, "OpenSM %s", OSM_COMMAND_PROMPT);
147219820Sjeff		fflush(out);
148219820Sjeff	}
149219820Sjeff}
150219820Sjeff
151219820Sjeffint osm_console_init(osm_subn_opt_t * opt, osm_console_t * p_oct, osm_log_t * p_log)
152219820Sjeff{
153219820Sjeff	p_oct->socket = -1;
154219820Sjeff	strncpy(p_oct->client_type, opt->console, sizeof(p_oct->client_type));
155219820Sjeff
156219820Sjeff	/* set up the file descriptors for the console */
157219820Sjeff	if (strcmp(opt->console, OSM_LOCAL_CONSOLE) == 0) {
158219820Sjeff		p_oct->in = stdin;
159219820Sjeff		p_oct->out = stdout;
160219820Sjeff		p_oct->in_fd = fileno(stdin);
161219820Sjeff		p_oct->out_fd = fileno(stdout);
162219820Sjeff
163219820Sjeff		osm_console_prompt(p_oct->out);
164219820Sjeff#ifdef ENABLE_OSM_CONSOLE_SOCKET
165219820Sjeff	} else if (strcmp(opt->console, OSM_REMOTE_CONSOLE) == 0
166219820Sjeff		   || strcmp(opt->console, OSM_LOOPBACK_CONSOLE) == 0) {
167219820Sjeff		struct sockaddr_in sin;
168219820Sjeff		int optval = 1;
169219820Sjeff
170219820Sjeff		if ((p_oct->socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
171219820Sjeff			OSM_LOG(p_log, OSM_LOG_ERROR,
172219820Sjeff				"ERR 4B01: Failed to open console socket: %s\n",
173219820Sjeff				strerror(errno));
174219820Sjeff			return -1;
175219820Sjeff		}
176219820Sjeff		setsockopt(p_oct->socket, SOL_SOCKET, SO_REUSEADDR,
177219820Sjeff			   &optval, sizeof(optval));
178219820Sjeff		sin.sin_family = AF_INET;
179219820Sjeff		sin.sin_port = htons(opt->console_port);
180219820Sjeff		if (strcmp(opt->console, OSM_REMOTE_CONSOLE) == 0)
181219820Sjeff			sin.sin_addr.s_addr = htonl(INADDR_ANY);
182219820Sjeff		else
183219820Sjeff			sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
184219820Sjeff		if (bind(p_oct->socket, &sin, sizeof(sin)) < 0) {
185219820Sjeff			OSM_LOG(p_log, OSM_LOG_ERROR,
186219820Sjeff				"ERR 4B02: Failed to bind console socket: %s\n",
187219820Sjeff				strerror(errno));
188219820Sjeff			return -1;
189219820Sjeff		}
190219820Sjeff		if (listen(p_oct->socket, 1) < 0) {
191219820Sjeff			OSM_LOG(p_log, OSM_LOG_ERROR,
192219820Sjeff				"ERR 4B03: Failed to listen on socket: %s\n",
193219820Sjeff				strerror(errno));
194219820Sjeff			return -1;
195219820Sjeff		}
196219820Sjeff
197219820Sjeff		signal(SIGPIPE, SIG_IGN);	/* protect ourselves from closed pipes */
198219820Sjeff		p_oct->in = NULL;
199219820Sjeff		p_oct->out = NULL;
200219820Sjeff		p_oct->in_fd = -1;
201219820Sjeff		p_oct->out_fd = -1;
202219820Sjeff		OSM_LOG(p_log, OSM_LOG_INFO,
203219820Sjeff			"Console listening on port %d\n", opt->console_port);
204219820Sjeff#endif
205219820Sjeff	}
206219820Sjeff
207219820Sjeff	return 0;
208219820Sjeff}
209219820Sjeff
210219820Sjeff/* clean up and release resources */
211219820Sjeffvoid osm_console_exit(osm_console_t * p_oct, osm_log_t * p_log)
212219820Sjeff{
213219820Sjeff	// clean up and release resources, currently just close the socket
214219820Sjeff	osm_console_close(p_oct, p_log);
215219820Sjeff}
216219820Sjeff
217219820Sjeff#ifdef ENABLE_OSM_CONSOLE_SOCKET
218219820Sjeffint cio_open(osm_console_t * p_oct, int new_fd, osm_log_t * p_log)
219219820Sjeff{
220219820Sjeff	// returns zero if opened fine, -1 otherwise
221219820Sjeff	char *p_line;
222219820Sjeff	size_t len;
223219820Sjeff	ssize_t n;
224219820Sjeff
225219820Sjeff	if (p_oct->in_fd >= 0) {
226219820Sjeff		FILE *file = fdopen(new_fd, "w+");
227219820Sjeff
228219820Sjeff		fprintf(file, "OpenSM Console connection already in use\n"
229219820Sjeff			"   kill other session (y/n)? ");
230219820Sjeff		fflush(file);
231219820Sjeff		p_line = NULL;
232219820Sjeff		n = getline(&p_line, &len, file);
233219820Sjeff		if (n > 0 && (p_line[0] == 'y' || p_line[0] == 'Y')) {
234219820Sjeff			osm_console_close(p_oct, p_log);
235219820Sjeff		} else {
236219820Sjeff			OSM_LOG(p_log, OSM_LOG_INFO,
237219820Sjeff				"Console connection aborted: %s (%s)\n",
238219820Sjeff				p_oct->client_hn, p_oct->client_ip);
239219820Sjeff			close(new_fd);
240219820Sjeff			return -1;
241219820Sjeff		}
242219820Sjeff	}
243219820Sjeff	p_oct->in_fd = new_fd;
244219820Sjeff	p_oct->out_fd = p_oct->in_fd;
245219820Sjeff	p_oct->in = fdopen(p_oct->in_fd, "w+");
246219820Sjeff	p_oct->out = p_oct->in;
247219820Sjeff	osm_console_prompt(p_oct->out);
248219820Sjeff	OSM_LOG(p_log, OSM_LOG_INFO,
249219820Sjeff		"Console connection accepted: %s (%s)\n",
250219820Sjeff		p_oct->client_hn, p_oct->client_ip);
251219820Sjeff
252219820Sjeff	return (p_oct->in == NULL) ? -1 : 0;
253219820Sjeff}
254219820Sjeff#endif
255