New extra config option for additional disks to monitor

This commit is contained in:
Andrea Mazzoleni 2026-03-11 13:53:42 +01:00
parent c6f6e7e448
commit 60c7b6baa3
15 changed files with 329 additions and 279 deletions

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -331,8 +331,27 @@ struct snapraid_extent {
tommy_tree_node file_node; /**< Tree sorter by <file,file_pos>. */
};
/**
* 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.
*/

View file

@ -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;

View file

@ -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.

View file

@ -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. */
}

View file

@ -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. */

View file

@ -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 */

View file

@ -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) {

View file

@ -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 \`.\`.

View file

@ -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 `.`.

View file

@ -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:

View file

@ -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

View file

@ -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