mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
Add a new -s, --spin-down-on-error option
Also handle correctly chars outside the Windows codepage as arguments.
This commit is contained in:
parent
30474cfb80
commit
bb2b84d21a
6 changed files with 316 additions and 6 deletions
|
|
@ -23,6 +23,7 @@ snapraid_SOURCES = \
|
|||
raid/test.c \
|
||||
raid/tag.c \
|
||||
tommyds/tommy.c \
|
||||
cmdline/main.c \
|
||||
cmdline/snapraid.c \
|
||||
cmdline/io.c \
|
||||
cmdline/bw.c \
|
||||
|
|
|
|||
277
cmdline/main.c
Normal file
277
cmdline/main.c
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
|
||||
/*
|
||||
* Copyright (C) 2025 Andrea Mazzoleni
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "portable.h"
|
||||
|
||||
#include "snapraid.h"
|
||||
|
||||
#define MODE_DEFAULT 0
|
||||
#define MODE_SPINDOWN 1
|
||||
|
||||
#ifdef _WIN32
|
||||
#define COMMAND_LINE_MAX 32767
|
||||
|
||||
static int needs_quote(const WCHAR* arg)
|
||||
{
|
||||
while (*arg) {
|
||||
if (*arg == L' ' || *arg == L'\t' || *arg == L'"')
|
||||
return 1;
|
||||
++arg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define charcat(c) \
|
||||
do { \
|
||||
if (pos + 1 >= size) \
|
||||
return -1; \
|
||||
cmd[pos++] = (c); \
|
||||
} while (0)
|
||||
|
||||
static int argcat(WCHAR* cmd, int size, int pos, const WCHAR* arg)
|
||||
{
|
||||
/* space separator */
|
||||
if (pos != 0)
|
||||
charcat(L' ');
|
||||
|
||||
if (!needs_quote(arg)) {
|
||||
while (*arg)
|
||||
charcat(*arg++);
|
||||
return pos;
|
||||
}
|
||||
|
||||
charcat(L'"');
|
||||
|
||||
while (*arg) {
|
||||
int bl = 0;
|
||||
while (*arg == L'\\') {
|
||||
++arg;
|
||||
++bl;
|
||||
}
|
||||
|
||||
if (*arg == 0) {
|
||||
bl = bl * 2;
|
||||
while (bl--)
|
||||
charcat(L'\\');
|
||||
} else if (*arg == '"') {
|
||||
bl = bl * 2 + 1;
|
||||
while (bl--)
|
||||
charcat(L'\\');
|
||||
charcat(L'"');
|
||||
++arg;
|
||||
} else {
|
||||
while (bl--)
|
||||
charcat(L'\\');
|
||||
charcat(*arg);
|
||||
++arg;
|
||||
}
|
||||
}
|
||||
|
||||
/* ending quote */
|
||||
charcat(L'"');
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static char* argutf8(const WCHAR* arg)
|
||||
{
|
||||
size_t len = wcslen(arg);
|
||||
char* utf8_arg;
|
||||
DWORD utf8_len;
|
||||
DWORD res;
|
||||
|
||||
utf8_len = WideCharToMultiByte(CP_UTF8, 0, arg, len + 1 /* include final null */, 0, 0, 0, 0);
|
||||
|
||||
utf8_arg = malloc(utf8_len);
|
||||
if (!utf8_arg)
|
||||
return 0;
|
||||
|
||||
res = WideCharToMultiByte(CP_UTF8, 0, arg, len + 1 /* include final null */, utf8_arg, utf8_len, 0, 0);
|
||||
if (res != utf8_len)
|
||||
return 0;
|
||||
|
||||
return utf8_arg;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int wide_argc;
|
||||
WCHAR** wide_argv;
|
||||
int utf8_argc;
|
||||
char** utf8_argv;
|
||||
WCHAR app_buffer[COMMAND_LINE_MAX];
|
||||
WCHAR cmd_buffer[COMMAND_LINE_MAX];
|
||||
DWORD res;
|
||||
int i;
|
||||
int pos;
|
||||
int mode;
|
||||
int ret;
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
res = GetModuleFileNameW(NULL, app_buffer, sizeof(app_buffer));
|
||||
if (res == 0 || res >= sizeof(app_buffer)) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
wide_argv = CommandLineToArgvW(GetCommandLineW(), &wide_argc);
|
||||
if (!wide_argv) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
utf8_argc = 0;
|
||||
utf8_argv = malloc((wide_argc + 1) * sizeof(void*));
|
||||
if (!utf8_argv) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
pos = argcat(cmd_buffer, sizeof(cmd_buffer), 0, app_buffer);
|
||||
if (pos < 0) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
utf8_argv[0] = argutf8(app_buffer);
|
||||
if (!utf8_argv[0]) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
utf8_argc = 1;
|
||||
|
||||
mode = MODE_DEFAULT;
|
||||
for (i = 1; i < wide_argc; i++) {
|
||||
if (wcscmp(wide_argv[i], L"-s") == 0 || wcscmp(wide_argv[i], L"--spin-down-on-error") == 0) {
|
||||
mode = MODE_SPINDOWN;
|
||||
} else {
|
||||
pos = argcat(cmd_buffer, sizeof(cmd_buffer), 0, wide_argv[i]);
|
||||
if (pos < 0) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
utf8_argv[utf8_argc] = argutf8(wide_argv[i]);
|
||||
if (!utf8_argv[utf8_argc]) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
++utf8_argc;
|
||||
}
|
||||
}
|
||||
utf8_argv[utf8_argc] = 0;
|
||||
cmd_buffer[pos] = 0;
|
||||
|
||||
LocalFree(wide_argv);
|
||||
|
||||
if (mode == MODE_DEFAULT) {
|
||||
ret = snapraid_main(utf8_argc, utf8_argv);
|
||||
} else {
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
if (!CreateProcessW(app_buffer, cmd_buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
|
||||
GetExitCodeProcess(pi.hProcess, &res);
|
||||
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
ret = res;
|
||||
|
||||
if (ret != 0) {
|
||||
char* spindown_argv[3];
|
||||
spindown_argv[0] = utf8_argv[0];
|
||||
spindown_argv[1] = "down";
|
||||
spindown_argv[2] = 0;
|
||||
snapraid_main(2, spindown_argv);
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0;i < utf8_argc; ++i)
|
||||
free(utf8_argv[i]);
|
||||
free(utf8_argv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int mode;
|
||||
int i, j;
|
||||
int ret;
|
||||
|
||||
mode = MODE_DEFAULT;
|
||||
j = 1;
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--spin-down-on-error") == 0 ) {
|
||||
mode = MODE_SPINDOWN;
|
||||
} else {
|
||||
argv[j] = argv[i];
|
||||
++j;
|
||||
}
|
||||
}
|
||||
argc = j;
|
||||
argv[argc] = 0;
|
||||
|
||||
if (mode == MODE_DEFAULT) {
|
||||
ret = snapraid_main(argc, argv);
|
||||
} else {
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
execv(argv[0], argv);
|
||||
|
||||
/* here it's an error */
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
int status;
|
||||
|
||||
/* parent process - wait for child */
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
ret = WEXITSTATUS(status);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
/* terminated by signal */
|
||||
ret = 128 + WTERMSIG(status);
|
||||
} else {
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
char* spindown_argv[3];
|
||||
spindown_argv[0] = argv[0];
|
||||
spindown_argv[1] = "down";
|
||||
spindown_argv[2] = 0;
|
||||
snapraid_main(2, spindown_argv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -340,7 +340,7 @@ struct option long_options[] = {
|
|||
{ "speed-test", 0, 0, 'T' }, /* undocumented speed test command */
|
||||
{ "gen-conf", 1, 0, 'C' },
|
||||
{ "verbose", 0, 0, 'v' },
|
||||
{ "quiet", 0, 0, 'q' }, /* undocumented quiet option */
|
||||
{ "quiet", 0, 0, 'q' },
|
||||
{ "gui", 0, 0, 'G' }, /* undocumented GUI interface option */
|
||||
{ "help", 0, 0, 'H' },
|
||||
{ "version", 0, 0, 'V' },
|
||||
|
|
@ -486,6 +486,11 @@ struct option long_options[] = {
|
|||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Free letters: AgIjJkKMnPQrtuxXWz
|
||||
*
|
||||
* The 's' letter is used in main.c
|
||||
*/
|
||||
#define OPTIONS "c:f:d:mebp:o:S:B:L:i:l:ZEUDNFRahTC:vqHVGw:"
|
||||
|
||||
volatile int global_interrupt = 0;
|
||||
|
|
@ -540,7 +545,7 @@ void signal_init(void)
|
|||
#define OPERATION_SMART 17
|
||||
#define OPERATION_PROBE 18
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
int snapraid_main(int argc, char* argv[])
|
||||
{
|
||||
int c;
|
||||
struct snapraid_option opt;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
void speed(int period);
|
||||
void selftest(void);
|
||||
int snapraid_main(int argc, char* argv[]);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
17
snapraid.1
17
snapraid.1
|
|
@ -26,7 +26,7 @@ snapraid [\-c, \-\-conf CONFIG]
|
|||
.PD 0
|
||||
.PP
|
||||
.PD
|
||||
[\-w, \-\-bw\-limit RATE]
|
||||
[\-s, \-\-spin\-down\-on\-error] [\-w, \-\-bw\-limit RATE]
|
||||
.PD 0
|
||||
.PP
|
||||
.PD
|
||||
|
|
@ -740,7 +740,12 @@ Spins down all the disks of the array.
|
|||
This command uses the \[dq]smartctl\[dq] tool, and it\'s equivalent to run
|
||||
\[dq]smartctl \-s standby,now\[dq] on all the devices.
|
||||
.PP
|
||||
You can spin\-down only some specific disks using the \-d, \-\-filter\-disk option.
|
||||
You can spin\-down only some specific disks using the \-d, \-\-filter\-disk
|
||||
option.
|
||||
.PP
|
||||
To automatically spin down on error, you can use the option
|
||||
\-s, \-\-spin\-down\-on\-error with any other command, which is equivalent
|
||||
to running \[dq]down\[dq] manually when an error occurs.
|
||||
.PP
|
||||
Nothing is modified.
|
||||
.SS diff
|
||||
|
|
@ -1079,6 +1084,14 @@ The files are read also in sub\-directories and they are
|
|||
identified regardless of their name.
|
||||
This option can be used only with \[dq]check\[dq] and \[dq]fix\[dq].
|
||||
.TP
|
||||
.B \-s, \-\-spin\-down\-on\-error
|
||||
On any error, spin down all managed disks before exiting with
|
||||
a non\-zero status code. This prevents the drives from
|
||||
remaining active and spinning after an aborted operation,
|
||||
helping to avoid unnecessary heat build\-up and power
|
||||
consumption. Use this option to ensure disks are safely
|
||||
stopped even when a command fails.
|
||||
.TP
|
||||
.B \-w, \-\-bw\-limit RATE
|
||||
Applies a global bandwidth limit for all disks. The RATE is
|
||||
the number of bytes per second. You can specify a multiplier
|
||||
|
|
|
|||
17
snapraid.d
17
snapraid.d
|
|
@ -8,7 +8,7 @@ Synopsis
|
|||
: [-a, --audit-only] [-h, --pre-hash] [-i, --import DIR]
|
||||
: [-p, --plan PERC|bad|new|full]
|
||||
: [-o, --older-than DAYS] [-l, --log FILE]
|
||||
: [-w, --bw-limit RATE]
|
||||
: [-s, --spin-down-on-error] [-w, --bw-limit RATE]
|
||||
: [-Z, --force-zero] [-E, --force-empty]
|
||||
: [-U, --force-uuid] [-D, --force-device]
|
||||
: [-N, --force-nocopy] [-F, --force-full]
|
||||
|
|
@ -471,7 +471,12 @@ Commands
|
|||
This command uses the "smartctl" tool, and it's equivalent to run
|
||||
"smartctl -s standby,now" on all the devices.
|
||||
|
||||
You can spin-down only some specific disks using the -d, --filter-disk option.
|
||||
You can spin-down only some specific disks using the -d, --filter-disk
|
||||
option.
|
||||
|
||||
To automatically spin down on error, you can use the option
|
||||
-s, --spin-down-on-error with any other command, which is equivalent
|
||||
to running "down" manually when an error occurs.
|
||||
|
||||
Nothing is modified.
|
||||
|
||||
|
|
@ -804,6 +809,14 @@ Options
|
|||
identified regardless of their name.
|
||||
This option can be used only with "check" and "fix".
|
||||
|
||||
-s, --spin-down-on-error
|
||||
On any error, spin down all managed disks before exiting with
|
||||
a non-zero status code. This prevents the drives from
|
||||
remaining active and spinning after an aborted operation,
|
||||
helping to avoid unnecessary heat build-up and power
|
||||
consumption. Use this option to ensure disks are safely
|
||||
stopped even when a command fails.
|
||||
|
||||
-w, --bw-limit RATE
|
||||
Applies a global bandwidth limit for all disks. The RATE is
|
||||
the number of bytes per second. You can specify a multiplier
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue