Files
scst/usr/events/events.c
Vladislav Bolkhovitin dc1e3d786a Problem statement: Applications that are capable of running as Fibre
Transport Target mode server need to able to function when running in
containerized form factor in a secure and multi-tenant platform.

Solution: Such applications when containerized can run in any container
based platform such as Kubernetes/OpenShift or a custom one. These
applications are generic in nature and all they need is access  to SCST
user storage device driver (/dev/scst_user). The security policy of the
platform may not allow these applications to manage Fibre Channel (FC)
port and /sys file-system. The platform, however, can manage the FC ports
on behalf of these applications. The containerized applications can
remain generic in nature and run on any containerized platforms. The
sequence of operations would be 1. The platform converts the desired FC
ports to target mode prior to starting the containerized application 2.
The application does device registration using exported the SCST user
storage device driver 3. The platform, at this point, asynchronously adds
FC port LUNs to the user device registered by the application. As the
application inside the container can auto or manual restart
asynchronously, the platform would add the LUNs every time the
application does device registration.
 
Patch description: The SCST event mechanism is leveraged to achieve the
asynchronous LUN additions when application does device registration. The
current set of SCST events is extended to send a new event whenever there
is a virtual user device registration happens. The platform can watch for
the specific event and make business logic decisions to allow target mode
applications function securely. By extending the set of event, the SCST
module will be friendly to containerized applications and platforms.

Signed-off-by: Vikas Goel <vikas.goel@veritas.com>



git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@9210 d57e44dd-8a1f-0410-8b47-8ef2f437770f
2020-12-08 17:06:40 +00:00

337 lines
8.5 KiB
C

