Blame libopeniscsiusr/misc.c

Packit eace71
/*
Packit eace71
 * Copyright (C) 2017 Red Hat, Inc.
Packit eace71
 *
Packit eace71
 * This program is free software: you can redistribute it and/or modify
Packit eace71
 * it under the terms of the GNU General Public License as published by
Packit eace71
 * the Free Software Foundation, either version 3 of the License, or
Packit eace71
 * (at your option) any later version.
Packit eace71
 *
Packit eace71
 * This program is distributed in the hope that it will be useful,
Packit eace71
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit eace71
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit eace71
 * GNU General Public License for more details.
Packit eace71
 *
Packit eace71
 * You should have received a copy of the GNU General Public License
Packit eace71
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit eace71
 *
Packit eace71
 * Author: Gris Ge <fge@redhat.com>
Packit eace71
 */
Packit eace71
Packit eace71
#include <stdint.h>
Packit eace71
#include <stdlib.h>
Packit eace71
#include <stdio.h>
Packit eace71
#include <errno.h>
Packit eace71
#include <dirent.h>
Packit eace71
#include <string.h>
Packit eace71
#include <unistd.h>
Packit eace71
#include <net/if.h>
Packit eace71
#include <sys/socket.h>
Packit eace71
#include <linux/ethtool.h>
Packit eace71
#include <sys/ioctl.h>
Packit eace71
#include <linux/sockios.h>
Packit eace71
#include <net/if_arp.h>
Packit eace71
Packit eace71
#include "libopeniscsiusr/libopeniscsiusr.h"
Packit eace71
#include "misc.h"
Packit eace71
#include "context.h"
Packit eace71
Packit eace71
#define _UNUSED(x) (void)(x)
Packit eace71
Packit eace71
#define _ISCSI_LOG_STRERR_ALIGN_WIDTH	80
Packit eace71
/* ^ Only used in _iscsi_log_stderr() for pretty log output.
Packit eace71
 *   When provided log message is less than 80 bytes, fill it with space, then
Packit eace71
 *   print code file name, function name, line after the 80th bytes.
Packit eace71
 */
Packit eace71
Packit eace71
struct _num_str_conv {
Packit eace71
	const uint32_t value;
Packit eace71
	const char *str;
Packit eace71
};
Packit eace71
Packit eace71
#define _iscsi_str_func_gen(func_name, var_type, var, conv_array) \
Packit eace71
const char *func_name(var_type var) { \
Packit eace71
	size_t i = 0; \
Packit eace71
	uint32_t tmp_var = var & UINT32_MAX; \
Packit eace71
	errno = 0; \
Packit eace71
	/* In the whole libopeniscsiusr, we don't have negative value */ \
Packit eace71
	for (; i < sizeof(conv_array)/sizeof(conv_array[0]); ++i) { \
Packit eace71
		if ((conv_array[i].value) == tmp_var) \
Packit eace71
			return conv_array[i].str; \
Packit eace71
	} \
Packit eace71
	errno = EINVAL; \
Packit eace71
	return "Invalid argument"; \
Packit eace71
}
Packit eace71
Packit eace71
static const struct _num_str_conv _ISCSI_RC_MSG_CONV[] = {
Packit eace71
	{LIBISCSI_OK, "OK"},
Packit eace71
	{LIBISCSI_ERR_BUG, "BUG of libopeniscsiusr library"},
Packit eace71
	{LIBISCSI_ERR_SESS_NOT_FOUND, "Specified iSCSI session not found"},
Packit eace71
	{LIBISCSI_ERR_ACCESS, "Permission deny"},
Packit eace71
	{LIBISCSI_ERR_NOMEM, "Out of memory"},
Packit eace71
	{LIBISCSI_ERR_SYSFS_LOOKUP, "Could not lookup object in sysfs"},
Packit eace71
	{LIBISCSI_ERR_IDBM, "Error accessing/managing iSCSI DB"},
Packit eace71
	{LIBISCSI_ERR_TRANS_NOT_FOUND,
Packit eace71
		"iSCSI transport module not loaded in kernel or iscsid"},
Packit eace71
	{LIBISCSI_ERR_INVAL, "Invalid argument"},
Packit eace71
};
Packit eace71
Packit eace71
_iscsi_str_func_gen(iscsi_strerror, int, rc, _ISCSI_RC_MSG_CONV);
Packit eace71
Packit eace71
static const struct _num_str_conv _ISCSI_PRI_CONV[] = {
Packit eace71
	{LIBISCSI_LOG_PRIORITY_DEBUG, "DEBUG"},
Packit eace71
	{LIBISCSI_LOG_PRIORITY_INFO, "INFO"},
Packit eace71
	{LIBISCSI_LOG_PRIORITY_WARNING, "WARNING"},
Packit eace71
	{LIBISCSI_LOG_PRIORITY_ERROR, "ERROR"},
Packit eace71
};
Packit eace71
Packit eace71
_iscsi_str_func_gen(iscsi_log_priority_str, int, priority, _ISCSI_PRI_CONV);
Packit eace71
Packit eace71
void _iscsi_log_stderr(struct iscsi_context *ctx, int priority,
Packit eace71
		       const char *file, int line, const char *func_name,
Packit eace71
		       const char *format, va_list args)
