From 60c7b6baa34ff98a43f3a5ea9ea24347045ceb5c Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Wed, 11 Mar 2026 13:53:42 +0100 Subject: [PATCH] New extra config option for additional disks to monitor --- HISTORY | 3 +- cmdline/device.c | 37 ++++++- cmdline/elem.c | 29 +++++ cmdline/elem.h | 31 +++++- cmdline/mingw.c | 130 +--------------------- cmdline/portable.h | 2 +- cmdline/state.c | 200 +++++++++++++++++++++++++++++----- cmdline/state.h | 3 +- cmdline/thermal.c | 2 +- cmdline/unix.c | 100 +---------------- doc/snapraid.1 | 9 ++ doc/snapraid.d | 10 ++ doc/snapraid.txt | 38 ++++--- snapraid.conf.example | 7 ++ snapraid.conf.example.windows | 7 ++ 15 files changed, 329 insertions(+), 279 deletions(-) diff --git a/HISTORY b/HISTORY index 6911d3d5..cbaacadf 100644 --- a/HISTORY +++ b/HISTORY @@ -3,7 +3,8 @@ SnapRAID HISTORY 14.0rc3 ?/03/2026 ================== - * Added new --gui options for better interaction with SnapRAID Daemon. + * New 'extra' config option to define additional disks to monitor with the + 'probe' and 'smart' commands. This replaces the previous autodetection. * Removed the UAC request for Windows executable. 14.0rc2 08/03/2026 diff --git a/cmdline/device.c b/cmdline/device.c index d8fa8865..abfc457a 100644 --- a/cmdline/device.c +++ b/cmdline/device.c @@ -914,8 +914,6 @@ static void state_stat(struct snapraid_state* state, tommy_list* high) if (devinfo->split == 0 && devinfo->access_stat != 0) { if (devinfo->name[0]) log_tag("stat:%s:%" PRIu64 "\n", devinfo->name, devinfo->access_stat); - else - log_tag("stat_device:%s:%" PRIu64 "\n", devinfo->file, devinfo->access_stat); } } } @@ -1074,6 +1072,36 @@ void state_device(struct snapraid_state* state, int operation, tommy_list* filte } } + /* extra disks are never spundown or spunup */ + int include_extra = 1; + switch (operation) { + case DEVICE_UP : + case DEVICE_DOWN : + case DEVICE_DOWNIFUP : + include_extra = 0; + break; + } + if (include_extra) { + /* for all extra disks */ + for (i = state->extralist; i != 0; i = i->next) { + struct snapraid_extra* extra = i->data; + devinfo_t* entry; + + if (filterlist_disk != 0 && filter_path(filterlist_disk, 0, extra->name, 0) != 0) + continue; + + entry = calloc_nofail(1, sizeof(devinfo_t)); + + entry->device = extra->device; + device_name_set(entry, extra->name, 0); + pathcpy(entry->mount, sizeof(entry->mount), extra->dir); + pathcpy(entry->smartctl, sizeof(entry->smartctl), extra->smartctl); + memcpy(entry->smartignore, extra->smartignore, sizeof(extra->smartignore)); + + tommy_list_insert_tail(&high, &entry->node, entry); + } + } + /* with a GUI always gives time reference */ if (state->opt.gui) log_tag("unixtime:%" PRIi64 "\n", (int64_t)now); @@ -1081,9 +1109,7 @@ void state_device(struct snapraid_state* state, int operation, tommy_list* filte if (state->opt.fake_device) { ret = devtest(&high, &low, operation); } else { - int others = operation == DEVICE_SMART || operation == DEVICE_PROBE || operation == DEVICE_LIST; - - ret = devquery(&high, &low, operation, others); + ret = devquery(&high, &low, operation); } /* if the list is empty, it's not supported in this platform */ @@ -1103,7 +1129,6 @@ void state_device(struct snapraid_state* state, int operation, tommy_list* filte log_fatal(ESOFT, "%s is unsupported in this platform.\n", ope); } else { state_stat(state, &high); - state_stat(state, &low); if (operation == DEVICE_SMART) state_smart(state, state->level + tommy_list_count(&state->disklist), &low); diff --git a/cmdline/elem.c b/cmdline/elem.c index 92f61c77..8ae5418d 100644 --- a/cmdline/elem.c +++ b/cmdline/elem.c @@ -824,6 +824,35 @@ void disk_free(struct snapraid_disk* disk) free(disk); } +struct snapraid_extra* extra_alloc(const char* name, const char* dir, uint64_t dev, const char* uuid) +{ + struct snapraid_extra* extra; + unsigned i; + + extra = malloc_nofail(sizeof(struct snapraid_extra)); + pathcpy(extra->name, sizeof(extra->name), name); + pathimport(extra->dir, sizeof(extra->dir), dir); + pathcpy(extra->uuid, sizeof(extra->uuid), uuid); + + /* ensure that the dir terminate with "/" if it isn't empty */ + pathslash(extra->dir, sizeof(extra->dir)); + + extra->smartctl[0] = 0; + for (i = 0; i < SMART_IGNORE_MAX; ++i) + extra->smartignore[i] = 0; + extra->fstype[0] = 0; + extra->fslabel[0] = 0; + extra->device = dev; + + return extra; +} + +void extra_free(struct snapraid_extra* extra) +{ + free(extra); +} + + static inline void fs_lock(struct snapraid_disk* disk) { #if HAVE_THREAD diff --git a/cmdline/elem.h b/cmdline/elem.h index 0b51f747..1152df5b 100644 --- a/cmdline/elem.h +++ b/cmdline/elem.h @@ -331,8 +331,27 @@ struct snapraid_extent { tommy_tree_node file_node; /**< Tree sorter by . */ }; + /** - * Disk. + * Other disk. + */ +struct snapraid_extra { + char name[PATH_MAX]; /**< Name of the disk. */ + char dir[PATH_MAX]; /**< Mount point of the disk. It always terminates with /. */ + char smartctl[PATH_MAX]; /**< Custom command for smartctl. Empty means auto. */ + int smartignore[SMART_IGNORE_MAX]; /**< Smart attributes to ignore for this device. */ + char uuid[UUID_MAX]; /**< UUID of the disk. They are probed during the config reading. */ + char fstype[FSINFO_MAX]; /**< Filesystem type */ + char fslabel[FSINFO_MAX]; /**< Filesystem label */ + + uint64_t device; /**< Device identifier. */ + + /* nodes for data structures */ + tommy_node node; +}; + +/** + * Data disk. */ struct snapraid_disk { char name[PATH_MAX]; /**< Name of the disk. */ @@ -996,6 +1015,16 @@ struct snapraid_disk* disk_alloc(const char* name, const char* dir, uint64_t dev */ void disk_free(struct snapraid_disk* disk); +/** + * Allocate a extra disk. + */ +struct snapraid_extra* extra_alloc(const char* name, const char* dir, uint64_t dev, const char* uuid); + +/** + * Deallocate a other disk. + */ +void extra_free(struct snapraid_extra* other); + /** * Get the size of the disk in blocks. */ diff --git a/cmdline/mingw.c b/cmdline/mingw.c index 420b706a..7a8cd0c3 100644 --- a/cmdline/mingw.c +++ b/cmdline/mingw.c @@ -2445,124 +2445,6 @@ static int devstat(uint64_t device, const char* name, const char* wfile, uint64_ return 0; } -/** - * Read smartctl --scan from a stream. - * Return 0 on success. - */ -static int smartctl_scan(FILE* f, tommy_list* list) -{ - while (1) { - char buf[256]; - char* s; - - s = fgets(buf, sizeof(buf), f); - if (s == 0) - break; - - /* remove extraneous chars */ - s = strpolish(buf); - - log_tag("smartctl:scan::text: %s\n", s); - - if (*s == '/') { - char* sep = strchr(s, ' '); - if (sep) { - tommy_node* i; - const char* number; - uint64_t device; - - /* clear everything after the first space */ - *sep = 0; - - /* get the device number from the device file */ - /* note that this is Windows specific */ - /* for the format /dev/pdX of smartmontools */ - number = s; - while (*number != 0 && !isdigit(*number)) - ++number; - device = atoi(number); - - /* check if already present */ - /* comparing the device file */ - for (i = tommy_list_head(list); i != 0; i = i->next) { - devinfo_t* devinfo = i->data; - if (devinfo->device == device) - break; - } - - /* if not found */ - if (i == 0) { - devinfo_t* devinfo; - - devinfo = calloc_nofail(1, sizeof(devinfo_t)); - devinfo->device = device; - pathprint(devinfo->file, sizeof(devinfo->file), "/dev/pd%" PRIu64, devinfo->device); - pathprint(devinfo->wfile, sizeof(devinfo->wfile), "\\\\.\\PhysicalDrive%" PRIu64, devinfo->device); - - /* retrieve access stat for the low level device */ - uint64_t access_stat; - if (devstat(device, devinfo->name, devinfo->wfile, &access_stat) == 0) - devinfo->access_stat = access_stat; - - /* insert in the list */ - tommy_list_insert_tail(list, &devinfo->node, devinfo); - } - } - } - } - - return 0; -} - -/** - * Scan all the devices. - */ -static int devscan(tommy_list* list) -{ - char conv_buf[CONV_MAX]; - WCHAR cmd[MAX_PATH + 128]; - FILE* f; - int ret; - - snwprintf(cmd, sizeof(cmd), L"\"%lssmartctl.exe\" --scan-open -d pd", exedir); - - log_tag("smartctl:scan::run: %s\n", u16tou8(conv_buf, cmd)); - - f = _wpopen(cmd, L"rt"); - if (!f) { - /* LCOV_EXCL_START */ - log_fatal(errno, "Failed to run '%s' (from popen).\n", u16tou8(conv_buf, cmd)); - return -1; - /* LCOV_EXCL_STOP */ - } - - if (smartctl_scan(f, list) != 0) { - /* LCOV_EXCL_START */ - pclose(f); - return -1; - /* LCOV_EXCL_STOP */ - } - - ret = pclose(f); - - log_tag("smartctl:scan::ret: %x\n", ret); - - if (ret == -1) { - /* LCOV_EXCL_START */ - log_fatal(errno, "Failed to run '%s' (from pclose).\n", u16tou8(conv_buf, cmd)); - return -1; - /* LCOV_EXCL_STOP */ - } - if (ret != 0) { - /* LCOV_EXCL_START */ - log_fatal(errno, "Failed to run '%s' with return code %xh.\n", u16tou8(conv_buf, cmd), ret); - return -1; - /* LCOV_EXCL_STOP */ - } - - return 0; -} - /** * Get SMART attributes. */ @@ -3218,7 +3100,7 @@ static int device_thread(tommy_list* list, void* (*func)(void* arg)) return 0; } -int devquery(tommy_list* high, tommy_list* low, int operation, int others) +int devquery(tommy_list* high, tommy_list* low, int operation) { tommy_node* i; void* (*func)(void* arg) = 0; @@ -3253,16 +3135,6 @@ int devquery(tommy_list* high, tommy_list* low, int operation, int others) } } - /* add other devices */ - if (others) { - if (devscan(low) != 0) { - /* LCOV_EXCL_START */ - log_fatal(EEXTERNAL, "Failed to list other devices.\n"); - return -1; - /* LCOV_EXCL_STOP */ - } - } - switch (operation) { case DEVICE_UP : func = thread_spinup; break; case DEVICE_DOWN : func = thread_spindown; break; diff --git a/cmdline/portable.h b/cmdline/portable.h index 65b7e816..c0b1f70d 100644 --- a/cmdline/portable.h +++ b/cmdline/portable.h @@ -570,7 +570,7 @@ void device_name_set(devinfo_t* dev, const char* name, int index); * * The passed "low" device list must be already initialized. */ -int devquery(tommy_list* high, tommy_list* low, int operation, int others); +int devquery(tommy_list* high, tommy_list* low, int operation); /** * Fill with fake data the device list. diff --git a/cmdline/state.c b/cmdline/state.c index b6403ed4..abb5fc0c 100644 --- a/cmdline/state.c +++ b/cmdline/state.c @@ -194,6 +194,7 @@ void state_init(struct snapraid_state* state) state->thermal_cooldown_time = 0; tommy_list_init(&state->disklist); + tommy_list_init(&state->extralist); tommy_list_init(&state->maplist); tommy_list_init(&state->contentlist); tommy_list_init(&state->filterlist); @@ -209,6 +210,7 @@ void state_init(struct snapraid_state* state) void state_done(struct snapraid_state* state) { tommy_list_foreach(&state->disklist, (tommy_foreach_func*)disk_free); + tommy_list_foreach(&state->extralist, (tommy_foreach_func*)extra_free); tommy_list_foreach(&state->maplist, (tommy_foreach_func*)map_free); tommy_list_foreach(&state->contentlist, (tommy_foreach_func*)content_free); tommy_list_foreach(&state->filterlist, (tommy_foreach_func*)filter_free); @@ -501,6 +503,13 @@ static void state_config_check(struct snapraid_state* state, const char* path, t if (wnmatch(filter->pattern, disk->name) == 0) break; } + if (j == 0) { + for (j = state->extralist; j != 0; j = j->next) { + struct snapraid_extra* extra = j->data; + if (wnmatch(filter->pattern, extra->name) == 0) + break; + } + } if (j == 0) { /* check matching with parity disks */ for (l = 0; l < state->level; ++l) @@ -508,7 +517,7 @@ static void state_config_check(struct snapraid_state* state, const char* path, t break; if (l == state->level) { /* LCOV_EXCL_START */ - log_fatal(EUSER, "Option -d, --filter-disk %s doesn't match any data or parity disk.\n", filter->pattern); + log_fatal(EUSER, "Option -d, --filter-disk %s doesn't match any data or parity or extra disk.\n", filter->pattern); exit(EXIT_FAILURE); /* LCOV_EXCL_STOP */ } @@ -1020,6 +1029,92 @@ void state_config(struct snapraid_state* state, const char* path, const char* co disk = disk_alloc(buffer, dir, dev, uuid, skip_access); tommy_list_insert_tail(&state->disklist, &disk->node, disk); + } else if (strcmp(tag, "extra") == 0) { + char dir[PATH_MAX]; + char device[PATH_MAX]; + char uuid[UUID_MAX]; + struct snapraid_extra* extra; + uint64_t dev; + + ret = sgettok(f, buffer, sizeof(buffer)); + if (ret < 0) { + /* LCOV_EXCL_START */ + log_fatal(EUSER, "Invalid 'extra' name specification in '%s' at line %u\n", path, line); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + + if (!*buffer) { + /* LCOV_EXCL_START */ + log_fatal(EUSER, "Empty 'extra' name specification in '%s' at line %u\n", path, line); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + + sgetspace(f); + + ret = sgetlasttok(f, dir, sizeof(dir)); + if (ret < 0) { + /* LCOV_EXCL_START */ + log_fatal(EUSER, "Invalid 'extra' dir specification in '%s' at line %u\n", path, line); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + + if (!*dir) { + /* LCOV_EXCL_START */ + log_fatal(EUSER, "Empty 'extra' dir specification in '%s' at line %u\n", path, line); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + + /* get the device of the dir */ + pathimport(device, sizeof(device), dir); + + /* check if the disk name already exists */ + for (i = state->extralist; i != 0; i = i->next) { + extra = i->data; + if (strcmp(extra->name, buffer) == 0) + break; + } + if (i) { + /* LCOV_EXCL_START */ + log_fatal(EUSER, "Duplicate 'extra' name '%s' at line %u\n", buffer, line); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + + /* if the disk has to be present */ + if (!state->opt.skip_disk_access) { + struct stat st; + + if (stat(device, &st) == 0) { + dev = st.st_dev; + + /* read the uuid, if unsupported use an empty one */ + if (devuuid(dev, dir, uuid, sizeof(uuid)) != 0) { + *uuid = 0; + } + + /* fake a different UUID when testing */ + if (state->opt.fake_uuid) { + snprintf(uuid, sizeof(uuid), "fake-uuid-%d", state->opt.fake_uuid); + --state->opt.fake_uuid; + } + } else { + /* use a fake device, and mark the disk to be skipped */ + dev = 0; + *uuid = 0; + } + } else { + /* use a fake device */ + dev = 0; + *uuid = 0; + } + + extra = extra_alloc(buffer, dir, dev, uuid); + + tommy_list_insert_tail(&state->extralist, &extra->node, extra); } else if (strcmp(tag, "smartctl") == 0) { char custom[PATH_MAX]; @@ -1073,29 +1168,47 @@ void state_config(struct snapraid_state* state, const char* path, const char* co pathcpy(state->parity[level].smartctl, sizeof(state->parity[level].smartctl), custom); } else { - struct snapraid_disk* disk; - /* search the disk */ - disk = 0; + struct snapraid_disk* found_disk = 0; for (i = state->disklist; i != 0; i = i->next) { - disk = i->data; - if (strcmp(disk->name, buffer) == 0) + struct snapraid_disk* disk = i->data; + if (strcmp(disk->name, buffer) == 0) { + found_disk = disk; break; + } } - if (!disk) { - /* LCOV_EXCL_START */ - log_fatal(EUSER, "Missing disk 'smartctl' '%s' at line %u\n", buffer, line); - exit(EXIT_FAILURE); - /* LCOV_EXCL_STOP */ - } - if (disk->smartctl[0] != 0) { - /* LCOV_EXCL_START */ - log_fatal(EUSER, "Duplicate disk name '%s' at line %u\n", buffer, line); - exit(EXIT_FAILURE); - /* LCOV_EXCL_STOP */ + struct snapraid_extra* found_extra = 0; + for (i = state->extralist; i != 0; i = i->next) { + struct snapraid_extra* extra = i->data; + if (strcmp(extra->name, buffer) == 0) { + found_extra = extra; + break; + } } + if (found_disk) { + if (found_disk->smartctl[0] != 0) { + /* LCOV_EXCL_START */ + log_fatal(EUSER, "Duplicate 'smartctl' name '%s' at line %u\n", buffer, line); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } - pathcpy(disk->smartctl, sizeof(disk->smartctl), custom); + pathcpy(found_disk->smartctl, sizeof(found_disk->smartctl), custom); + } else if (found_extra) { + if (found_extra->smartctl[0] != 0) { + /* LCOV_EXCL_START */ + log_fatal(EUSER, "Duplicate 'smartctl' name '%s' at line %u\n", buffer, line); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + + pathcpy(found_extra->smartctl, sizeof(found_extra->smartctl), custom); + } else { + /* LCOV_EXCL_START */ + log_fatal(EUSER, "Unknown 'smartctl' name '%s' at line %u\n", buffer, line); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } } } else if (strcmp(tag, "smartignore") == 0) { int* smart; @@ -1122,23 +1235,33 @@ void state_config(struct snapraid_state* state, const char* path, const char* co } else if (lev_config_scan(buffer, &level, 0) == 0) { /* search for parity */ smart = state->parity[level].smartignore; } else { - struct snapraid_disk* disk; - /* search the disk */ - disk = 0; + struct snapraid_disk* found_disk = 0; for (i = state->disklist; i != 0; i = i->next) { - disk = i->data; - if (strcmp(disk->name, buffer) == 0) + struct snapraid_disk* disk = i->data; + if (strcmp(disk->name, buffer) == 0) { + found_disk = disk; break; + } } - if (!disk) { + struct snapraid_extra* found_extra = 0; + for (i = state->extralist; i != 0; i = i->next) { + struct snapraid_extra* extra = i->data; + if (strcmp(extra->name, buffer) == 0) { + found_extra = extra; + break; + } + } + if (found_disk) { + smart = found_disk->smartignore; + } else if (found_extra) { + smart = found_extra->smartignore; + } else { /* LCOV_EXCL_START */ - log_fatal(EUSER, "Missing disk 'smartctl' '%s' at line %u\n", buffer, line); + log_fatal(EUSER, "Unknown 'smartignore' name '%s' at line %u\n", buffer, line); exit(EXIT_FAILURE); /* LCOV_EXCL_STOP */ } - - smart = disk->smartignore; } int si = 0; @@ -1378,6 +1501,10 @@ void state_config(struct snapraid_state* state, const char* path, const char* co struct snapraid_disk* disk = i->data; log_tag("data:%s:%s:%s\n", esc_tag(disk->name, esc_buffer), esc_tag(disk->dir, esc_buffer1), disk->uuid); } + for (i = state->extralist; i != 0; i = i->next) { + struct snapraid_extra* extra = i->data; + log_tag("extra:%s:%s:%s\n", esc_tag(extra->name, esc_buffer), esc_tag(extra->dir, esc_buffer1), extra->uuid); + } log_tag("mode:%s\n", lev_raid_name(state->raid_mode, state->level)); for (l = 0; l < state->level; ++l) { @@ -1763,6 +1890,25 @@ void state_refresh(struct snapraid_state* state) log_tag("fsinfo_parity:%s:%" PRIu64 ":%" PRIu64 "\n", lev_config_name(l), state->parity[l].total_blocks * bs, state->parity[l].free_blocks * bs); } + /* for all extra disks */ + for (i = state->extralist; i != 0; i = i->next) { + struct snapraid_extra* extra = i->data; + uint64_t total_space; + uint64_t free_space; + int ret; + + ret = fsinfo(extra->dir, 0, 0, &total_space, &free_space, extra->fstype, sizeof(extra->fstype), extra->fslabel, sizeof(extra->fslabel)); + if (ret != 0) { + /* LCOV_EXCL_START */ + log_fatal(errno, "Error accessing disk '%s' to retrieve file-system info. %s.\n", extra->dir, strerror(errno)); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + + log_tag("fsinfo_extra:%s:%" PRIu64 ":%" PRIu64 "\n", esc_tag(extra->name, esc_buffer), total_space, free_space); + } + + /* note what we don't set need_write = 1, because we don't want */ /* to update the content file only for the free space info. */ } diff --git a/cmdline/state.h b/cmdline/state.h index e945aae6..74d11f9f 100644 --- a/cmdline/state.h +++ b/cmdline/state.h @@ -155,7 +155,8 @@ struct snapraid_state { uint32_t updated_files; /**< Files updated. Updated in scan */ tommy_list contentlist; /**< List of content files. */ - tommy_list disklist; /**< List of all the disks. */ + tommy_list disklist; /**< List of all the data disks. */ + tommy_list extralist; /**< List of all the other disks. */ tommy_list maplist; /**< List of all the disk mappings. */ tommy_list filterlist; /**< List of inclusion/exclusion. */ tommy_list importlist; /**< List of import file. */ diff --git a/cmdline/thermal.c b/cmdline/thermal.c index a5fb6b53..c9474faa 100644 --- a/cmdline/thermal.c +++ b/cmdline/thermal.c @@ -178,7 +178,7 @@ void state_thermal(struct snapraid_state* state, time_t now) if (state->opt.fake_device) { ret = devtest(&high, &low, DEVICE_SMART); } else { - ret = devquery(&high, &low, DEVICE_SMART, 0 /* only disks in the array */); + ret = devquery(&high, &low, DEVICE_SMART); } /* on error, just disable thermal gathering */ diff --git a/cmdline/unix.c b/cmdline/unix.c index 2b726ead..6ba8255d 100644 --- a/cmdline/unix.c +++ b/cmdline/unix.c @@ -1470,92 +1470,6 @@ static int devstat(dev_t device, uint64_t* count) } #endif -/** - * Scan all the devices. - * - * If a device is already in, it's not added again. - */ -#if HAVE_LINUX_DEVICE -static int devscan(tommy_list* list) -{ - char dir[PATH_MAX]; - DIR* d; - struct dirent* dd; - - pathprint(dir, sizeof(dir), "/sys/dev/block/"); - - /* check if there is a slaves list */ - d = opendir(dir); - if (d == 0) { - /* LCOV_EXCL_START */ - log_fatal(errno, "Failed to open dir '%s'.\n", dir); - return -1; - /* LCOV_EXCL_STOP */ - } - - while ((dd = readdir(d)) != 0) { - char path[PATH_MAX]; - tommy_node* i; - dev_t device; - devinfo_t* devinfo; - - if (dd->d_name[0] == '.') - continue; - - pathprint(path, sizeof(path), "/sys/dev/block/%s/device", dd->d_name); - - /* check if it's a real device */ - if (access(path, F_OK) != 0) - continue; - - pathprint(path, sizeof(path), "/sys/dev/block/%s/dev", dd->d_name); - - device = devread(path); - if (!device) { - /* LCOV_EXCL_START */ - log_tag("scan:skip: Skipping device %s because failed to read its device number.\n", dd->d_name); - continue; - /* LCOV_EXCL_STOP */ - } - - /* check if already present */ - for (i = tommy_list_head(list); i != 0; i = i->next) { - devinfo = i->data; - if (devinfo->device == device) - break; - } - - /* if already present */ - if (i != 0) - continue; - - /* get the device file */ - if (devresolve(device, path, sizeof(path)) != 0) { - /* LCOV_EXCL_START */ - log_tag("scan:skip: Skipping device %u:%u because failed to resolve.\n", major(device), minor(device)); - continue; - /* LCOV_EXCL_STOP */ - } - - devinfo = calloc_nofail(1, sizeof(devinfo_t)); - - devinfo->device = device; - pathcpy(devinfo->file, sizeof(devinfo->file), path); - - /* retrieve access stat for the low level device */ - uint64_t access_stat; - if (devstat(device, &access_stat) == 0) - devinfo->access_stat = access_stat; - - /* insert in the list */ - tommy_list_insert_tail(list, &devinfo->node, devinfo); - } - - closedir(d); - return 0; -} -#endif - /** * Get SMART attributes. */ @@ -2199,7 +2113,7 @@ static int device_thread(tommy_list* list, void* (*func)(void* arg)) return 0; } -int devquery(tommy_list* high, tommy_list* low, int operation, int others) +int devquery(tommy_list* high, tommy_list* low, int operation) { void* (*func)(void* arg) = 0; @@ -2268,20 +2182,8 @@ int devquery(tommy_list* high, tommy_list* low, int operation, int others) /* LCOV_EXCL_STOP */ } } - - /* add other devices */ - if (others) { - if (devscan(low) != 0) { - /* LCOV_EXCL_START */ - log_fatal(EEXTERNAL, "Failed to list other devices.\n"); - return -1; - /* LCOV_EXCL_STOP */ - } - } - #else (void)high; - (void)others; #endif switch (operation) { diff --git a/doc/snapraid.1 b/doc/snapraid.1 index df77e638..b669fc47 100644 --- a/doc/snapraid.1 +++ b/doc/snapraid.1 @@ -1408,6 +1408,15 @@ You can rename a disk later by changing the NAME directly in the configuration file and then running a \`sync\` command. In the case of renaming, the association is done using the stored UUID of the disks. +.SS extra NAME DIR +Defines the name and mount point of additional disks to monitor +with the \`smart\` and \`probe\` commands. +.PP +This is useful for monitoring disks that are not part of the +array but are required for the system to function, such as +the boot disk. +.PP +Note that such disks are not affected by the \`up\` and \`down\` commands. .SS nohidden Excludes all hidden files and directories. In Unix, hidden files are those starting with \`.\`. diff --git a/doc/snapraid.d b/doc/snapraid.d index 08d6fa09..1febe51a 100644 --- a/doc/snapraid.d +++ b/doc/snapraid.d @@ -1129,6 +1129,16 @@ Configuration In the case of renaming, the association is done using the stored UUID of the disks. + extra NAME DIR + Defines the name and mount point of additional disks to monitor + with the `smart` and `probe` commands. + + This is useful for monitoring disks that are not part of the + array but are required for the system to function, such as + the boot disk. + + Note that such disks are not affected by the `up` and `down` commands. + nohidden Excludes all hidden files and directories. In Unix, hidden files are those starting with `.`. diff --git a/doc/snapraid.txt b/doc/snapraid.txt index a82cb9d2..e38ceced 100644 --- a/doc/snapraid.txt +++ b/doc/snapraid.txt @@ -1203,14 +1203,26 @@ in the configuration file and then running a `sync` command. In the case of renaming, the association is done using the stored UUID of the disks. -7.6 nohidden +7.6 extra NAME DIR +------------------ + +Defines the name and mount point of additional disks to monitor +with the `smart` and `probe` commands. + +This is useful for monitoring disks that are not part of the +array but are required for the system to function, such as +the boot disk. + +Note that such disks are not affected by the `up` and `down` commands. + +7.7 nohidden ------------ Excludes all hidden files and directories. In Unix, hidden files are those starting with `.`. In Windows, they are those with the hidden attribute. -7.7 exclude/include PATTERN +7.8 exclude/include PATTERN --------------------------- Defines the file or directory patterns to exclude or include @@ -1228,7 +1240,7 @@ specifications. This option can be used multiple times. -7.8 blocksize SIZE_IN_KIBIBYTES +7.9 blocksize SIZE_IN_KIBIBYTES ------------------------------- Defines the basic block size in kibibytes for the parity. @@ -1276,8 +1288,8 @@ This results in about 1.5% extra space, approximately 60 GB for a 4 TB disk, which allows about 460,000 files on each data disk without any wasted space. -7.9 hashsize SIZE_IN_BYTES --------------------------- +7.10 hashsize SIZE_IN_BYTES +--------------------------- Defines the hash size in bytes for the saved blocks. @@ -1314,7 +1326,7 @@ Switching to both a hash size of 8 and a block size of 512, you get: RAM = (8 * 4 * 10^12) * (1+8) / (512 * 2^10) = 0.51 GiB -7.10 autosave SIZE_IN_GIGABYTES +7.11 autosave SIZE_IN_GIGABYTES ------------------------------- Automatically saves the state when syncing or scrubbing after the @@ -1322,7 +1334,7 @@ specified amount of GB processed. This option is useful to avoid restarting long `sync` commands from scratch if interrupted by a machine crash or any other event. -7.11 temp_limit TEMPERATURE_CELSIUS +7.12 temp_limit TEMPERATURE_CELSIUS ----------------------------------- Sets the maximum allowed disk temperature in Celsius. When specified, @@ -1353,14 +1365,14 @@ temperature measured at the start of the run as a fallback reference. Normally, SnapRAID shows only the temperature of the hottest disk. To display the temperature of all disks, use the -A or --stats option. -7.12 temp_sleep TIME_IN_MINUTES +7.13 temp_sleep TIME_IN_MINUTES ------------------------------- Sets the standby time, in minutes, when the temperature limit is reached. During this period, the disks remain spun down. The default is 5 minutes. -7.13 pool DIR +7.14 pool DIR ------------- Defines the pooling directory where the virtual view of the disk @@ -1368,7 +1380,7 @@ array is created using the `pool` command. The directory must already exist. -7.14 share UNC_DIR +7.15 share UNC_DIR ------------------ Defines the Windows UNC path required to access the disks remotely. @@ -1384,7 +1396,7 @@ file directory and name. This option is required only for Windows. -7.15 smartctl DISK/PARITY OPTIONS... +7.16 smartctl DISK/PARITY OPTIONS... ------------------------------------ Defines custom smartctl options to obtain the SMART attributes for @@ -1409,7 +1421,7 @@ For example: smartctl parity -d sat %s -7.16 smartignore DISK/PARITY ATTR [ATTR...] +7.17 smartignore DISK/PARITY ATTR [ATTR...] ------------------------------------------- Ignores the specified SMART attribute when computing the probability @@ -1430,7 +1442,7 @@ To ignore it only on the first parity disk: smartignore parity 197 -7.17 Examples +7.18 Examples ------------- An example of a typical configuration for Unix is: diff --git a/snapraid.conf.example b/snapraid.conf.example index ffb01aa8..f6539449 100644 --- a/snapraid.conf.example +++ b/snapraid.conf.example @@ -37,6 +37,13 @@ data d1 /mnt/disk1/ data d2 /mnt/disk2/ data d3 /mnt/disk3/ +# Defines the name and mount point of additional disks to monitor +# with the 'smart' and 'probe' commands. +# This is useful for monitoring disks that are not part of the array +# but are required for the system to function, such as the boot disk. +# Format: "other DISK_NAME DISK_MOUNT_POINT" +extra boot / + # Excludes hidden files and directories (uncomment to enable). #nohidden diff --git a/snapraid.conf.example.windows b/snapraid.conf.example.windows index 140cd680..57d758bd 100644 --- a/snapraid.conf.example.windows +++ b/snapraid.conf.example.windows @@ -37,6 +37,13 @@ data d1 K:\array\ data d2 L:\array\ data d3 M:\array\ +# Defines the name and mount point of additional disks to monitor +# with the 'smart' and 'probe' commands. +# This is useful for monitoring disks that are not part of the array +# but are required for the system to function, such as the boot disk. +# Format: "other DISK_NAME DISK_MOUNT_POINT" +extra boot C:\ + # Excludes hidden files and directories (uncomment to enable). #nohidden