/*
* events.c
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <getopt.h>
#include <malloc.h>
#include <inttypes.h>
#include <stdbool.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/user.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include "version.h"
#include "debug.h"
#include "scst_event.h"
char *app_name;
#if defined(DEBUG) || defined(TRACING)
#ifdef DEBUG
/*#define DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF \
& ~TRACE_FUNCTION)
#define DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF & \
~TRACE_SCSI & ~TRACE_SCSI_SERIALIZING & ~TRACE_DEBUG)
*/
#define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_PID | \
TRACE_FUNCTION | TRACE_SPECIAL | TRACE_MGMT | TRACE_MGMT_DEBUG | \
TRACE_TIME)
#define TRACE_SN(args...) TRACE(TRACE_SCSI_SERIALIZING, args)
#else /* DEBUG */
# ifdef TRACING
#define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MGMT | \
TRACE_TIME | TRACE_SPECIAL)
# else
#define DEFAULT_LOG_FLAGS 0
# endif
#endif /* DEBUG */
bool log_daemon = false; /* needed for the tracing infrastructure */
unsigned long trace_flag = DEFAULT_LOG_FLAGS;
#endif /* defined(DEBUG) || defined(TRACING) */
static struct option const long_options[] = {
{"allowed_event", required_argument, 0, 'e'},
{"allowed_issuer", required_argument, 0, 'i'},
{"non_blocking", no_argument, 0, 'n'},
#if defined(DEBUG) || defined(TRACING)
{"debug", required_argument, 0, 'd'},
#endif
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0},
};
#define MAX_ALLOWED_EVENTS 16
static struct scst_event allowed_events[MAX_ALLOWED_EVENTS];
static int allowed_events_num;
static int non_blocking;
static void usage(void)
{
printf("Usage: %s [OPTIONS]\n", app_name);
printf("\nSCST events testing program\n");
printf(" -e, --allowed_event=code Allowed event code, 0 - any event\n");
printf(" -i, --allowed_issuer=issuer Allowed issuer, * - any issuer\n");
printf(" -n, --non_blocking Use non-blocking operations\n");
#if defined(DEBUG) || defined(TRACING)
printf(" -d, --debug=level Debug tracing level\n");
#endif
}
static void handle_reg_vdev_received(struct scst_event_user *event_user)
{
struct scst_event_reg_vdev_payload *p = (struct scst_event_reg_vdev_payload *)event_user->out_event.payload;
printf("Virtual device %s registration received.\n", p->device_name);
return;
}
static void handle_tm_received(struct scst_event_user *event_user)
{
struct scst_event_tm_fn_received_payload *p = (struct scst_event_tm_fn_received_payload *)event_user->out_event.payload;
printf("fn %d, device %s\n", p->fn, p->device_name);
return;
}
int main(int argc, char **argv)
{
int res = 0, i;
int ch, longindex;
int event_fd;
uint8_t event_user_buf[10240];
struct scst_event_user *event_user = (struct scst_event_user *)event_user_buf;
struct pollfd pl;
setlinebuf(stdout);
res = debug_init();
if (res != 0)
goto out;
app_name = argv[0];
while ((ch = getopt_long(argc, argv, "+e:i:d:nhv",
long_options, &longindex)) >= 0) {
switch (ch) {
case 'e':
if (allowed_events[allowed_events_num].event_code != 0) {
allowed_events_num++;
if (allowed_events_num >= MAX_ALLOWED_EVENTS) {
PRINT_ERROR("Too many allowed events %d",
allowed_events_num);
exit(1);
}
}
allowed_events[allowed_events_num].event_code = atoi(optarg);
break;
case 'i':
if (allowed_events[allowed_events_num].issuer_name[0] != '\0') {
allowed_events_num++;
if (allowed_events_num >= MAX_ALLOWED_EVENTS) {
PRINT_ERROR("Too many allowed events %d",
allowed_events_num);
exit(1);
}
}
strncpy(allowed_events[allowed_events_num].issuer_name,
optarg, sizeof(allowed_events[allowed_events_num].issuer_name));
allowed_events[allowed_events_num].issuer_name[
sizeof(allowed_events[allowed_events_num].issuer_name)-1] = '\0';
break;
case 'n':
non_blocking = 1;
break;
#if defined(DEBUG) || defined(TRACING)
case 'd':
trace_flag = strtol(optarg, (char **)NULL, 0);
break;
#endif
case 'v':
printf("%s version %s\n", app_name, VERSION_STR);
goto out_done;
default:
goto out_usage;
}
}
if (allowed_events_num == 0) {
if ((allowed_events[0].event_code == 0) &&
(allowed_events[0].issuer_name[0] == '\0'))
allowed_events[0].issuer_name[0] = '*';
}
allowed_events_num++;
#ifdef DEBUG
PRINT_INFO("trace_flag %lx", trace_flag);
#endif
if (non_blocking)
PRINT_INFO("%s", "NON-BLOCKING");
event_fd = open(SCST_EVENT_DEV, O_RDWR | (non_blocking ? O_NONBLOCK : 0));
if (event_fd < 0) {
res = -errno;
PRINT_ERROR("Unable to open SCST event device %s (%s)",
SCST_EVENT_DEV, strerror(-res));
goto out_done;
}
memset(&pl, 0, sizeof(pl));
pl.fd = event_fd;
pl.events = POLLIN;
for (i = 0; i < allowed_events_num; i++) {
struct scst_event e;
PRINT_INFO("Setting allowed event code %d, issuer_name %s",
allowed_events[i].event_code,
allowed_events[i].issuer_name);
memset(&e, 0, sizeof(e));
e.event_code = allowed_events[i].event_code;
strncpy(e.issuer_name, allowed_events[i].issuer_name,
sizeof(e.issuer_name));
e.issuer_name[sizeof(e.issuer_name)-1] = '\0';
res = ioctl(event_fd, SCST_EVENT_ALLOW_EVENT, &e);
if (res != 0) {
PRINT_ERROR("SCST_EVENT_ALLOW_EVENT failed: %s (res %d)",
strerror(errno), res);
goto out_done;
}
}
while (1) {
memset(event_user_buf, 0, sizeof(event_user_buf));
event_user->max_event_size = sizeof(event_user_buf);
res = ioctl(event_fd, SCST_EVENT_GET_NEXT_EVENT, event_user);
if (res != 0) {
res = errno;
switch (res) {
case ESRCH:
case EBUSY:
TRACE_MGMT_DBG("SCST_EVENT_GET_NEXT_EVENT returned "
"%d (%s)", res, strerror(res));
/* fall through */
case EINTR:
continue;
case EAGAIN:
TRACE_DBG("SCST_EVENT_GET_NEXT_EVENT, returned "
"EAGAIN (%d)", res);
if (non_blocking)
break;
else
continue;
default:
PRINT_ERROR("SCST_EVENT_GET_NEXT_EVENT failed: %s (res %d)",
strerror(errno), res);
goto out_done;
}
again_poll:
res = poll(&pl, 1, 2000);
if (res > 0)
continue;
else if (res == 0)
goto again_poll;
else {
res = errno;
switch (res) {
case ESRCH:
case EBUSY:
case EAGAIN:
TRACE_MGMT_DBG("poll() returned %d "
"(%s)", res, strerror(res));
case EINTR:
goto again_poll;
default:
PRINT_ERROR("poll() failed: %s", strerror(res));
#if 1
goto again_poll;
#else
goto out_close;
#endif
}
}
}
PRINT_INFO("\nevent_code %d, issuer_name %s",
event_user->out_event.event_code,
event_user->out_event.issuer_name);
if (event_user->out_event.payload_len != 0)
PRINT_BUFFER("payoad", event_user->out_event.payload,
event_user->out_event.payload_len);
PRINT_INFO("%s", "");
if (event_user->out_event.event_code == 0x12345) {
struct scst_event_notify_done d;
PRINT_INFO("%s", "Press any key to send reply to event "
"0x12345");
getchar();
memset(&d, 0, sizeof(d));
d.event_id = event_user->out_event.event_id;
d.status = -19;
res = ioctl(event_fd, SCST_EVENT_NOTIFY_DONE, &d);
if (res != 0)
PRINT_ERROR("SCST_EVENT_NOTIFY_DONE failed: %s "
"(res %d)", strerror(errno), res);
} else if (event_user->out_event.event_code == SCST_EVENT_REG_VIRT_DEV) {
handle_reg_vdev_received(event_user);
} else if (event_user->out_event.event_code == SCST_EVENT_TM_FN_RECEIVED)
handle_tm_received(event_user);
else if (event_user->out_event.event_code == SCST_EVENT_TM_FN_RECEIVED) {
struct scst_event_notify_done d;
memset(&d, 0, sizeof(d));
d.event_id = event_user->out_event.event_id;
d.status = -20;
res = ioctl(event_fd, SCST_EVENT_NOTIFY_DONE, &d);
if (res != 0)
PRINT_ERROR("SCST_EVENT_NOTIFY_DONE failed: %s "
"(res %d)", strerror(errno), res);
}
#if 0
{
struct scst_event e;
PRINT_INFO("Deleting allowed event code %d, issuer_name %s",
allowed_events[0].event_code,
allowed_events[0].issuer_name);
memset(&e, 0, sizeof(e));
e.event_code = allowed_events[0].event_code;
strncpy(e.issuer_name, allowed_events[0].issuer_name,
sizeof(e.issuer_name));
e.issuer_name[sizeof(e.issuer_name)-1] = '\0';
res = ioctl(event_fd, SCST_EVENT_DISALLOW_EVENT, &e);
if (res != 0) {
PRINT_ERROR("SCST_EVENT_DISALLOW_EVENT failed: "
"%s (res %d)", strerror(errno), res);
}
}
#endif
}
out_done:
debug_done();
out:
return res;
out_usage:
usage();
goto out_done;
}