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 
65 static 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
74 static 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
86 static 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 
97 int 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
108 int 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 
124 int 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  **********************************************************************/
166 int 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 
176 void osm_console_prompt(FILE * out)
177 {
178 	if (out) {
179 		fprintf(out, "OpenSM %s", OSM_COMMAND_PROMPT);
180 		fflush(out);
181 	}
182 }
183 
184 int 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 */
256 void 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