1321936Shselasky/* 2321936Shselasky * Copyright (c) 2005-2009 Voltaire, Inc. All rights reserved. 3321936Shselasky * Copyright (c) 2008 HNR Consulting. All rights reserved. 4321936Shselasky * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. 5321936Shselasky * 6321936Shselasky * This software is available to you under a choice of one of two 7321936Shselasky * licenses. You may choose to be licensed under the terms of the GNU 8321936Shselasky * General Public License (GPL) Version 2, available from the file 9321936Shselasky * COPYING in the main directory of this source tree, or the 10321936Shselasky * OpenIB.org BSD license below: 11321936Shselasky * 12321936Shselasky * Redistribution and use in source and binary forms, with or 13321936Shselasky * without modification, are permitted provided that the following 14321936Shselasky * conditions are met: 15321936Shselasky * 16321936Shselasky * - Redistributions of source code must retain the above 17321936Shselasky * copyright notice, this list of conditions and the following 18321936Shselasky * disclaimer. 19321936Shselasky * 20321936Shselasky * - Redistributions in binary form must reproduce the above 21321936Shselasky * copyright notice, this list of conditions and the following 22321936Shselasky * disclaimer in the documentation and/or other materials 23321936Shselasky * provided with the distribution. 24321936Shselasky * 25321936Shselasky * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26321936Shselasky * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 27321936Shselasky * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28321936Shselasky * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 29321936Shselasky * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 30321936Shselasky * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 31321936Shselasky * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32321936Shselasky * SOFTWARE. 33321936Shselasky * 34321936Shselasky */ 35321936Shselasky 36321936Shselasky/* 37321936Shselasky * Abstract: 38321936Shselasky * Provide a framework for the Console which decouples the connection 39321936Shselasky * or I/O from the functionality, or commands. 40321936Shselasky * 41321936Shselasky * Extensible - allows a variety of connection methods independent of 42321936Shselasky * the console commands. 43321936Shselasky */ 44321936Shselasky 45321936Shselasky#if HAVE_CONFIG_H 46321936Shselasky# include <config.h> 47321936Shselasky#endif /* HAVE_CONFIG_H */ 48321936Shselasky 49321936Shselasky#define _WITH_GETLINE /* for getline */ 50321936Shselasky#ifdef ENABLE_OSM_CONSOLE_LOOPBACK 51321936Shselasky#include <tcpd.h> 52321936Shselasky#include <arpa/inet.h> 53321936Shselasky#include <netinet/in.h> 54321936Shselasky#include <sys/socket.h> 55321936Shselasky#endif 56321936Shselasky 57321936Shselasky#include <unistd.h> 58321936Shselasky#include <errno.h> 59321936Shselasky#include <signal.h> 60321936Shselasky#include <opensm/osm_file_ids.h> 61321936Shselasky#define FILE_ID OSM_FILE_CONSOLE_IO_C 62321936Shselasky#include <opensm/osm_console_io.h> 63321936Shselasky#include <stdlib.h> 64321936Shselasky 65321936Shselaskystatic int is_local(char *str) 66321936Shselasky{ 67321936Shselasky /* convenience - checks if just stdin/stdout */ 68321936Shselasky if (str) 69321936Shselasky return (strcmp(str, OSM_LOCAL_CONSOLE) == 0); 70321936Shselasky return 0; 71321936Shselasky} 72321936Shselasky 73321936Shselasky#ifdef ENABLE_OSM_CONSOLE_LOOPBACK 74321936Shselaskystatic int is_loopback(char *str) 75321936Shselasky{ 76321936Shselasky /* convenience - checks if socket based connection */ 77321936Shselasky if (str) 78321936Shselasky return (strcmp(str, OSM_LOOPBACK_CONSOLE) == 0); 79321936Shselasky return 0; 80321936Shselasky} 81321936Shselasky#else 82321936Shselasky#define is_loopback is_local 83321936Shselasky#endif 84321936Shselasky 85321936Shselasky#ifdef ENABLE_OSM_CONSOLE_SOCKET 86321936Shselaskystatic int is_remote(char *str) 87321936Shselasky{ 88321936Shselasky /* convenience - checks if socket based connection */ 89321936Shselasky if (str) 90321936Shselasky return strcmp(str, OSM_REMOTE_CONSOLE) == 0 || is_loopback(str); 91321936Shselasky return 0; 92321936Shselasky} 93321936Shselasky#else 94321936Shselasky#define is_remote is_loopback 95321936Shselasky#endif 96321936Shselasky 97321936Shselaskyint is_console_enabled(osm_subn_opt_t * p_opt) 98321936Shselasky{ 99321936Shselasky /* checks for a variety of types of consoles - default is off or 0 */ 100321936Shselasky if (p_opt) 101321936Shselasky return is_local(p_opt->console) || is_loopback(p_opt->console) 102321936Shselasky || is_remote(p_opt->console); 103321936Shselasky return 0; 104321936Shselasky} 105321936Shselasky 106321936Shselasky 107321936Shselasky#ifdef ENABLE_OSM_CONSOLE_LOOPBACK 108321936Shselaskyint cio_close(osm_console_t * p_oct, osm_log_t * p_log) 109321936Shselasky{ 110321936Shselasky int rtnval = -1; 111321936Shselasky if (p_oct && p_oct->in_fd > 0) { 112321936Shselasky OSM_LOG(p_log, OSM_LOG_VERBOSE, 113321936Shselasky "Console connection closed: %s (%s)\n", 114321936Shselasky p_oct->client_hn, p_oct->client_ip); 115321936Shselasky rtnval = fclose(p_oct->in); 116321936Shselasky p_oct->in_fd = -1; 117321936Shselasky p_oct->out_fd = -1; 118321936Shselasky p_oct->in = NULL; 119321936Shselasky p_oct->out = NULL; 120321936Shselasky } 121321936Shselasky return rtnval; 122321936Shselasky} 123321936Shselasky 124321936Shselaskyint cio_open(osm_console_t * p_oct, int new_fd, osm_log_t * p_log) 125321936Shselasky{ 126321936Shselasky /* returns zero if opened fine, -1 otherwise */ 127321936Shselasky char *p_line; 128321936Shselasky size_t len; 129321936Shselasky ssize_t n; 130321936Shselasky 131321936Shselasky if (p_oct->in_fd >= 0) { 132321936Shselasky FILE *file = fdopen(new_fd, "w+"); 133321936Shselasky 134321936Shselasky fprintf(file, "OpenSM Console connection already in use\n" 135321936Shselasky " kill other session (y/n)? "); 136321936Shselasky fflush(file); 137321936Shselasky p_line = NULL; 138321936Shselasky n = getline(&p_line, &len, file); 139321936Shselasky if (n > 0 && (p_line[0] == 'y' || p_line[0] == 'Y')) 140321936Shselasky cio_close(p_oct, p_log); 141321936Shselasky else { 142321936Shselasky OSM_LOG(p_log, OSM_LOG_INFO, 143321936Shselasky "Console connection aborted: %s (%s) - " 144321936Shselasky "already in use\n", 145321936Shselasky p_oct->client_hn, p_oct->client_ip); 146321936Shselasky fclose(file); 147321936Shselasky free(p_line); 148321936Shselasky return -1; 149321936Shselasky } 150321936Shselasky free(p_line); 151321936Shselasky } 152321936Shselasky p_oct->in_fd = new_fd; 153321936Shselasky p_oct->out_fd = p_oct->in_fd; 154321936Shselasky p_oct->in = fdopen(p_oct->in_fd, "w+"); 155321936Shselasky p_oct->out = p_oct->in; 156321936Shselasky osm_console_prompt(p_oct->out); 157321936Shselasky OSM_LOG(p_log, OSM_LOG_VERBOSE, "Console connection accepted: %s (%s)\n", 158321936Shselasky p_oct->client_hn, p_oct->client_ip); 159321936Shselasky 160321936Shselasky return (p_oct->in == NULL) ? -1 : 0; 161321936Shselasky} 162321936Shselasky 163321936Shselasky/********************************************************************** 164321936Shselasky * Do authentication & authorization check 165321936Shselasky **********************************************************************/ 166321936Shselaskyint is_authorized(osm_console_t * p_oct) 167321936Shselasky{ 168321936Shselasky /* allowed to use the console? */ 169321936Shselasky p_oct->authorized = !is_remote(p_oct->client_type) || 170321936Shselasky hosts_ctl((char *)OSM_DAEMON_NAME, p_oct->client_hn, p_oct->client_ip, 171321936Shselasky (char *)STRING_UNKNOWN); 172321936Shselasky return p_oct->authorized; 173321936Shselasky} 174321936Shselasky#endif 175321936Shselasky 176321936Shselaskyvoid osm_console_prompt(FILE * out) 177321936Shselasky{ 178321936Shselasky if (out) { 179321936Shselasky fprintf(out, "OpenSM %s", OSM_COMMAND_PROMPT); 180321936Shselasky fflush(out); 181321936Shselasky } 182321936Shselasky} 183321936Shselasky 184321936Shselaskyint osm_console_init(osm_subn_opt_t * opt, osm_console_t * p_oct, osm_log_t * p_log) 185321936Shselasky{ 186321936Shselasky p_oct->socket = -1; 187321936Shselasky strncpy(p_oct->client_type, opt->console, sizeof(p_oct->client_type)); 188321936Shselasky 189321936Shselasky /* set up the file descriptors for the console */ 190321936Shselasky if (strcmp(opt->console, OSM_LOCAL_CONSOLE) == 0) { 191321936Shselasky p_oct->in = stdin; 192321936Shselasky p_oct->out = stdout; 193321936Shselasky p_oct->in_fd = fileno(stdin); 194321936Shselasky p_oct->out_fd = fileno(stdout); 195321936Shselasky 196321936Shselasky osm_console_prompt(p_oct->out); 197321936Shselasky#ifdef ENABLE_OSM_CONSOLE_LOOPBACK 198321936Shselasky } else if (strcmp(opt->console, OSM_LOOPBACK_CONSOLE) == 0 199321936Shselasky#ifdef ENABLE_OSM_CONSOLE_SOCKET 200321936Shselasky || strcmp(opt->console, OSM_REMOTE_CONSOLE) == 0 201321936Shselasky#endif 202321936Shselasky ) { 203321936Shselasky struct sockaddr_in sin; 204321936Shselasky int optval = 1; 205321936Shselasky 206321936Shselasky if ((p_oct->socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 207321936Shselasky OSM_LOG(p_log, OSM_LOG_ERROR, 208321936Shselasky "ERR 4B01: Failed to open console socket: %s\n", 209321936Shselasky strerror(errno)); 210321936Shselasky return -1; 211321936Shselasky } 212321936Shselasky 213321936Shselasky if (setsockopt(p_oct->socket, SOL_SOCKET, SO_REUSEADDR, 214321936Shselasky &optval, sizeof(optval))) { 215321936Shselasky OSM_LOG(p_log, OSM_LOG_ERROR, 216321936Shselasky "ERR 4B06: Failed to set socket option: %s\n", 217321936Shselasky strerror(errno)); 218321936Shselasky return -1; 219321936Shselasky } 220321936Shselasky 221321936Shselasky sin.sin_family = AF_INET; 222321936Shselasky sin.sin_port = htons(opt->console_port); 223321936Shselasky#ifdef ENABLE_OSM_CONSOLE_SOCKET 224321936Shselasky if (strcmp(opt->console, OSM_REMOTE_CONSOLE) == 0) 225321936Shselasky sin.sin_addr.s_addr = htonl(INADDR_ANY); 226321936Shselasky else 227321936Shselasky#endif 228321936Shselasky sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 229321936Shselasky if (bind(p_oct->socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 230321936Shselasky OSM_LOG(p_log, OSM_LOG_ERROR, 231321936Shselasky "ERR 4B02: Failed to bind console socket: %s\n", 232321936Shselasky strerror(errno)); 233321936Shselasky return -1; 234321936Shselasky } 235321936Shselasky if (listen(p_oct->socket, 1) < 0) { 236321936Shselasky OSM_LOG(p_log, OSM_LOG_ERROR, 237321936Shselasky "ERR 4B03: Failed to listen on console socket: %s\n", 238321936Shselasky strerror(errno)); 239321936Shselasky return -1; 240321936Shselasky } 241321936Shselasky 242321936Shselasky signal(SIGPIPE, SIG_IGN); /* protect ourselves from closed pipes */ 243321936Shselasky p_oct->in = NULL; 244321936Shselasky p_oct->out = NULL; 245321936Shselasky p_oct->in_fd = -1; 246321936Shselasky p_oct->out_fd = -1; 247321936Shselasky OSM_LOG(p_log, OSM_LOG_INFO, 248321936Shselasky "Console listening on port %d\n", opt->console_port); 249321936Shselasky#endif 250321936Shselasky } 251321936Shselasky 252321936Shselasky return 0; 253321936Shselasky} 254321936Shselasky 255321936Shselasky/* clean up and release resources */ 256321936Shselaskyvoid osm_console_exit(osm_console_t * p_oct, osm_log_t * p_log) 257321936Shselasky{ 258321936Shselasky#ifdef ENABLE_OSM_CONSOLE_LOOPBACK 259321936Shselasky cio_close(p_oct, p_log); 260321936Shselasky if (p_oct->socket > 0) { 261321936Shselasky OSM_LOG(p_log, OSM_LOG_INFO, "Closing console socket\n"); 262321936Shselasky close(p_oct->socket); 263321936Shselasky p_oct->socket = -1; 264321936Shselasky } 265321936Shselasky#endif 266321936Shselasky} 267