From ae38d284e086733f50e0722ddd748fb0ffdf1a72 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Fri, 6 Nov 2015 03:40:37 +0000 Subject: [PATCH] Explicit ALUA user space git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@6579 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- Makefile | 4 +- usr/Makefile | 78 +++++ usr/fileio/Makefile | 3 +- usr/fileio/debug.c | 149 +-------- usr/fileio/fileio.c | 5 +- usr/include/debug.c | 147 +++++++++ usr/{fileio => include}/debug.h | 257 +++++++-------- usr/include/version.h | 24 ++ usr/stpgd/Makefile | 104 ++++++ usr/stpgd/README | 9 + usr/stpgd/debug.c | 1 + usr/stpgd/scst_on_stpg | 11 + usr/stpgd/stpgd_main.c | 562 ++++++++++++++++++++++++++++++++ 13 files changed, 1066 insertions(+), 288 deletions(-) create mode 100644 usr/Makefile create mode 100644 usr/include/debug.c rename usr/{fileio => include}/debug.h (54%) create mode 100644 usr/include/version.h create mode 100644 usr/stpgd/Makefile create mode 100644 usr/stpgd/README create mode 100644 usr/stpgd/debug.c create mode 100644 usr/stpgd/scst_on_stpg create mode 100644 usr/stpgd/stpgd_main.c diff --git a/Makefile b/Makefile index c984d1a62..7263ec5d6 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ QLA_DIR=qla2x00t_git/qla2x00-target QLA_OLD_INI_DIR=qla2x00t QLA_OLD_DIR=qla2x00t/qla2x00-target LSI_DIR=mpt -USR_DIR=usr/fileio +USR_DIR=usr SRP_DIR=srpt SCST_LOCAL_DIR=scst_local MVSAS_DIR=mvsas_tgt @@ -129,7 +129,7 @@ help: @echo " scst_local_install : scst_local target: install" @echo " scst_local_uninstall : scst_local target: uninstall" @echo "" - @echo " usr : make user space fileio_tgt target" + @echo " usr : make user space targets" @echo " usr_clean : usr target: clean " @echo " usr_extraclean : usr target: clean + clean dependencies" @echo " usr_install : usr target: install" diff --git a/usr/Makefile b/usr/Makefile new file mode 100644 index 000000000..77cb77135 --- /dev/null +++ b/usr/Makefile @@ -0,0 +1,78 @@ +# +# Common makefile for SCSI target mid-level and its drivers +# +# Copyright (C) 2004 - 2015 Vladislav Bolkhovitin +# Copyright (C) 2004 - 2005 Leonid Stoljar +# Copyright (C) 2007 - 2015 SanDisk Corporation +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, version 2 +# of the License. +# +# This program is distributed in the hope that 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. +# +# + +SHELL=/bin/bash +# Decide to use which kernel src. If not specified, is current running kernel. +#export KDIR=/usr/src/linux-2.6 + +FILEIO_DIR=fileio +STPGD_DIR=stpgd + +all: + cd $(FILEIO_DIR) && $(MAKE) $@ + cd $(STPGD_DIR) && $(MAKE) $@ + +install: + cd $(FILEIO_DIR) && $(MAKE) $@ + cd $(STPGD_DIR) && $(MAKE) $@ + +uninstall: + cd $(FILEIO_DIR) && $(MAKE) $@ + cd $(STPGD_DIR) && $(MAKE) $@ + +clean: + cd $(FILEIO_DIR) && $(MAKE) $@ + cd $(STPGD_DIR) && $(MAKE) $@ + +extraclean: + cd $(FILEIO_DIR) && $(MAKE) $@ + cd $(STPGD_DIR) && $(MAKE) $@ + +2release: + cd $(FILEIO_DIR) && $(MAKE) $@ + cd $(STPGD_DIR) && $(MAKE) $@ + +2debug: + cd $(FILEIO_DIR) && $(MAKE) $@ + cd $(STPGD_DIR) && $(MAKE) $@ + +2perf: + cd $(FILEIO_DIR) && $(MAKE) $@ + cd $(STPGD_DIR) && $(MAKE) $@ + +disable_proc: + cd $(FILEIO_DIR) && $(MAKE) $@ + cd $(STPGD_DIR) && $(MAKE) $@ + +enable_proc: + cd $(FILEIO_DIR) && $(MAKE) $@ + cd $(STPGD_DIR) && $(MAKE) $@ + +help: + @echo " all (the default) : make all" + @echo " clean : clean files" + @echo " extraclean : clean + clean dependencies" + @echo " install : install" + @echo " uninstall : uninstall" + @echo " Notes :" + @echo " - install and uninstall must be made as root." + @echo " - be sure to compile qla against the correct initiator" + @echo " driver. Read its README for details." + +.PHONY: all install uninstall clean extraclean help 2release 2debug 2perf disable_proc enable_proc diff --git a/usr/fileio/Makefile b/usr/fileio/Makefile index 7f6b23695..4789564ef 100644 --- a/usr/fileio/Makefile +++ b/usr/fileio/Makefile @@ -29,10 +29,11 @@ OBJS_F = $(SRCS_F:.c=.o) SCST_INC_DIR := $(shell if [ -e "$$PWD/../../scst" ]; \ then echo "$$PWD/../../scst/include"; \ else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi) +DEBUG_INC_DIR := ../include INSTALL_DIR := $(DESTDIR)$(PREFIX)/bin/scst CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes \ - -I$(SCST_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \ + -I$(SCST_INC_DIR) -I$(DEBUG_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \ -D__USE_LARGEFILE64 PROGS = fileio_tgt LIBS = -lpthread diff --git a/usr/fileio/debug.c b/usr/fileio/debug.c index 296025302..03c08a556 100644 --- a/usr/fileio/debug.c +++ b/usr/fileio/debug.c @@ -1,148 +1 @@ -/* - * debug.c - * - * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin - * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2015 SanDisk Corporation - * - * Contains helper functions for execution tracing and error reporting. - * Intended to be included in main .c file. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, version 2 - * of the License. - * - * This program is distributed in the hope that 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. - */ - -#include -#include -#include -#include -#include -#include - -#include "debug.h" - -pid_t gettid (void) -{ - return syscall(__NR_gettid); -} - -#if defined(DEBUG) || defined(TRACING) - -#define TRACE_BUF_SIZE 512 - -static char trace_buf[TRACE_BUF_SIZE]; -static pthread_spinlock_t trace_buf_lock; - -int debug_print_prefix(unsigned long trace_flag, const char *prefix, - const char *func, int line) -{ - int i = 0; - - pthread_spin_lock(&trace_buf_lock); - - if (trace_flag & TRACE_TIME) { - struct tm t; - time_t tt; - time(&tt); - localtime_r(&tt, &t); - i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%d:%d:%d ", - t.tm_hour, t.tm_min, t.tm_sec); - } - if (trace_flag & TRACE_PID) - i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "[%d]: ", - gettid()); - if (prefix != NULL) - i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", prefix); - if (trace_flag & TRACE_FUNCTION) - i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", func); - if (trace_flag & TRACE_LINE) - i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%i:", line); - - if (i > 0) - PRINTN("%s", trace_buf); - - pthread_spin_unlock(&trace_buf_lock); - - return i; -} - -void debug_print_buffer(const void *data, int len) -{ - int z, z1, i; - const unsigned char *buf = (const unsigned char *) data; - int f = 0; - - if (buf == NULL) - return; - - pthread_spin_lock(&trace_buf_lock); - - PRINT(" (h)___0__1__2__3__4__5__6__7__8__9__A__B__C__D__E__F"); - for (z = 0, z1 = 0, i = 0; z < len; z++) { - if (z % 16 == 0) { - if (z != 0) { - i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, - " "); - for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1); - z1++) { - if ((buf[z1] >= 0x20) && - (buf[z1] < 0x80)) - trace_buf[i++] = buf[z1]; - else - trace_buf[i++] = '.'; - } - trace_buf[i] = '\0'; - PRINT("%s", trace_buf); - i = 0; - f = 1; - } - i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, - "%4x: ", z); - } - i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%02x ", - buf[z]); - } - i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, " "); - for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1); z1++) { - if ((buf[z1] > 0x20) && (buf[z1] < 0x80)) - trace_buf[i++] = buf[z1]; - else - trace_buf[i++] = '.'; - } - trace_buf[i] = '\0'; - if (f) { - PRINT("%s", trace_buf) - } else { - PRINT("%s", trace_buf); - } - - pthread_spin_unlock(&trace_buf_lock); - return; -} - -int debug_init(void) -{ - int res; - - res = pthread_spin_init(&trace_buf_lock, PTHREAD_PROCESS_PRIVATE); - if (res != 0) { - res = errno; - PRINT_ERROR("pthread_spin_init() failed: %s", strerror(res)); - } - - return res; -} - -void debug_done(void) -{ - pthread_spin_destroy(&trace_buf_lock); -} - -#endif /* DEBUG || TRACING */ +#include "../include/debug.c" diff --git a/usr/fileio/fileio.c b/usr/fileio/fileio.c index 9c2d91fe6..c9ca1aaf1 100644 --- a/usr/fileio/fileio.c +++ b/usr/fileio/fileio.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -36,7 +37,9 @@ char *app_name; +#include "version.h" #include "common.h" +#include "debug.h" #if defined(DEBUG) || defined(TRACING) @@ -62,11 +65,11 @@ char *app_name; # endif #endif /* DEBUG */ +bool log_daemon = false; unsigned long trace_flag = DEFAULT_LOG_FLAGS; #endif /* defined(DEBUG) || defined(TRACING) */ #define DEF_BLOCK_SHIFT 9 -#define VERSION_STR "3.1.0-pre1" #define THREADS 7 #define MAX_VDEVS 10 diff --git a/usr/include/debug.c b/usr/include/debug.c new file mode 100644 index 000000000..8d77e475b --- /dev/null +++ b/usr/include/debug.c @@ -0,0 +1,147 @@ +/* + * debug.c + * + * Copyright (C) 2004 - 2015 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2005 Leonid Stoljar + * Copyright (C) 2007 - 2015 SanDisk Corporation + * + * Contains helper functions for execution tracing and error reporting. + * Intended to be included in main .c file. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2 + * of the License. + * + * This program is distributed in the hope that 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "debug.h" + +pid_t gettid(void) +{ + return syscall(__NR_gettid); +} + +#if defined(DEBUG) || defined(TRACING) + +#define TRACE_BUF_SIZE 512 + +static char trace_buf[TRACE_BUF_SIZE]; +static pthread_spinlock_t trace_buf_lock; + +int debug_print_prefix(unsigned long trace_flag, const char *prefix, + const char *func, int line) +{ + int i = 0; + + pthread_spin_lock(&trace_buf_lock); + + if (trace_flag & TRACE_TIME) { + struct tm t; + time_t tt; + + time(&tt); + localtime_r(&tt, &t); + i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%d:%d:%d ", + t.tm_hour, t.tm_min, t.tm_sec); + } + if (trace_flag & TRACE_PID) + i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "[%d]: ", + gettid()); + if (prefix != NULL) + i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", prefix); + if (trace_flag & TRACE_FUNCTION) + i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", func); + if (trace_flag & TRACE_LINE) + i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%i:", line); + + if (i > 0) + PRINTN(LOG_INFO, "%s", trace_buf); + + pthread_spin_unlock(&trace_buf_lock); + + return i; +} + +void debug_print_buffer(const void *data, int len) +{ + int z, z1, i; + const unsigned char *buf = (const unsigned char *) data; + int f = 0; + + if (buf == NULL) + return; + + pthread_spin_lock(&trace_buf_lock); + + PRINT(LOG_INFO, " (h)___0__1__2__3__4__5__6__7__8__9__A__B__C__D__E__F"); + for (z = 0, z1 = 0, i = 0; z < len; z++) { + if (z % 16 == 0) { + if (z != 0) { + i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, + " "); + for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1); + z1++) { + if ((buf[z1] >= 0x20) && + (buf[z1] < 0x80)) + trace_buf[i++] = buf[z1]; + else + trace_buf[i++] = '.'; + } + trace_buf[i] = '\0'; + PRINT(LOG_INFO, "%s", trace_buf); + i = 0; + f = 1; + } + i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, + "%4x: ", z); + } + i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%02x ", + buf[z]); + } + i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, " "); + for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1); z1++) { + if ((buf[z1] > 0x20) && (buf[z1] < 0x80)) + trace_buf[i++] = buf[z1]; + else + trace_buf[i++] = '.'; + } + trace_buf[i] = '\0'; + if (f) + PRINT(LOG_INFO, "%s", trace_buf); + else + PRINT(LOG_INFO, "%s", trace_buf); + + pthread_spin_unlock(&trace_buf_lock); +} + +int debug_init(void) +{ + int res; + + res = pthread_spin_init(&trace_buf_lock, PTHREAD_PROCESS_PRIVATE); + if (res != 0) { + res = errno; + PRINT_ERROR("pthread_spin_init() failed: %s", strerror(res)); + } + + return res; +} + +void debug_done(void) +{ + pthread_spin_destroy(&trace_buf_lock); +} + +#endif /* DEBUG || TRACING */ diff --git a/usr/fileio/debug.h b/usr/include/debug.h similarity index 54% rename from usr/fileio/debug.h rename to usr/include/debug.h index dbd0b8af1..bba07f5ef 100644 --- a/usr/fileio/debug.h +++ b/usr/include/debug.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include extern pid_t gettid(void); @@ -55,8 +57,21 @@ extern pid_t gettid(void); #define TRACE_ORDER 0x00008000 #define TRACE_ALL 0xffffffff -#define PRINT(format, args...) fprintf(stdout, format "\n", ## args); -#define PRINTN(format, args...) fprintf(stdout, format, ## args); +#define PRINT(priority, format, args...) \ +do { \ + if (log_daemon) \ + syslog(priority, format "\n", ## args); \ + else \ + fprintf(stdout, format "\n", ## args); \ +} while (0) + +#define PRINTN(priority, format, args...) \ +do { \ + if (log_daemon) \ + syslog(priority, format, ## args); \ + else \ + fprintf(stdout, format, ## args); \ +} while (0) extern char *app_name; #define LOG_PREFIX app_name @@ -69,6 +84,7 @@ extern char *app_name; #if defined(DEBUG) || defined(TRACING) +extern bool log_daemon; extern unsigned long trace_flag; extern int debug_init(void); @@ -80,24 +96,23 @@ extern void debug_done(void); */ extern int debug_print_prefix(unsigned long trace_flag, const char *prefix, - const char *func, int line); + const char *func, int line); extern void debug_print_buffer(const void *data, int len); #define TRACE(trace, format, args...) \ do { \ - if (trace_flag & (trace)) \ - { \ - debug_print_prefix(trace_flag, __LOG_PREFIX, __FUNCTION__, \ - __LINE__); \ - PRINT(format, args); \ - } \ -} while(0) + if (trace_flag & (trace)) { \ + debug_print_prefix(trace_flag, __LOG_PREFIX, \ + __func__, __LINE__); \ + PRINT(LOG_DEBUG, format, args); \ + } \ +} while (0) #define PRINT_BUFFER(message, buff, len) \ do { \ - PRINT("%s:", message); \ - debug_print_buffer(buff, len); \ -} while(0) + PRINT(LOG_INFO, "%s:", message); \ + debug_print_buffer(buff, len); \ +} while (0) #else /* DEBUG || TRACING */ @@ -115,142 +130,126 @@ static inline void debug_done(void) {} #define TRACE_MEM(format, args...) \ do { \ - if (trace_flag & TRACE_MEMORY) \ - { \ - debug_print_prefix(trace_flag, NULL, __FUNCTION__, \ - __LINE__); \ - PRINT(format, args); \ - } \ -} while(0) + if (trace_flag & TRACE_MEMORY) { \ + debug_print_prefix(trace_flag, NULL, \ + __func__, __LINE__); \ + PRINT(LOG_DEBUG, format, args); \ + } \ +} while (0) #define TRACE_DBG(format, args...) \ do { \ - if (trace_flag & TRACE_DEBUG) \ - { \ - debug_print_prefix(trace_flag, NULL, __FUNCTION__, \ - __LINE__); \ - PRINT(format, args); \ - } \ -} while(0) + if (trace_flag & TRACE_DEBUG) { \ + debug_print_prefix(trace_flag, NULL, \ + __func__, __LINE__); \ + PRINT(LOG_DEBUG, format, args); \ + } \ +} while (0) #define TRACE_DBG_SPECIAL(args...) TRACE(TRACE_DEBUG|TRACE_SPECIAL, args) #define TRACE_MGMT_DBG(format, args...) \ do { \ - if (trace_flag & TRACE_MGMT_DEBUG) \ - { \ - debug_print_prefix(trace_flag, NULL, __FUNCTION__, \ - __LINE__); \ - PRINT(format, args); \ - } \ -} while(0) + if (trace_flag & TRACE_MGMT_DEBUG) { \ + debug_print_prefix(trace_flag, NULL, \ + __func__, __LINE__); \ + PRINT(LOG_DEBUG, format, args); \ + } \ +} while (0) #define TRACE_BUFFER(message, buff, len) \ do { \ - if (trace_flag & TRACE_BUFF) \ - { \ - debug_print_prefix(trace_flag, NULL, __FUNCTION__, \ - __LINE__); \ - PRINT("%s:", message); \ - debug_print_buffer(buff, len); \ - } \ -} while(0) + if (trace_flag & TRACE_BUFF) { \ + debug_print_prefix(trace_flag, NULL, \ + __func__, __LINE__); \ + PRINT(LOG_DEBUG, "%s:", message); \ + debug_print_buffer(buff, len); \ + } \ +} while (0) #define TRACE_BUFF_FLAG(flag, message, buff, len) \ do { \ - if (trace_flag & (flag)) \ - { \ - debug_print_prefix(trace_flag, NULL, __FUNCTION__, \ - __LINE__); \ - PRINT("%s:", message); \ - debug_print_buffer(buff, len); \ - } \ -} while(0) + if (trace_flag & (flag)) { \ + debug_print_prefix(trace_flag, NULL, \ + __func__, __LINE__); \ + PRINT(LOG_DEBUG, "%s:", message); \ + debug_print_buffer(buff, len); \ + } \ +} while (0) #define PRINT_INFO(format, args...) \ do { \ - debug_print_prefix(trace_flag, __LOG_PREFIX, __FUNCTION__, \ - __LINE__); \ - PRINT(format, args); \ -} while(0) + debug_print_prefix(trace_flag, __LOG_PREFIX, \ + __func__, __LINE__); \ + PRINT(LOG_INFO, format, args); \ +} while (0) #define PRINT_WARNING(format, args...) \ do { \ - debug_print_prefix(trace_flag, __LOG_PREFIX, __FUNCTION__, \ - __LINE__); \ - PRINT("***WARNING*** " format, args); \ -} while(0) + debug_print_prefix(trace_flag, __LOG_PREFIX, \ + __func__, __LINE__); \ + PRINT(LOG_WARNING, "***WARNING*** " format, args); \ +} while (0) #define PRINT_ERROR(format, args...) \ do { \ - debug_print_prefix(trace_flag, __LOG_PREFIX, __FUNCTION__, \ - __LINE__); \ - PRINT("***ERROR*** " format, args); \ -} while(0) + debug_print_prefix(trace_flag, __LOG_PREFIX, \ + __func__, __LINE__); \ + PRINT(LOG_ERR, "***ERROR*** " format, args); \ +} while (0) #define TRACE_ENTRY() \ do { \ - if (trace_flag & TRACE_ENTRYEXIT) \ - { \ - if (trace_flag & TRACE_PID) \ - { \ - PRINT("[%d]: ENTRY %s", gettid(), \ - __FUNCTION__); \ - } \ - else \ - { \ - PRINT("ENTRY %s", __FUNCTION__); \ - } \ - } \ -} while(0) + if (trace_flag & TRACE_ENTRYEXIT) { \ + if (trace_flag & TRACE_PID) { \ + PRINT(LOG_DEBUG, "[%d]: ENTRY %s", \ + gettid(), __func__); \ + } else { \ + PRINT(LOG_DEBUG, "ENTRY %s", \ + __func__); \ + } \ + } \ +} while (0) #define TRACE_EXIT() \ do { \ - if (trace_flag & TRACE_ENTRYEXIT) \ - { \ - if (trace_flag & TRACE_PID) \ - { \ - PRINT("[%d]: EXIT %s", gettid(), \ - __FUNCTION__); \ - } \ - else \ - { \ - PRINT("EXIT %s", __FUNCTION__); \ - } \ - } \ -} while(0) + if (trace_flag & TRACE_ENTRYEXIT) { \ + if (trace_flag & TRACE_PID) { \ + PRINT(LOG_DEBUG, "[%d]: EXIT %s", \ + gettid(), __func__); \ + } else { \ + PRINT(LOG_DEBUG, "EXIT %s", __func__); \ + } \ + } \ +} while (0) #define TRACE_EXIT_RES(res) \ do { \ - if (trace_flag & TRACE_ENTRYEXIT) \ - { \ - if (trace_flag & TRACE_PID) \ - { \ - PRINT("[%d]: EXIT %s: %ld", gettid(), \ - __FUNCTION__, (long)(res)); \ - } \ - else \ - { \ - PRINT("EXIT %s: %ld", __FUNCTION__, (long)(res)); \ - } \ - } \ -} while(0) + if (trace_flag & TRACE_ENTRYEXIT) { \ + if (trace_flag & TRACE_PID) { \ + PRINT(LOG_DEBUG, "[%d]: EXIT %s: %ld", \ + gettid(), __func__, \ + (long)(res)); \ + } else { \ + PRINT(LOG_DEBUG, "EXIT %s: %ld", \ + __func__, (long)(res)); \ + } \ + } \ +} while (0) #define TRACE_EXIT_HRES(res) \ do { \ - if (trace_flag & TRACE_ENTRYEXIT) \ - { \ - if (trace_flag & TRACE_PID) \ - { \ - PRINT("[%d]: EXIT %s: 0x%lx", gettid(), \ - __FUNCTION__, (long)(res)); \ - } \ - else \ - { \ - PRINT("EXIT %s: %lx", __FUNCTION__, (long)(res)); \ - } \ - } \ -} while(0) + if (trace_flag & TRACE_ENTRYEXIT) { \ + if (trace_flag & TRACE_PID) { \ + PRINT(LOG_DEBUG, "[%d]: EXIT %s: 0x%lx",\ + gettid(), __func__, \ + (long)(res)); \ + } else { \ + PRINT(LOG_DEBUG, "EXIT %s: %lx", \ + __func__, (long)(res)); \ + } \ + } \ +} while (0) #else /* DEBUG */ @@ -271,39 +270,25 @@ do { \ #ifdef LOG_PREFIX #define PRINT_INFO(format, args...) \ -do { \ - PRINT("%s: " format, LOG_PREFIX, args); \ -} while(0) + PRINT(LOG_INFO, "%s: " format, LOG_PREFIX, args) \ #define PRINT_WARNING(format, args...) \ -do { \ - PRINT("%s: ***WARNING*** " \ - format, LOG_PREFIX, args); \ -} while(0) + PRINT(LOG_WARNING, "%s: ***WARNING*** " format, \ + LOG_PREFIX, args) \ #define PRINT_ERROR(format, args...) \ -do { \ - PRINT("%s: ***ERROR*** " \ - format, LOG_PREFIX, args); \ -} while(0) + PRINT(LOG_ERR, "%s: ***ERROR*** " format, \ + LOG_PREFIX, args) \ #else -#define PRINT_INFO(format, args...) \ -do { \ - PRINT(format, args); \ -} while(0) +#define PRINT_INFO(format, args...) PRINT(LOG_INFO, format, args) #define PRINT_WARNING(format, args...) \ -do { \ - PRINT("***WARNING*** " format, args); \ -} while(0) - + PRINT(LOG_WARNING, "***WARNING*** " format, args) \ #define PRINT_ERROR(format, args...) \ -do { \ - PRINT("***ERROR*** " format, args); \ -} while(0) + PRINT(LOG_ERR, "***ERROR*** " format, args) \ #endif /* LOG_PREFIX */ diff --git a/usr/include/version.h b/usr/include/version.h new file mode 100644 index 000000000..f4626c8c5 --- /dev/null +++ b/usr/include/version.h @@ -0,0 +1,24 @@ +/* + * version.h + * + * Copyright (C) 2015 SanDisk Corporation + * + * Contains macroses for execution tracing and error reporting + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2 + * of the License. + * + * This program is distributed in the hope that 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. + */ + +#ifndef __VERSION_H +#define __VERSION_H + +#define VERSION_STR "3.1.0-pre1" + +#endif /* __VERSION_H */ diff --git a/usr/stpgd/Makefile b/usr/stpgd/Makefile new file mode 100644 index 000000000..0f8336c95 --- /dev/null +++ b/usr/stpgd/Makefile @@ -0,0 +1,104 @@ +ifndef PREFIX + PREFIX=/usr/local +endif + +SHELL=/bin/bash + +SRCS_F = stpgd_main.c debug.c +OBJS_F = $(SRCS_F:.c=.o) + +#SRCS_C = +#OBJS_C = $(SRCS_C:.c=.o) + +SCST_INC_DIR := $(shell if [ -e "$$PWD/../../scst" ]; \ + then echo "$$PWD/../../scst/include"; \ + else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi) +DEBUG_INC_DIR := ../include +INSTALL_DIR := $(DESTDIR)$(PREFIX)/sbin +ON_STPG := $(DESTDIR)$(PREFIX)/bin/scst/scst_on_stpg + +CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes \ + -I$(SCST_INC_DIR) -I$(DEBUG_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \ + -D__USE_LARGEFILE64 +PROGS = stpgd +LIBS = -lpthread + +CFLAGS += -DEXTRACHECKS +#CFLAGS += -DTRACING +CFLAGS += -DDEBUG -g -fno-inline -fno-inline-functions +CFLAGS += -W -Wno-unused-parameter +CFLAGS += $(LOCAL_CFLAGS) + +#CFLAGS += -DDEBUG_NOMEM + +all: $(PROGS) + +stpgd: .depend_f $(OBJS_F) + $(CC) $(OBJS_F) $(LIBS) $(LOCAL_LD_FLAGS) -o $@ + +#cdrom_tgt: .depend_c $(OBJS_C) +# $(CC) $(OBJS_C) $(LIBS) $(LOCAL_LD_FLAGS) -o $@ + +ifeq (.depend_f,$(wildcard .depend_f)) +-include .depend_f +endif + +#ifeq (.depend_c,$(wildcard .depend_c)) +#-include .depend_c +#endif + +%.o: %.c Makefile + $(CC) -c -o $(@) $(CFLAGS) $(<) + +.depend_f: + $(CC) -M $(CFLAGS) $(SRCS_F) >$(@) + +#.depend_c: +# $(CC) -M $(CFLAGS) $(SRCS_C) >$(@) + +install: all + install -d $(INSTALL_DIR) + install -m 700 $(PROGS) $(INSTALL_DIR) + install -m 700 scst_on_stpg $(ON_STPG) + +uninstall: + rm -f $(INSTALL_DIR)/$(PROGS) + rm -rf $(INSTALL_DIR) + +clean: + rm -f *.o $(PROGS) .depend* + +extraclean: clean + rm -f *.orig *.rej + +2release: + sed -i.aa s/"^C\?FLAGS += \-DEXTRACHECKS"/"#CFLAGS += \-DEXTRACHECKS"/ Makefile + grep "^#CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null + sed -i.aa s/"^#\?CFLAGS += \-DTRACING"/"CFLAGS += \-DTRACING"/ Makefile + grep "^CFLAGS += \-DTRACING" Makefile >/dev/null + sed -i.aa s/"^C\?FLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile + grep "^#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null + rm Makefile.aa + +2debug: + sed -i.aa s/"^#\?CFLAGS += \-DEXTRACHECKS"/"CFLAGS += \-DEXTRACHECKS"/ Makefile + grep "^CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null + sed -i.aa s/"^C\?FLAGS += \-DTRACING"/"#CFLAGS += \-DTRACING"/ Makefile + grep "^#CFLAGS += \-DTRACING" Makefile >/dev/null + sed -i.aa s/"^#\?CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile + grep "^CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null + rm Makefile.aa + +2perf: + sed -i.aa s/"^C\?FLAGS += \-DEXTRACHECKS"/"#CFLAGS += \-DEXTRACHECKS"/ Makefile + grep "^#CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null + sed -i.aa s/"^C\?FLAGS += \-DTRACING"/"#CFLAGS += \-DTRACING"/ Makefile + grep "^#CFLAGS += \-DTRACING" Makefile >/dev/null + sed -i.aa s/"^C\?FLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile + grep "^#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null + rm Makefile.aa + +release-archive: + ../../scripts/generate-release-archive stpgd "$$(sed -n 's/^#define[[:blank:]]VERSION_STR[[:blank:]]*\"\([^\"]*\)\".*/\1/p' stpgd_main.c)" + +.PHONY: all install uninstall clean extraclean 2release 2debug 2perf diff --git a/usr/stpgd/README b/usr/stpgd/README new file mode 100644 index 000000000..64b5ad98b --- /dev/null +++ b/usr/stpgd/README @@ -0,0 +1,9 @@ +This is stpgd service called by the SCST core upon receiving SET TARGET +PORT GROUPS command via SCST events subsystem. In turn, stpgd calls +external script to actually modify ALUA state as requested by the SET +TARGET PORT GROUPS command via cluster manager and SCST sysfs. You can +find example of such script in scst_on_stpg file. + +Reason why such dual stage approach is used is, because there is no way +from inside the kernel to control execution of external programs and +there is no way to write a service calling IOCTLs on shell. diff --git a/usr/stpgd/debug.c b/usr/stpgd/debug.c new file mode 100644 index 000000000..03c08a556 --- /dev/null +++ b/usr/stpgd/debug.c @@ -0,0 +1 @@ +#include "../include/debug.c" diff --git a/usr/stpgd/scst_on_stpg b/usr/stpgd/scst_on_stpg new file mode 100644 index 000000000..cb86767b0 --- /dev/null +++ b/usr/stpgd/scst_on_stpg @@ -0,0 +1,11 @@ +#!/bin/bash +# +# Script invoked by SCST for processing a SCSI SET TARGET PORT GROUPS command +# +# Arguments: SCST_DEVICE_NAME (device name where this STPG command was +# received on), SCST_PREV_ALUA_STATE, SCST_ALUA_STATE, +# SCST_DEVICE_GROUP and SCST_TARGET_GROUP + environmental variables +# with the same names set +# + +echo $SCST_ALUA_STATE >/sys/kernel/scst_tgt/device_groups/$SCST_DEVICE_GROUP/target_groups/$SCST_TARGET_GROUP/state diff --git a/usr/stpgd/stpgd_main.c b/usr/stpgd/stpgd_main.c new file mode 100644 index 000000000..625879cfc --- /dev/null +++ b/usr/stpgd/stpgd_main.c @@ -0,0 +1,562 @@ +/* + * stpgd_main.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "version.h" +#include "debug.h" +#include "scst_event.h" + +char *app_name; + +#define DEFAULT_TRANSITION_TIME 17 + +#if defined(DEBUG) || defined(TRACING) + +#ifdef 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 */ + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +bool log_daemon = true; +unsigned long trace_flag = DEFAULT_LOG_FLAGS; +#endif /* defined(DEBUG) || defined(TRACING) */ + +int transition_timeout = DEFAULT_TRANSITION_TIME; + +static struct option const long_options[] = { + {"path", required_argument, 0, 'p'}, + {"timeout", required_argument, 0, 't'}, + {"foreground", no_argument, 0, 'f'}, +#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}, +}; + +int stpg_init_report_pipe[2]; +char *stpg_path; + +static void usage(int status) +{ + if (status != 0) + fprintf(stderr, "Try '%s --help' for more information.\n", app_name); + else { + printf("Usage: %s [OPTIONS]\n", app_name); + printf("STPG target daemon\n"); + printf(" -f, --foreground make the program run in the foreground\n"); + printf(" -p, --path absolute path to the STPG script\n"); + printf(" -t, --timeout transition timeout\n"); +#if defined(DEBUG) || defined(TRACING) + printf(" -d, --debug=level debug tracing level\n"); +#endif + printf(" -h, --help display this help and exit\n"); + } +} + +static void stpg_handle_tm_received(struct scst_event_user *event_user) +{ + /* + * Put code to abort state transition here, if this STPG cmd, + * identified by cmd_to_abort_tag or RESET, requested to be aborted + */ +} + +int invoke_stpg(const uint8_t *device_name, + const struct scst_event_stpg_descr *descr, pid_t *out_pid) +{ + char *args[7], *env[7]; + int res = 0, ret, i; + pid_t c_pid; + + args[0] = stpg_path; + args[1] = (char *)device_name; + args[2] = (char *)descr->prev_state; + args[3] = (char *)descr->new_state; + args[4] = (char *)descr->dg_name; + args[5] = (char *)descr->tg_name; + args[6] = NULL; + + env[0] = "PATH=/bin:/usr/bin:/sbin:/usr/sbin"; + ret = asprintf(&env[1], "SCST_DEVICE_NAME=%s", device_name); + if (ret < 0) { + res = -errno; + PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res)); + goto out; + } + ret = asprintf(&env[2], "SCST_PREV_ALUA_STATE=%s", descr->prev_state); + if (ret < 0) { + res = -errno; + PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res)); + goto out; + } + ret = asprintf(&env[3], "SCST_ALUA_STATE=%s", descr->new_state); + if (ret < 0) { + res = -errno; + PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res)); + goto out; + } + ret = asprintf(&env[4], "SCST_DEVICE_GROUP=%s", descr->dg_name); + if (ret < 0) { + res = -errno; + PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res)); + goto out; + } + ret = asprintf(&env[5], "SCST_TARGET_GROUP=%s", descr->tg_name); + if (ret < 0) { + res = -errno; + PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res)); + goto out; + } + env[6] = NULL; + + PRINT_INFO("Invoking script %s with parameters: %s %s %s %s %s and environment: " + "%s %s %s %s %s", stpg_path, args[1], args[2], args[3], + args[4], args[5], env[1], env[2], env[3], env[4], env[5]); + + c_pid = fork(); + if (c_pid == 0) { + ret = setpgid(getpid(), getpid()); + if (ret < 0) { + res = -errno; + PRINT_ERROR("setgid failed %d (%s)", ret, strerror(-ret)); + } + TRACE_DBG("pgid %d (pid %d)", getpgid(getpid()), getpid()); + ret = execve(stpg_path, args, env); + if (ret < 0) { + res = -errno; + PRINT_ERROR("EXEC failed %d (%s)", ret, strerror(-ret)); + } + exit(0); + } else if (c_pid < 0) { + res = -errno; + PRINT_ERROR("fork() failed: %d (%s)", res, strerror(-res)); + } + + *out_pid = c_pid; + + for (i = 1; i < (signed)ARRAY_SIZE(env); i++) + free(env[i]); + +out: + return res; +} + +/* Returns 0, if the pid is still running, >0 if it was exited or <0 error code */ +int wait_until_finished(pid_t pid, unsigned long deadline, int *status, int child) +{ + int res; + time_t start, end; + double elapsed; + + TRACE_ENTRY(); + + time(&start); + do { + res = waitpid(pid, status, WNOHANG); + if (res != 0) { + if (res < 0) { + res = -errno; + PRINT_ERROR("Waitpid for pid %d (child %d) " + "failed: %d (%s)", pid, child, + errno, strerror(errno)); + } + break; + } + sleep(0.1); + time(&end); + elapsed = difftime(end, start); + } while (elapsed < deadline); + + TRACE_EXIT_RES(res); + return res; +} + +int handle_stpg_received(struct scst_event_user *event_user) +{ + const struct scst_event_stpg_payload *p = (struct scst_event_stpg_payload *)event_user->out_event.payload; + int num, k; + int res = 0; + pid_t pids[p->stpg_descriptors_cnt]; + + TRACE_DBG("device name %s, stpg_descriptors_cnt %d", p->device_name, + p->stpg_descriptors_cnt); + + for (num = 0; num < p->stpg_descriptors_cnt; num++) { + res = invoke_stpg(p->device_name, &p->stpg_descriptors[num], &pids[num]); + TRACE_DBG("num %d, res %d, pid %d", num, res, pids[num]); + if (res != 0) + break; + } + + TRACE_DBG("num %d", num); + for (k = 0; k < num; k++) { + int status = 0, rc; + + TRACE_DBG("k %d, pid %d", k, pids[k]); + + rc = wait_until_finished(pids[k], transition_timeout, &status, k); + TRACE_DBG("rc %d, status %d", rc, WEXITSTATUS(status)); + if (rc > 0) { + if (res == 0) + res = WEXITSTATUS(status); + continue; + } else if (rc < 0) { + if (res == 0) + res = rc; + continue; + } + + PRINT_WARNING("on_stpg %d (pid %d) did not finish on time - " + "sending SIGTERM", k, pids[k]); + if (res == 0) + res = -ETIMEDOUT; + rc = killpg(pids[k], SIGTERM); + if (rc < 0) + PRINT_ERROR("Failed to send SIGTERM to child %d (pid %d): %d/%s", + k, pids[k], errno, strerror(errno)); + + rc = wait_until_finished(pids[k], 1, &status, k); + if (rc != 0) + continue; + + while (1) { + PRINT_WARNING("on_stpg %d (pid %d) did not finish on time - " + "sending SIGKILL", k, pids[k]); + rc = killpg(pids[k], SIGKILL); + if (rc < 0) { + PRINT_ERROR("Failed to send SIGKILL to child %d " + "(pid %d): %d/%s", k, pids[k], errno, + strerror(errno)); + break; + } + rc = wait_until_finished(pids[k], 1, &status, k); + if (rc != 0) + break; + }; + } + + TRACE_EXIT_RES(res); + return res; +} + +static int stpg_event_loop(void) +{ + int res = 0, status; + int event_fd; + uint8_t event_user_buf[1024*1024]; + pid_t c_pid = 0; + struct pollfd pl; + struct scst_event_user *event_user = + (struct scst_event_user *)event_user_buf; + struct scst_event e1; + bool first_error = true; + + event_fd = open(SCST_EVENT_DEV, O_RDWR); + if (event_fd < 0) { + res = -errno; + PRINT_ERROR("Unable to open SCST event device %s (%s)", + SCST_EVENT_DEV, strerror(-res)); + goto out; + } + + close(stpg_init_report_pipe[0]); + + if (log_daemon) + res = write(stpg_init_report_pipe[1], &res, sizeof(res)); + + close(stpg_init_report_pipe[1]); + + memset(&pl, 0, sizeof(pl)); + pl.fd = event_fd; + pl.events = POLLIN; + + memset(&e1, 0, sizeof(e1)); + e1.event_code = SCST_EVENT_STPG_USER_INVOKE; + strncpy(e1.issuer_name, SCST_EVENT_SCST_CORE_ISSUER, + sizeof(e1.issuer_name)); + e1.issuer_name[sizeof(e1.issuer_name)-1] = '\0'; + PRINT_INFO("Setting allowed event code %d, issuer_name %s", + e1.event_code, e1.issuer_name); + + res = ioctl(event_fd, SCST_EVENT_ALLOW_EVENT, &e1); + if (res != 0) { + res = -errno; + PRINT_ERROR("SCST_EVENT_ALLOW_EVENT failed: %d (%s)", + res, strerror(-res)); + goto out; + } + + e1.event_code = SCST_EVENT_TM_FN_RECEIVED; + strncpy(e1.issuer_name, SCST_EVENT_SCST_CORE_ISSUER, + sizeof(e1.issuer_name)); + e1.issuer_name[sizeof(e1.issuer_name)-1] = '\0'; + PRINT_INFO("Setting allowed event code %d, issuer_name %s", + e1.event_code, e1.issuer_name); + res = ioctl(event_fd, SCST_EVENT_ALLOW_EVENT, &e1); + if (res != 0) { + res = -errno; + PRINT_ERROR("SCST_EVENT_ALLOW_EVENT failed: %d (%s)", + res, strerror(-res)); + goto out; + } + + 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)); + /* go through */ + case EINTR: + continue; + case EAGAIN: + TRACE_DBG("SCST_EVENT_GET_NEXT_EVENT, " + "returned EAGAIN (%d)", -res); + continue; + default: + PRINT_ERROR("SCST_EVENT_GET_NEXT_EVENT " + "failed: %d (%s)", res, strerror(-res)); + if (!first_error) + goto out; + first_error = false; + continue; + } + first_error = true; +again_poll: + res = poll(&pl, 1, c_pid > 0 ? 1 : 0); + 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: %d (%s)", + res, strerror(-res)); + goto again_poll; + } + } + } + first_error = true; +#ifdef DEBUG + PRINT_INFO("event_code %d, issuer_name %s", + event_user->out_event.event_code, + event_user->out_event.issuer_name); +#endif + if (event_user->out_event.payload_len != 0) + TRACE_BUFFER("payload", event_user->out_event.payload, + event_user->out_event.payload_len); + + if (event_user->out_event.event_code == SCST_EVENT_STPG_USER_INVOKE) { + c_pid = fork(); + if (c_pid == -1) + PRINT_ERROR("Failed to fork: %d", c_pid); + else if (c_pid == 0) { + struct scst_event_notify_done d; + + signal(SIGCHLD, SIG_DFL); + + status = handle_stpg_received(event_user); + + memset(&d, 0, sizeof(d)); + d.event_id = event_user->out_event.event_id; + d.status = status; + res = ioctl(event_fd, SCST_EVENT_NOTIFY_DONE, &d); + if (res != 0) { + res = -errno; + PRINT_ERROR("SCST_EVENT_NOTIFY_DONE " + "failed: %s (res %d)", + strerror(-res), res); + } else + PRINT_INFO("STPG event completed with status %d", status); + exit(res); + } + } else if (event_user->out_event.event_code == SCST_EVENT_TM_FN_RECEIVED) + stpg_handle_tm_received(event_user); + else + PRINT_ERROR("Unknown event %d received", event_user->out_event.event_code); + } +out: + return res; +} + +void sig_chld(int signal) +{ + /* Check just in case */ + if (signal == SIGCHLD) { + TRACE_DBG("Cleanup zombie (pid %d)", getpid()); + wait(NULL); + } +} + +int main(int argc, char **argv) +{ + int res = 0, ch, longindex; + pid_t pid; + struct sigaction sa; + + setlinebuf(stdout); + + openlog(argv[0], LOG_PID, LOG_USER); + + res = pipe(stpg_init_report_pipe); + if (res == -1) { + res = -errno; + PRINT_ERROR("Pipe failed: %d (%s)", res, strerror(-res)); + goto out; + } + + sa.sa_handler = &sig_chld; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; + if (sigaction(SIGCHLD, &sa, 0) == -1) { + PRINT_ERROR("sigaction() failed: %d/%s", errno, strerror(errno)); + exit(1); + } + + /* + * Otherwise we could die in some later write() during the event_loop() + * instead of getting EPIPE! + */ + signal(SIGPIPE, SIG_IGN); + + res = debug_init(); + if (res != 0) + goto out; + + app_name = argv[0]; + + while ((ch = getopt_long(argc, argv, "+d:fp:t:hv", + long_options, &longindex)) >= 0) { + switch (ch) { + case 'p': + stpg_path = optarg; + 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; + case 'f': + log_daemon = false; + break; + case 't': + transition_timeout = strtol(optarg, (char **)NULL, 0); + if (transition_timeout < 0) { + printf("Invalid timeout %d\n", transition_timeout); + res = -EINVAL; + goto out_done; + } + break; + case 'h': + usage(0); + goto out_done; + default: + goto out_usage; + } + } + + if (!stpg_path) + stpg_path = "/usr/local/bin/scst/scst_on_stpg"; + + if (access(stpg_path, X_OK) == -1) { + PRINT_ERROR("Script file \" %s \"does not exist or not " + "executable", stpg_path); + res = -1; + goto out_done; + } + +#ifdef DEBUG + PRINT_INFO("trace_flag %lx", trace_flag); +#endif + if (log_daemon) { + trace_flag &= ~TRACE_TIME; + trace_flag &= ~TRACE_PID; + + pid = fork(); + if (pid < 0) { + PRINT_ERROR("starting daemon failed(%d)", pid); + res = pid; + goto out_done; + } else if (pid) { + int res1 = -1; + + close(stpg_init_report_pipe[1]); + if ((unsigned)read(stpg_init_report_pipe[0], &res1, sizeof(res1)) < sizeof(res1)) { + res = -1; + goto out_done; + } else { + res = res1; + goto out_done; + } + } + + close(0); + open("/dev/null", O_RDWR); + dup2(0, 1); + dup2(0, 2); + setsid(); + } + + res = stpg_event_loop(); + +out_done: + debug_done(); + +out: + closelog(); + return res; + +out_usage: + usage(1); + goto out_done; +}