Skip to content

Commit

Permalink
Add manual scsi probe of a Lun
Browse files Browse the repository at this point in the history
This commit introduces manual probing of a specified SCSI LUN.
The LUN can be specified either via the kernel command line
`scsi.addr=<host>:<channel>:<target>:<lun>` or by adding a line to the
`initoverlayfs.config` file `scsi.addr <host>:<channel>:<target>:<lun>`.
In case both are present, the configuration takes precedence.
Any operation depends on the presence of the `scsi_mod.scan=manual` kernel
argument.
If it is not present in the kernel command line, no action will occur.
The directory `scsi_probe` contains the main functions needed to complete the
task and some related tests.
To run the tests, simply run `make` in the `scsi_probe` subdirectory.

Signed-off-by: Alessandro Carminati (Red Hat) <[email protected]>
  • Loading branch information
alessandrocarminati committed Apr 24, 2024
1 parent 755bdf0 commit 89dd5dc
Show file tree
Hide file tree
Showing 15 changed files with 362 additions and 5 deletions.
25 changes: 23 additions & 2 deletions config-parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ static inline int conf_construct(conf* c) {
c->fs.scoped = (str*)calloc(1, sizeof(str));
c->fstype.val = (str*)calloc(1, sizeof(str));
c->fstype.scoped = (str*)calloc(1, sizeof(str));
#ifdef SCSI_PROBE
c->scsi_dev.val = (str*)calloc(1, sizeof(str));
c->scsi_dev.scoped = (str*)calloc(1, sizeof(str));
#endif
return !c->bootfs.val || !c->bootfs.scoped || !c->bootfs_hint.val ||
!c->bootfs_hint.scoped || !c->bootfstype.val ||
!c->bootfstype.scoped || !c->fs.val || !c->fs.scoped ||
Expand Down Expand Up @@ -41,6 +45,9 @@ static inline void conf_set_pick(conf* c, str** line) {
.len = sizeof("bootfstype") - 1};
const str fs_str = {.c_str = "fs", .len = sizeof("fs") - 1};
const str fstype_str = {.c_str = "fstype", .len = sizeof("fstype") - 1};
#ifdef SCSI_PROBE
const str devtoscan_str = {.c_str = SCSI_ADDR_BOOT_ARG, .len = sizeof(SCSI_ADDR_BOOT_ARG) - 1};
#endif

if (is_line_key(*line, &bootfs_str))
set_conf(&c->bootfs, line, bootfs_str.len);
Expand All @@ -52,18 +59,32 @@ static inline void conf_set_pick(conf* c, str** line) {
set_conf(&c->fs, line, fs_str.len);
else if (is_line_key(*line, &fstype_str))
set_conf(&c->fstype, line, fstype_str.len);
#ifdef SCSI_PROBE
else if (is_line_key(*line, &devtoscan_str)){
(*line)->c_str[devtoscan_str.len]='=';
set_conf(&c->scsi_dev, line, devtoscan_str.len);
}
#endif
}

static inline conf* conf_print(conf* c) {
#ifdef DEBUG
printd(
"bootfs: {\"%s\", \"%s\"}, bootfs_hint: {\"%s\", \"%s\"}, bootfstype: "
"{\"%s\", \"%s\"}, fs: {\"%s\", "
"\"%s\"}, fstype: {\"%s\", \"%s\"}\n",
"\"%s\"}, fstype: {\"%s\", \"%s\"}"
#ifdef SCSI_PROBE
", scsi_dev: {\"%s\", \"%s\"}"
#endif
"\n",
c->bootfs.val->c_str, c->bootfs.scoped->c_str, c->bootfs_hint.val->c_str,
c->bootfs_hint.scoped->c_str, c->bootfstype.val->c_str,
c->bootfstype.scoped->c_str, c->fs.val->c_str, c->fs.scoped->c_str,
c->fstype.val->c_str, c->fstype.scoped->c_str);
c->fstype.val->c_str, c->fstype.scoped->c_str
#ifdef SCSI_PROBE
, c->scsi_dev.val->c_str, c->scsi_dev.scoped->c_str
#endif
);
#endif
return c;
}
Expand Down
28 changes: 27 additions & 1 deletion initoverlayfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@
#include <sys/utsname.h>
#include <sys/vfs.h>
#include <sys/wait.h>

#ifdef SCSI_PROBE
#define CMDLINE "/proc/cmdline"
#define SCSI_SYS_TMPL "/sys/class/scsi_host/host%d/scan"
#define SCSI_SYS_TMPL_SZ 40
#include "scsi_probe/scsi_probe.h"
#include <errno.h>
#endif
#include "config-parser.h"

#define fork_execl_no_wait(pid, exe, ...) \
Expand Down Expand Up @@ -518,7 +526,7 @@ static int switchroot(const char* newroot) {
}

static int mount_proc_sys_dev(void) {
if (false) {
if (true) {
if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV,
NULL)) {
print(
Expand Down Expand Up @@ -599,6 +607,24 @@ int main(int argc, char* argv[]) {
return 0;

conf_read(&conf, "/etc/initoverlayfs.conf");
#ifdef SCSI_PROBE
struct args ba;
char cmdline[50];
FILE *file;

file = fopen(CMDLINE, "r");
if (file != NULL) {
if (fgets(cmdline, sizeof(cmdline), file)) {
char *kcmdline = fetch_kernel_cmdline(CMDLINE);
conf_print(&conf);
parse_kernel_cmdline(kcmdline, &ba);
update_scsi_args(conf.scsi_dev.scoped->c_str, &ba);
trigger_scan(&ba, SCSI_SYS_TMPL, SCSI_SYS_TMPL_SZ);
}
fclose(file);
}
#endif

const bool do_bootfs = convert_bootfs(&conf, systemd);
convert_fs(&conf);
if (systemd)
Expand Down
10 changes: 9 additions & 1 deletion initoverlayfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ typedef struct conf {
pair bootfstype;
pair fs;
pair fstype;
#ifdef SCSI_PROBE
pair scsi_dev;
#endif
} conf;

static inline void cleanup_free_conf(conf* p) {
Expand All @@ -77,7 +80,12 @@ static inline void cleanup_free_conf(conf* p) {
free(p->fs.scoped->c_str);
if (p->fstype.scoped)
free(p->fstype.scoped->c_str);

#ifdef SCSI_PROBE
if (p->scsi_dev.scoped)
free(p->scsi_dev.scoped->c_str);
free(p->scsi_dev.scoped);
free(p->scsi_dev.val);
#endif
free(p->bootfs.scoped);
free(p->bootfs_hint.scoped);
free(p->bootfstype.scoped);
Expand Down
2 changes: 1 addition & 1 deletion initoverlayfs.spec.in
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Requires: dracut

%build
RPM_OPT_FLAGS="${RPM_OPT_FLAGS/-flto=auto /}"
gcc ${RPM_OPT_FLAGS} -lblkid initoverlayfs.c -o initoverlayfs
gcc ${RPM_OPT_FLAGS} -lblkid initoverlayfs.c scsi_probe/scsi_probe.c -o initoverlayfs

%install
install -D -m755 bin/initoverlayfs-install ${RPM_BUILD_ROOT}/%{_bindir}/initoverlayfs-install
Expand Down
54 changes: 54 additions & 0 deletions scsi_probe/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
CC=gcc
CFLAGS= -g
#QUIET = &>/dev/null
VALGRIND_FLAGS= --error-exitcode=1 --leak-check=full -s

tests: tests_res/parse_kernel_cmdline.exec.log tests_res/parse_kernel_cmdline.valgrind.log tests_res/fetch_kernel_cmdline.exec.log tests_res/fetch_kernel_cmdline.valgrind.log tests_res/trigger_scan.valgrind.log tests_res/trigger_scan.exec.log

################################################################
tests_res/parse_kernel_cmdline.exec.log: tests/parse_kernel_cmdline
@echo "======================= Testing... " $< " exec"
@$< > $@

tests_res/parse_kernel_cmdline.valgrind.log: tests/parse_kernel_cmdline
@echo "======================= Testing... " $< "valgrind"
@valgrind $(VALGRIND_FLAGS) $< >$@ 2>&1

tests/parse_kernel_cmdline: tests/parse_kernel_cmdline.c scsi_probe.o
@echo "======================= Build... " $<
@$(CC) $(CFLAGS) $^ -o $@

###########################################################################
tests_res/fetch_kernel_cmdline.exec.log: tests/fetch_kernel_cmdline
@echo "======================= Testing... " $< " exec"
@$< > $@

tests_res/fetch_kernel_cmdline.valgrind.log: tests/fetch_kernel_cmdline
@echo "======================= Testing... " $< "valgrind"
@valgrind $(VALGRIND_FLAGS) $< >$@ 2>&1

tests/fetch_kernel_cmdline: tests/fetch_kernel_cmdline.c scsi_probe.o
@echo "======================= Build... " $<
@$(CC) $(CFLAGS) $^ -o $@

###########################################################################
tests_res/trigger_scan.exec.log: tests/trigger_scan
@echo "======================= Testing... " $< " exec"
@$< > $@

tests_res/trigger_scan.valgrind.log: tests/trigger_scan
@echo "======================= Testing... " $< "valgrind"
@valgrind $(VALGRIND_FLAGS) $< >$@ 2>&1

tests/trigger_scan: tests/trigger_scan.c scsi_probe.o
@echo "======================= Build... " $<
@$(CC) $(CFLAGS) $^ -o $@

###########################################################################
scsi_probe.o: scsi_probe.c scsi_probe.h
@echo "======================= Build... " $<
@$(CC) $(CFLAGS) -c $< -o $@

clean:
@echo "======================= CLEAN... "
@rm -f scsi_probe.o tests/parse_kernel_cmdline tests_res/*.log
76 changes: 76 additions & 0 deletions scsi_probe/scsi_probe.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "scsi_probe.h"

static int c2i(char *str){
char *endptr;
int num;

errno = 0;
num = strtol(str, &endptr, 10);
if ((errno != 0 && num == 0) || (num > 99)) return -1;
if (endptr == str) return -1;
return num;
}

inline int update_scsi_args(const char *ba_str, struct args *ba){
char *token;
char *ba_str2;
char *tmp1, *tmp2;

if (!ba_str) return 0;
if ((ba_str2 = strdup(ba_str)) == NULL) return 0;
token = strtok(ba_str2, " ");
while (token != NULL) {
if (strncmp(token, SCSI_ADDR_BOOT_ARG, strlen(SCSI_ADDR_BOOT_ARG)) == 0) {
tmp1 = strchr(token, '=');
if (tmp1 != NULL) {
tmp2 = tmp1 + 1;
sscanf(tmp2, "%d:%d:%d:%d", &ba->scsi_host, &ba->scsi_channel, &ba->scsi_id, &ba->scsi_lun);
}
}
token = strtok(NULL, " ");
}
free(ba_str2);
return 1;
}


int parse_kernel_cmdline(const char *ba_str, struct args *ba){
memset(ba, 0, sizeof(struct args));
ba->scsi_manual=strstr(ba_str, "scsi_mod.scan=manual")?1:0;
return update_scsi_args(ba_str, ba);
}

char *fetch_kernel_cmdline(const char *cmdline_fn){
FILE *file;
char *cmdline;

file = fopen(cmdline_fn, "r");
if (file == NULL) return NULL;
cmdline = (char *) malloc(MAX_BUF);
if (! fgets(cmdline, MAX_BUF, file)) {
free(cmdline);
return NULL;
}
fclose(file);
return cmdline;
}


int trigger_scan(struct args *ba, const char *scsi_sys_tmpl, const int scsi_sys_tmpl_sz) {
FILE *file;
char buf[128];
int n;

if (ba->scsi_manual==0) return 0;
snprintf(buf, scsi_sys_tmpl_sz, scsi_sys_tmpl, ba->scsi_host);
file = fopen(buf, "w");
if (file == NULL) return 0;
n = snprintf(buf, MAX_BUF, SCSI_SYS_SCAN_STR, ba->scsi_channel, ba->scsi_id, ba->scsi_lun);
fwrite(buf, n, 1, file);
fclose(file);
return 1;
}
19 changes: 19 additions & 0 deletions scsi_probe/scsi_probe.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#define MAX_ARGS 100
#define MAX_BUF 128

#define SCSI_SYS_SCAN_STR "%d %d %d\n"

#define SCSI_ADDR_BOOT_ARG "scsi.addr"

struct args {
int scsi_manual;
int scsi_host;
int scsi_channel;
int scsi_id;
int scsi_lun;
};

int update_scsi_args(const char *, struct args *);
int parse_kernel_cmdline(const char *, struct args *);
char *fetch_kernel_cmdline(const char *);
int trigger_scan(struct args *, const char *, const int);
1 change: 1 addition & 0 deletions scsi_probe/testfiles/cmd1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pippo=1 pluto=2 paperino=3
2 changes: 2 additions & 0 deletions scsi_probe/testfiles/cmd2
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pippo=1 pluto=2 paperino=3
pippo=z pluto=x paperino=c
1 change: 1 addition & 0 deletions scsi_probe/testfiles/host0scan
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1 2 3
1 change: 1 addition & 0 deletions scsi_probe/testfiles/host2scan
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3 4 5
39 changes: 39 additions & 0 deletions scsi_probe/tests/fetch_kernel_cmdline.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "../scsi_probe.h"

#define TEST_BUFF_SIZE 200

char *results[] = {
"pippo=1 pluto=2 paperino=3\n",
"pippo=1 pluto=2 paperino=3\n",
NULL
};

char *test_patterns[] = {
"testfiles/cmd1",
"testfiles/cmd2",
"testfiles/cmd3",
};

int main(){
int i;
char *res;

for (i=0; i< sizeof(test_patterns)/sizeof(char *); i++) {
res = fetch_kernel_cmdline(test_patterns[i]);
printf( "Test pattern='%s', expected result='%s' Actual result ='%s' -> ",
test_patterns[i], results[i], res);
if (res && results[i]) {
if (strcmp(res, results[i])!=0) {
printf("Failed\n");
free(res);
return -1;
}
}
printf("Success\n");
free(res);
}
return 0;
}
40 changes: 40 additions & 0 deletions scsi_probe/tests/parse_kernel_cmdline.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include <stdio.h>
#include <string.h>
#include "../scsi_probe.h"

#define TEST_BUFF_SIZE 200

char *results[] = {
"has manual=1 host=1 addr=2:3:45",
"has manual=1 host=1 addr=2:3:45",
"has manual=1 host=0 addr=0:0:0",
"has manual=0 host=0 addr=0:0:0"
};

char *test_patterns[] = {
"pippo=1 pluto=2 scsi_mod.scan=manual scsi.addr=1:2:3:45 paperino=peppe",
"pippo=1 pluto=2 scsi_mod.scan=manual posw scsi.addr=1:2:3:45 paperino=peppe",
"pippo=1 pluto=2 scsi_mod.scan=manual paperino=peppe",
"pippo=1 pluto=2 paperino=peppe",

};

int main(){
struct args ba;
int i;
char res[TEST_BUFF_SIZE];

for (i=0; i< sizeof(test_patterns)/sizeof(char *); i++) {
if (!parse_kernel_cmdline(test_patterns[i], &ba)) return -1;
sprintf(res, "has manual=%d host=%d addr=%d:%d:%d", ba.scsi_manual, ba.scsi_host, ba.scsi_channel, ba.scsi_id, ba.scsi_lun);

printf( "Test pattern='%s', expected result='%s' Actual result ='%s' -> ",
test_patterns[i], results[i], res);
if (strcmp(res, results[i])) {
printf("Failed\n");
return -1;
}
printf("Success\n");
}
return 0;
}
Loading

0 comments on commit 89dd5dc

Please sign in to comment.