mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-25 18:40:19 -04:00
New extra config option for additional disks to monitor
This commit is contained in:
parent
c6f6e7e448
commit
60c7b6baa3
15 changed files with 329 additions and 279 deletions
3
HISTORY
3
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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
130
cmdline/mingw.c
130
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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
200
cmdline/state.c
200
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. */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
100
cmdline/unix.c
100
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) {
|
||||
|
|
|
|||
|
|
@ -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 \`.\`.
|
||||
|
|
|
|||
|
|
@ -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 `.`.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue