/*
* Copyright(c) 2010 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Maintained at www.Open-FCoE.org
*/
#include <libgen.h>
#include <paths.h>
#include <net/if.h>
#include <sys/un.h>
#include <getopt.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "fcoe_utils.h"
#include "fcoe_utils_version.h"
#include "fcoe_clif.h"
#include "fcoeadm_display.h"
static const char optstring[] = "cdrSiftlm:sbhpv";
static const struct option fcoeadm_opts[] = {
{"create", no_argument, 0, 'c'},
{"destroy", no_argument, 0, 'd'},
{"reset", no_argument, 0, 'r'},
{"interface", no_argument, 0, 'i'},
{"Scan", no_argument, 0, 'S'},
{"fcf", no_argument, 0, 'f'},
{"target", no_argument, 0, 't'},
{"lun", no_argument, 0, 'l'},
{"mode", required_argument, 0, 'm'},
{"pid", no_argument, 0, 'p'},
{"stats", no_argument, 0, 's'},
{"lesb", no_argument, 0, 'b'},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'v'},
{0, 0, 0, 0}
};
char progname[20];
static void fcoeadm_help(void)
{
printf("Version %s\n", FCOE_UTILS_VERSION);
printf("Usage: %s\n"
"\t [-m|--mode fabric|vn2vn] [-c|--create] <ethX>\n"
"\t [-d|--destroy] <ethX>\n"
"\t [-r|--reset] <ethX>\n"
"\t [-S|--Scan] <ethX>\n"
"\t [-i|--interface] [<ethX>]\n"
"\t [-f]--fcf] [<ethX>]\n"
"\t [-t|--target] [<ethX>]\n"
"\t [-l|--lun] [<ethX>]\n"
"\t [-s|--stats] <ethX> [<interval>]\n"
"\t [-b|--lesb] <ethX> [<interval>]\n"
"\t [-p|--pid]\n"
"\t [-v|--version]\n"
"\t [-h|--help]\n\n", progname);
}
static enum fcoe_status fcoeadm_clif_request(struct clif_sock_info *clif_info,
const struct clif_data *cmd,
size_t cmd_len, char *reply,
size_t *reply_len)
{
struct timeval tv;
int ret;
fd_set rfds;
if (send(clif_info->socket_fd, cmd, cmd_len, 0) < 0)
return ENOMONCONN;
tv.tv_sec = CLIF_CMD_RESPONSE_TIMEOUT;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(clif_info->socket_fd, &rfds);
ret = select(clif_info->socket_fd + 1, &rfds, NULL, NULL, &tv);
if (FD_ISSET(clif_info->socket_fd, &rfds)) {
ret = recv(clif_info->socket_fd, reply, *reply_len, 0);
if (ret < 0)
return EINTERR;
*reply_len = ret;
return SUCCESS;
} else {
return EINTERR;
}
}
static int fcoeadm_request(struct clif_sock_info *clif_info,
struct clif_data *data)
{
char rbuf[MAX_MSGBUF];
size_t len;
int rc = SUCCESS;
/*
* TODO: This is odd that we read the response code back as a
* string. We should just write the error code into a member
* of clif_data and then just read it directly.
*/
len = MAX_MSGBUF - 1;
rc = fcoeadm_clif_request(clif_info, data, sizeof(struct clif_data),
rbuf, &len);
if (!rc) {
rbuf[len] = '\0';
rc = atoi(rbuf);
}
return rc;
}
static inline void fcoeadm_close_cli(struct clif_sock_info *clif_info)
{
close(clif_info->socket_fd);
}
/*
* Create fcoeadm client interface
*/
static enum fcoe_status fcoeadm_open_cli(struct clif_sock_info *clif_info)
{
enum fcoe_status rc = SUCCESS;
struct sockaddr_un *lp;
socklen_t addrlen;
clif_info->socket_fd = socket(AF_LOCAL, SOCK_DGRAM, 0);
if (clif_info->socket_fd < 0)
return ENOMONCONN;
lp = &clif_info->local;
memset(lp, 0, sizeof(*lp));
lp->sun_family = AF_LOCAL;
lp->sun_path[0] = '\0';
snprintf(&lp->sun_path[1], sizeof(lp->sun_path) - 1,
"%s/%lu", CLIF_IFNAME, (unsigned long int)getpid);
addrlen = sizeof(sa_family_t) + strlen(lp->sun_path + 1) + 1;
if (bind(clif_info->socket_fd, (struct sockaddr *)lp, addrlen) < 0) {
rc = ENOMONCONN;
goto err_close;
}
clif_info->dest.sun_family = AF_LOCAL;
clif_info->dest.sun_path[0] = '\0';
snprintf(&clif_info->dest.sun_path[1],
sizeof(clif_info->dest.sun_path) - 1,
"%s", CLIF_IFNAME);
addrlen = sizeof(sa_family_t) + strlen(clif_info->dest.sun_path + 1) + 1;
if (connect(clif_info->socket_fd, (struct sockaddr *)&clif_info->dest,
addrlen) < 0) {
rc = ENOMONCONN;
goto err_close;
}
return rc;
err_close:
close(clif_info->socket_fd);
return rc;
}
/*
* Send request to fcoemon
*/
static enum fcoe_status
fcoeadm_action(enum clif_action cmd, char *ifname, enum clif_flags flags)
{
struct clif_data data;
struct clif_sock_info clif_info;
int rc;
if (ifname) {
strncpy(data.ifname, ifname, IFNAMSIZ);
data.ifname[IFNAMSIZ - 1] = '\0';
} else
data.ifname[0] = '\0';
data.cmd = cmd;
data.flags = flags;
rc = fcoeadm_open_cli(&clif_info);
if (!rc) {
rc = fcoeadm_request(&clif_info, &data);
if (rc > 0 && cmd == CLIF_PID_CMD) {
printf("%d\n", rc);
rc = 0;
}
fcoeadm_close_cli(&clif_info);
}
return rc;
}
#define MAX_ARG_LEN 32
/*
* getopts_long(3) does not handle optional arguments
* correctly. It will not allow a ' ' between the option
* and its argument. For required arguments the user can
* specify, '-i X' or '-iX' but with optional arguments
* only the first style is valid.
*
* This is being worked around by making '-i/-t/-l' have
* no arguments, but then process any following argv
* elements.
*/
int main(int argc, char *argv[])
{
enum clif_action cmd = CLIF_NONE;
enum fcoe_status rc = SUCCESS;
enum clif_flags flags = CLIF_FLAGS_NONE;
int opt, stat_interval;
int op = -1;
char *ifname = NULL;
/*
* This has to be first because the error print macro
* expects progname to be valid.
*/
strncpy(progname, basename(argv[0]), sizeof(progname));
progname[sizeof(progname) - 1] = '\0';
/* check if we have sysfs */
if (fcoe_checkdir(SYSFS_MOUNT)) {
rc = ENOSYSFS;
goto err;
}
for (;;) {
opt = getopt_long(argc, argv, optstring, fcoeadm_opts, NULL);
if (opt < 0)
break;
switch (opt) {
case 'm':
if (strcasecmp(optarg, "vn2vn") == 0) {
flags &= ~CLIF_FLAGS_MODE_MASK;
flags |= CLIF_FLAGS_VN2VN;
} else if (strcasecmp(optarg, "fabric") == 0) {
flags &= ~CLIF_FLAGS_MODE_MASK;
} else {
rc = EINVALARG;
}
break;
default:
if (op == -1)
op = opt;
else
rc = EINVALARG;
break;
case '?':
rc = EIGNORE;
break;
}
}
if (op == -1)
fcoeadm_help();
else if (rc == SUCCESS) {
switch (op) {
case 'd':
cmd = CLIF_DESTROY_CMD;
flags = 0; /* No flags allowed on destroy yet */
/* fall through */
case 'c':
if (cmd == CLIF_NONE)
cmd = CLIF_CREATE_CMD;
if (argc - optind != 1) {
rc = EBADNUMARGS;
break;
}
ifname = argv[optind];
rc = fcoeadm_action(cmd, ifname, flags);
break;
case 'r':
cmd = CLIF_RESET_CMD;
/* fall through */
case 'S':
if (cmd == CLIF_NONE)
cmd = CLIF_SCAN_CMD;
if (argc - optind != 1) {
rc = EBADNUMARGS;
break;
}
ifname = argv[optind];
rc = fcoe_validate_fcoe_conn(ifname);
if (!rc)
rc = fcoeadm_action(cmd, ifname, flags);
break;
case 'i':
if (argc - optind > 1) {
rc = EBADNUMARGS;
break;
}
/*
* If there's an additional argument
* treat it as the interface name.
*/
if (optind != argc) {
ifname = argv[optind];
rc = fcoe_validate_fcoe_conn(ifname);
}
if (!rc)
rc = display_adapter_info(ifname);
break;
case 'f':
if (argc - optind > 1) {
rc = EBADNUMARGS;
break;
}
/*
* If there's an aditional argument
* treat it as the interface name.
*/
if (optind != argc) {
ifname = argv[optind];
rc = fcoe_validate_fcoe_conn(ifname);
}
if (!rc)
rc = display_fcf_info(ifname);
break;
case 't':
if (argc - optind > 1) {
rc = EBADNUMARGS;
break;
}
/*
* If there's an aditional argument
* treat it as the interface name.
*/
if (optind != argc) {
ifname = argv[optind];
rc = fcoe_validate_fcoe_conn(ifname);
}
if (!rc)
rc = display_target_info(ifname, DISP_TARG);
break;
case 'l':
if (argc - optind > 1) {
rc = EBADNUMARGS;
break;
}
/*
* If there's an aditional argument
* treat it as the interface name.
*/
if (optind != argc) {
ifname = argv[optind];
rc = fcoe_validate_fcoe_conn(ifname);
}
if (!rc)
rc = display_target_info(ifname, DISP_LUN);
break;
case 's':
if (argc - optind > 2) {
rc = EBADNUMARGS;
break;
}
if (optind != argc) {
ifname = argv[optind];
rc = fcoe_validate_fcoe_conn(ifname);
}
if (!rc && ++optind != argc) {
stat_interval = atoi(argv[optind]);
if (stat_interval <= 0)
rc = EINVALARG;
} else if (!rc && optind == argc)
stat_interval = DEFAULT_STATS_INTERVAL;
if (!rc)
rc = display_port_stats(ifname, stat_interval);
break;
case 'p':
rc = fcoeadm_action(CLIF_PID_CMD, NULL, flags);
break;
case 'b':
if (argc - optind > 2) {
rc = EBADNUMARGS;
break;
}
if (optind != argc) {
ifname = argv[optind];
rc = fcoe_validate_fcoe_conn(ifname);
}
if (!rc && ++optind != argc) {
stat_interval = atoi(argv[optind]);
if (stat_interval <= 0)
rc = EINVALARG;
} else if (!rc && optind == argc)
stat_interval = DEFAULT_STATS_INTERVAL;
if (!rc)
rc = display_port_lesb_stats(ifname,
stat_interval);
break;
case 'v':
if (argc - optind != 0) {
rc = EBADNUMARGS;
break;
}
printf("%s\n", FCOE_UTILS_VERSION);
break;
case 'h':
if (argc - optind != 0) {
rc = EBADNUMARGS;
break;
}
fcoeadm_help();
break;
case '?':
rc = EIGNORE;
break;
}
}
err:
if (rc) {
switch (rc) {
case EFAIL:
FCOE_LOG_ERR("Command failed\n");
break;
case ENOACTION:
FCOE_LOG_ERR("No action was taken\n");
break;
case EFCOECONN:
FCOE_LOG_ERR("Connection already created on "
"interface %s\n", ifname);
break;
case ENOFCOECONN:
case ENOFCHOST:
FCOE_LOG_ERR("No connection created on "
"interface %s\n", ifname);
break;
case EINVALARG:
FCOE_LOG_ERR("Invalid argument\n");
break;
case EBADNUMARGS:
/*
* Overloading E2BIG for too many argumets
* and too few arguments.
*/
FCOE_LOG_ERR("Incorrect number of arguments\n");
break;
case EIGNORE:
/*
* getopt_long will print the initial error, just break
* through to get the --help suggestion.
*/
break;
case ENOETHDEV:
FCOE_LOG_ERR("Invalid interface name %s\n", ifname);
break;
case ENOSYSFS:
FCOE_LOG_ERR("sysfs not mounted\n");
break;
case ENOMONCONN:
FCOE_LOG_ERR("Could not connect to fcoemon\n");
break;
case ECONNTMOUT:
FCOE_LOG_ERR("Connection to fcoemon timed out\n");
break;
case EHBAAPIERR:
FCOE_LOG_ERR("libHBAAPI or libhbalinux error\n");
break;
case EINTERR:
FCOE_LOG_ERR("Internal error\n");
break;
case EBADCLIFMSG:
FCOE_LOG_ERR("Messaging error\n");
break;
default:
/*
* This will catch EOPNOTSUPP which should never happen
*/
FCOE_LOG_ERR("Unknown error code %d\n", rc);
break;
}
fprintf(stderr, "Try \'%s --help\' for more information.\n",
progname);
}
return rc;
}