Packit eace71
{
Packit eace71
	int printed_bytes = 0;
Packit eace71
Packit eace71
	_UNUSED(ctx);
Packit eace71
Packit eace71
	printed_bytes += fprintf(stderr, "iSCSI %s: ",
Packit eace71
				 iscsi_log_priority_str(priority));
Packit eace71
	printed_bytes += vfprintf(stderr, format, args);
Packit eace71
Packit eace71
	if (printed_bytes < _ISCSI_LOG_STRERR_ALIGN_WIDTH) {
Packit eace71
		fprintf(stderr, "%*s # %s:%s():%d\n",
Packit eace71
			_ISCSI_LOG_STRERR_ALIGN_WIDTH - printed_bytes, "", file,
Packit eace71
			func_name, line);
Packit eace71
	} else {
Packit eace71
		fprintf(stderr, " # %s:%s():%d\n", file, func_name, line);
Packit eace71
	}
Packit eace71
}
Packit eace71
Packit eace71
void _iscsi_log(struct iscsi_context *ctx, int priority, const char *file,
Packit eace71
		int line, const char *func_name, const char *format, ...)
Packit eace71
{
Packit eace71
	va_list args;
Packit eace71
Packit eace71
	if (ctx->log_func == NULL)
Packit eace71
		return;
Packit eace71
Packit eace71
	va_start(args, format);
Packit eace71
	ctx->log_func(ctx, priority, file, line, func_name, format, args);
Packit eace71
	va_end(args);
Packit eace71
}
Packit eace71
Packit eace71
int _scan_filter_skip_dot(const struct dirent *dir)
Packit eace71
{
Packit eace71
	return strcmp(dir->d_name, ".") && strcmp(dir->d_name, "..");
Packit eace71
}
Packit eace71
Packit eace71
bool _file_exists(const char *path)
Packit eace71
{
Packit eace71
	if (access(path, F_OK) == 0)
Packit eace71
		return true;
Packit eace71
	else
Packit eace71
		return false;
Packit eace71
}
Packit eace71
Packit eace71
static bool _is_eth(struct iscsi_context *ctx, const char *if_name)
Packit eace71
{
Packit eace71
	struct ifreq ifr;
Packit eace71
	int sockfd = -1;
Packit eace71
	char strerr_buff[_STRERR_BUFF_LEN];
Packit eace71
Packit eace71
	assert(if_name != NULL);
Packit eace71
Packit eace71
	memset(&ifr, 0, sizeof(ifr));
Packit eace71
Packit eace71
	_strncpy(ifr.ifr_name, if_name, IFNAMSIZ);
Packit eace71
Packit eace71
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
Packit eace71
	if (sockfd < 0) {
Packit eace71
		_warn(ctx, "Failed to create SOCK_DGRAM AF_INET socket: %d %s",
Packit eace71
		      errno, _strerror(errno, strerr_buff));
Packit eace71
		return false;
Packit eace71
	}
Packit eace71
Packit eace71
	if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) != 0) {
Packit eace71
		_warn(ctx, "IOCTL SIOCGIFHWADDR to %s failed: %d %s", if_name,
Packit eace71
		      errno, _strerror(errno, strerr_buff));
Packit eace71
		close(sockfd);
Packit eace71
		return false;
Packit eace71
	}
