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
is_local(char * str)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
is_loopback(char * str)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
is_remote(char * str)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
is_console_enabled(osm_subn_opt_t * p_opt)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
cio_close(osm_console_t * p_oct,osm_log_t * p_log)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
cio_open(osm_console_t * p_oct,int new_fd,osm_log_t * p_log)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 **********************************************************************/
is_authorized(osm_console_t * p_oct)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
osm_console_prompt(FILE * out)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
osm_console_init(osm_subn_opt_t * opt,osm_console_t * p_oct,osm_log_t * p_log)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 */
osm_console_exit(osm_console_t * p_oct,osm_log_t * p_log)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