Packit eace71
Packit eace71
	close(sockfd);
Packit eace71
Packit eace71
	if (ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER)
Packit eace71
		return true;
Packit eace71
Packit eace71
	return false;
Packit eace71
}
Packit eace71
Packit eace71
/*
Packit eace71
 * driver_name should be char[_ETH_DRIVER_NAME_MAX_LEN]
Packit eace71
 */
Packit eace71
static int _eth_driver_get(struct iscsi_context *ctx, const char *if_name,
Packit eace71
			   char *driver_name)
Packit eace71
{
Packit eace71
	int sockfd = -1;
Packit eace71
	struct ethtool_drvinfo drvinfo;
Packit eace71
	struct ifreq ifr;
Packit eace71
	char strerr_buff[_STRERR_BUFF_LEN];
Packit eace71
Packit eace71
	assert(ctx != NULL);
Packit eace71
	assert(if_name != NULL);
Packit eace71
	assert(driver_name != NULL);
Packit eace71
Packit eace71
	memset(&ifr, 0, sizeof(ifr));
Packit eace71
	memset(&drvinfo, 0, sizeof(drvinfo));
Packit eace71
Packit eace71
	_strncpy(ifr.ifr_name, if_name, IFNAMSIZ);
Packit eace71
	drvinfo.cmd = ETHTOOL_GDRVINFO;
Packit eace71
	ifr.ifr_data = (caddr_t) &drvinfo;
Packit eace71
Packit eace71
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
Packit eace71
	if (sockfd < 0) {
Packit eace71
		_error(ctx, "Failed to create SOCK_DGRAM AF_INET socket: %d %s",
Packit eace71
		       errno, _strerror(errno, strerr_buff));
Packit eace71
		return LIBISCSI_ERR_BUG;
Packit eace71
	}
Packit eace71
Packit eace71
	if (ioctl(sockfd, SIOCETHTOOL, &ifr) != 0) {
Packit eace71
		_warn(ctx, "IOCTL SIOCETHTOOL to %s failed: %d %s", if_name,
Packit eace71
		      errno, _strerror(errno, strerr_buff));
Packit eace71
		close(sockfd);
Packit eace71
		return LIBISCSI_ERR_BUG;
Packit eace71
	}
Packit eace71
	close(sockfd);
Packit eace71
	snprintf(driver_name, _ETH_DRIVER_NAME_MAX_LEN, "%s", drvinfo.driver);
Packit eace71
Packit eace71
	return LIBISCSI_OK;
Packit eace71
}
Packit eace71
Packit eace71
int _eth_ifs_get(struct iscsi_context *ctx, struct _eth_if ***eifs,
Packit eace71
		 uint32_t *eif_count)
Packit eace71
{
Packit eace71
	int rc = LIBISCSI_OK;
Packit eace71
	struct if_nameindex *if_ni = NULL;
Packit eace71
	struct if_nameindex *if_i = NULL;
Packit eace71
	struct _eth_if *eif = NULL;
Packit eace71
	uint32_t tmp_count = 0;
Packit eace71
Packit eace71
	assert(ctx != NULL);
Packit eace71
	assert(eifs != NULL);
Packit eace71
	assert(eif_count != NULL);
Packit eace71
Packit eace71
	*eifs = NULL;
Packit eace71
	*eif_count = 0;
Packit eace71
Packit eace71
	if_ni = if_nameindex();
Packit eace71
	_alloc_null_check(ctx, if_ni, rc, out);
Packit eace71
Packit eace71
	for (if_i = if_ni; if_i && if_i->if_index && if_i->if_name; ++if_i)
Packit eace71
		tmp_count++;
Packit eace71
Packit eace71
	if (tmp_count == 0)
Packit eace71
		goto out;
Packit eace71
Packit eace71
	*eifs = calloc(tmp_count, sizeof(struct _eth_if *));
Packit eace71
	_alloc_null_check(ctx, *eifs, rc, out);
Packit eace71
Packit eace71
	for (if_i = if_ni; if_i && if_i->if_index && if_i->if_name; ++if_i) {
Packit eace71
		if (! _is_eth(ctx, if_i->if_name))
Packit eace71
			continue;
Packit eace71
		eif = calloc(1, sizeof(struct _eth_if));
Packit eace71
		_alloc_null_check(ctx, eif, rc, out);
Packit eace71
		(*eifs)[(*eif_count)++] = eif;
Packit eace71
		snprintf(eif->if_name, sizeof(eif->if_name)/sizeof(char),
Packit eace71
			 "%s", if_i->if_name);
Packit eace71
		_good(_eth_driver_get(ctx, eif->if_name, eif->driver_name),
Packit eace71
		      rc, out);
Packit eace71
	}
Packit eace71
Packit eace71
out:
Packit eace71
	if (rc != LIBISCSI_OK) {
Packit eace71
		_eth_ifs_free(*eifs, *eif_count);
Packit eace71
		*eifs = NULL;
Packit eace71
		*eif_count = 0;
Packit eace71
	}
Packit eace71
	if (if_ni != NULL)
Packit eace71
		if_freenameindex(if_ni);
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
void _eth_ifs_free(struct _eth_if **eifs, uint32_t eif_count)
Packit eace71
{
Packit eace71
	uint32_t i = 0;
Packit eace71
Packit eace71
	if ((eif_count == 0) || (eifs == NULL))
Packit eace71
		return;
Packit eace71
Packit eace71
	for (; i < eif_count; ++i)
Packit eace71
		free(eifs[i]);
Packit eace71
	free(eifs);
Packit eace71
}
Packit eace71
Packit eace71
void _scandir_free(struct dirent **namelist, int count)
Packit eace71
{
Packit eace71
	int i = 0;
Packit eace71
Packit eace71
	if ((namelist == NULL) || (count == 0))
Packit eace71
		return;
Packit eace71
Packit eace71
	for (i = count - 1; i >= 0; --i)
Packit eace71
		free(namelist[i]);
Packit eace71
	free(namelist);
Packit eace71
}
Packit eace71
Packit eace71
int _scandir(struct iscsi_context *ctx, const char *dir_path,
Packit eace71
	     struct dirent ***namelist, int *count)
Packit eace71
{
Packit eace71
	int rc = LIBISCSI_OK;
Packit eace71
	int errno_save = 0;
Packit eace71
Packit eace71
	assert(ctx != NULL);
Packit eace71
	assert(dir_path != NULL);
Packit eace71
	assert(namelist != NULL);
Packit eace71
	assert(count != NULL);
Packit eace71
Packit eace71
	*namelist = NULL;
Packit eace71
	*count = 0;
Packit eace71
Packit eace71
	*count = scandir(dir_path, namelist, _scan_filter_skip_dot, alphasort);
Packit eace71
	if (*count < 0) {
Packit eace71
		errno_save = errno;
Packit eace71
		if (errno_save == ENOENT) {
Packit eace71
			*count = 0;
Packit eace71
			goto out;
Packit eace71
		}
Packit eace71
		if (errno_save == ENOMEM) {
Packit eace71
			rc = LIBISCSI_ERR_NOMEM;
Packit eace71
			goto out;
Packit eace71
		}
Packit eace71
		if (errno_save == ENOTDIR) {
Packit eace71
			rc = LIBISCSI_ERR_BUG;
Packit eace71
			_error(ctx, "Got ENOTDIR error when scandir %s",
Packit eace71
			       dir_path);
Packit eace71
			goto out;
Packit eace71
		}
Packit eace71
		rc = LIBISCSI_ERR_BUG;
Packit eace71
		_error(ctx, "Got unexpected error %d when scandir %s",
Packit eace71
		       errno_save, dir_path);
Packit eace71
		goto out;
Packit eace71
	}
Packit eace71
Packit eace71
out:
Packit eace71
	if (rc != LIBISCSI_OK) {
Packit eace71
		_scandir_free(*namelist, *count);
Packit eace71
		*namelist = NULL;
Packit eace71
		*count = 0;
Packit eace71
	}
Packit eace71
Packit eace71
	return rc;
Packit eace71
}