diff --git a/cov/cmdline/bw.c.func-sort-c.html b/cov/cmdline/bw.c.func-sort-c.html new file mode 100644 index 00000000..f3900f9d --- /dev/null +++ b/cov/cmdline/bw.c.func-sort-c.html @@ -0,0 +1,80 @@ + + + + + + + LCOV - lcov.info - cmdline/bw.c - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cmdline - bw.c (source / functions)HitTotalCoverage
Test:lcov.infoLines:1414100.0 %
Date:2025-10-28 11:59:11Functions:22100.0 %
+
+ +
+ + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
bw_init215
bw_limit9469208
+
+
+ + + +
Generated by: LCOV version 1.0
+
+ + + diff --git a/cov/cmdline/bw.c.func.html b/cov/cmdline/bw.c.func.html new file mode 100644 index 00000000..8541b63e --- /dev/null +++ b/cov/cmdline/bw.c.func.html @@ -0,0 +1,80 @@ + + + + + + + LCOV - lcov.info - cmdline/bw.c - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cmdline - bw.c (source / functions)HitTotalCoverage
Test:lcov.infoLines:1414100.0 %
Date:2025-10-28 11:59:11Functions:22100.0 %
+
+ +
+ + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
bw_init215
bw_limit9469208
+
+
+ + + +
Generated by: LCOV version 1.0
+
+ + + diff --git a/cov/cmdline/bw.c.gcov.html b/cov/cmdline/bw.c.gcov.html new file mode 100644 index 00000000..fec2d844 --- /dev/null +++ b/cov/cmdline/bw.c.gcov.html @@ -0,0 +1,123 @@ + + + + + + + LCOV - lcov.info - cmdline/bw.c + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cmdline - bw.c (source / functions)HitTotalCoverage
Test:lcov.infoLines:1414100.0 %
Date:2025-10-28 11:59:11Functions:22100.0 %
+
+ + + + + + + + +

+
          Line data    Source code
+
+       1             : /*
+       2             :  * Copyright (C) 2016 Andrea Mazzoleni
+       3             :  *
+       4             :  * This program is free software: you can redistribute it and/or modify
+       5             :  * it under the terms of the GNU General Public License as published by
+       6             :  * the Free Software Foundation, either version 3 of the License, or
+       7             :  * (at your option) any later version.
+       8             :  *
+       9             :  * This program is distributed in the hope that it will be useful,
+      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      12             :  * GNU General Public License for more details.
+      13             :  *
+      14             :  * You should have received a copy of the GNU General Public License
+      15             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+      16             :  */
+      17             : 
+      18             : #include "portable.h"
+      19             : 
+      20             : #include "bw.h"
+      21             : 
+      22         215 : void bw_init(struct snapraid_bw* bw, uint64_t limit)
+      23             : {
+      24         215 :         bw->limit = limit;
+      25         215 :         bw->total = 0;
+      26         215 :         bw->start = tick_ms();
+      27         215 : }
+      28             : 
+      29     9469208 : void bw_limit(struct snapraid_bw* bw, uint64_t bytes)
+      30             : {
+      31     9469208 :         if (!bw || bw->limit == 0)
+      32     9469152 :                 return;
+      33             : 
+      34          56 :         uint64_t elapsed = tick_ms() - bw->start;
+      35             :         uint64_t done;
+      36             :         uint64_t eta;
+      37             : 
+      38          56 :         done = __atomic_fetch_add(&bw->total, bytes, __ATOMIC_SEQ_CST);
+      39             : 
+      40          56 :         eta = done * 1000 / bw->limit;
+      41             : 
+      42          56 :         if (eta > elapsed) {
+      43          49 :                 eta -= elapsed;
+      44          49 :                 usleep(eta * 1000);
+      45             :         }
+      46             : }
+      47             : 
+
+
+
+ + + + +
Generated by: LCOV version 1.0
+
+ + + diff --git a/cov/cmdline/main.c.func-sort-c.html b/cov/cmdline/main.c.func-sort-c.html new file mode 100644 index 00000000..b6a45627 --- /dev/null +++ b/cov/cmdline/main.c.func-sort-c.html @@ -0,0 +1,80 @@ + + + + + + + LCOV - lcov.info - cmdline/main.c - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cmdline - main.c (source / functions)HitTotalCoverage
Test:lcov.infoLines:516282.3 %
Date:2025-10-28 11:59:11Functions:22100.0 %
+
+ +
+ + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
get_argv01
main280
+
+
+ + + +
Generated by: LCOV version 1.0
+
+ + + diff --git a/cov/cmdline/main.c.func.html b/cov/cmdline/main.c.func.html new file mode 100644 index 00000000..5a645411 --- /dev/null +++ b/cov/cmdline/main.c.func.html @@ -0,0 +1,80 @@ + + + + + + + LCOV - lcov.info - cmdline/main.c - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cmdline - main.c (source / functions)HitTotalCoverage
Test:lcov.infoLines:516282.3 %
Date:2025-10-28 11:59:11Functions:22100.0 %
+
+ +
+ + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
get_argv01
main280
+
+
+ + + +
Generated by: LCOV version 1.0
+
+ + + diff --git a/cov/cmdline/main.c.gcov.html b/cov/cmdline/main.c.gcov.html new file mode 100644 index 00000000..06ae0e1d --- /dev/null +++ b/cov/cmdline/main.c.gcov.html @@ -0,0 +1,518 @@ + + + + + + + LCOV - lcov.info - cmdline/main.c + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cmdline - main.c (source / functions)HitTotalCoverage
Test:lcov.infoLines:516282.3 %
Date:2025-10-28 11:59:11Functions:22100.0 %
+
+ + + + + + + + +

+
          Line data    Source code
+
+       1             : /*
+       2             :  * Copyright (C) 2025 Andrea Mazzoleni
+       3             :  *
+       4             :  * This program is free software: you can redistribute it and/or modify
+       5             :  * it under the terms of the GNU General Public License as published by
+       6             :  * the Free Software Foundation, either version 3 of the License, or
+       7             :  * (at your option) any later version.
+       8             :  *
+       9             :  * This program is distributed in the hope that it will be useful,
+      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      12             :  * GNU General Public License for more details.
+      13             :  *
+      14             :  * You should have received a copy of the GNU General Public License
+      15             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+      16             :  */
+      17             : 
+      18             : #include "portable.h"
+      19             : 
+      20             : #include "snapraid.h"
+      21             : 
+      22             : #define MODE_DEFAULT 0
+      23             : #define MODE_SPINDOWN 1
+      24             : 
+      25             : #ifdef _WIN32
+      26             : #define COMMAND_LINE_MAX 32767
+      27             : 
+      28             : static int needs_quote(const WCHAR* arg)
+      29             : {
+      30             :         while (*arg) {
+      31             :                 if (*arg == L' ' || *arg == L'\t' || *arg == L'"')
+      32             :                         return 1;
+      33             :                 ++arg;
+      34             :         }
+      35             : 
+      36             :         return 0;
+      37             : }
+      38             : 
+      39             : #define charcat(c) \
+      40             :         do { \
+      41             :                 if (pos + 1 >= size) \
+      42             :                         return -1; \
+      43             :                 cmd[pos++] = (c); \
+      44             :         } while (0)
+      45             : 
+      46             : static int argcat(WCHAR* cmd, int size, int pos, const WCHAR* arg)
+      47             : {
+      48             :         int has_quote;
+      49             : 
+      50             :         /* space separator */
+      51             :         if (pos != 0)
+      52             :                 charcat(L' ');
+      53             : 
+      54             :         has_quote = needs_quote(arg);
+      55             : 
+      56             :         if (!has_quote) {
+      57             :                 while (*arg)
+      58             :                         charcat(*arg++);
+      59             :         } else {
+      60             :                 /* starting quote */
+      61             :                 charcat(L'"');
+      62             : 
+      63             :                 while (*arg) {
+      64             :                         int bl = 0;
+      65             :                         while (*arg == L'\\') {
+      66             :                                 ++arg;
+      67             :                                 ++bl;
+      68             :                         }
+      69             : 
+      70             :                         if (*arg == 0) {
+      71             :                                 /* double backslashes before closing quote */
+      72             :                                 bl = bl * 2;
+      73             :                                 while (bl--)
+      74             :                                         charcat(L'\\');
+      75             :                         } else if (*arg == '"') {
+      76             :                                 /* double backslashes + escape the quote */
+      77             :                                 bl = bl * 2 + 1;
+      78             :                                 while (bl--)
+      79             :                                         charcat(L'\\');
+      80             :                                 charcat(L'"');
+      81             :                                 ++arg;
+      82             :                         } else {
+      83             :                                 /* normal backslashes */
+      84             :                                 while (bl--)
+      85             :                                         charcat(L'\\');
+      86             :                                 charcat(*arg);
+      87             :                                 ++arg;
+      88             :                         }
+      89             :                 }
+      90             : 
+      91             :                 /* ending quote */
+      92             :                 charcat(L'"');
+      93             :         }
+      94             : 
+      95             :         return pos;
+      96             : }
+      97             : 
+      98             : static char* argutf8(const WCHAR* arg)
+      99             : {
+     100             :         size_t len = wcslen(arg);
+     101             :         char* utf8_arg;
+     102             :         DWORD utf8_len;
+     103             :         DWORD res;
+     104             : 
+     105             :         utf8_len = WideCharToMultiByte(CP_UTF8, 0, arg, len + 1 /* include final null */, 0, 0, 0, 0);
+     106             : 
+     107             :         utf8_arg = malloc(utf8_len);
+     108             :         if (!utf8_arg)
+     109             :                 return 0;
+     110             : 
+     111             :         res = WideCharToMultiByte(CP_UTF8, 0, arg, len + 1 /* include final null */, utf8_arg, utf8_len, 0, 0);
+     112             :         if (res != utf8_len)
+     113             :                 return 0;
+     114             : 
+     115             :         return utf8_arg;
+     116             : }
+     117             : 
+     118             : /* Global variable to store child process handle */
+     119             : static HANDLE child_process = NULL;
+     120             : 
+     121             : /* Console control handler - forwards Ctrl+C, Ctrl+Break to child */
+     122             : static BOOL WINAPI console_handler(DWORD ctrl_type)
+     123             : {
+     124             :         /* if no child, default behavior */
+     125             :         if (child_process == NULL)
+     126             :                 return FALSE;
+     127             : 
+     128             :         switch (ctrl_type) {
+     129             :         case CTRL_C_EVENT :
+     130             :         case CTRL_BREAK_EVENT :
+     131             :                 /*
+     132             :                  * Return TRUE to prevent parent termination. The child process
+     133             :                  * will receive these events automatically because it's attached
+     134             :                  * to the same console, so we don't need to forward them.
+     135             :                  */
+     136             :                 return TRUE; /* signal handled, don't terminate parent */
+     137             :         case CTRL_CLOSE_EVENT :
+     138             :         case CTRL_LOGOFF_EVENT :
+     139             :         case CTRL_SHUTDOWN_EVENT :
+     140             :                 /*
+     141             :                  * Return TRUE to prevent our termination while child handles shutdown.
+     142             :                  * The child receives these events automatically (same console).
+     143             :                  * Note: Windows will forcibly kill us after a timeout regardless
+     144             :                  * of returning TRUE: ~5 seconds for CLOSE_EVENT and LOGOFF_EVENT,
+     145             :                  * ~5-20 seconds for SHUTDOWN_EVENT (configurable in registry).
+     146             :                  */
+     147             :                 return TRUE; /* signal handled, but Windows will kill us after timeout */
+     148             :         default :
+     149             :                 return FALSE;
+     150             :         }
+     151             : }
+     152             : 
+     153             : int main(int argc, char* argv[])
+     154             : {
+     155             :         int wide_argc;
+     156             :         WCHAR** wide_argv;
+     157             :         int utf8_argc;
+     158             :         char** utf8_argv;
+     159             :         WCHAR app_buffer[COMMAND_LINE_MAX];
+     160             :         WCHAR cmd_buffer[COMMAND_LINE_MAX];
+     161             :         DWORD res;
+     162             :         int i;
+     163             :         int pos;
+     164             :         int mode;
+     165             :         int ret;
+     166             : 
+     167             :         (void)argc;
+     168             :         (void)argv;
+     169             : 
+     170             :         res = GetModuleFileNameW(NULL, app_buffer, sizeof(app_buffer));
+     171             :         if (res == 0 || res >= sizeof(app_buffer)) {
+     172             :                 exit(EXIT_FAILURE);
+     173             :         }
+     174             : 
+     175             :         wide_argv = CommandLineToArgvW(GetCommandLineW(), &wide_argc);
+     176             :         if (!wide_argv) {
+     177             :                 exit(EXIT_FAILURE);
+     178             :         }
+     179             : 
+     180             :         utf8_argc = 0;
+     181             :         utf8_argv = malloc((wide_argc + 1) * sizeof(void*));
+     182             :         if (!utf8_argv) {
+     183             :                 exit(EXIT_FAILURE);
+     184             :         }
+     185             : 
+     186             :         pos = argcat(cmd_buffer, sizeof(cmd_buffer), 0, app_buffer);
+     187             :         if (pos < 0) {
+     188             :                 exit(EXIT_FAILURE);
+     189             :         }
+     190             : 
+     191             :         utf8_argv[0] = argutf8(app_buffer);
+     192             :         if (!utf8_argv[0]) {
+     193             :                 exit(EXIT_FAILURE);
+     194             :         }
+     195             :         utf8_argc = 1;
+     196             : 
+     197             :         mode = MODE_DEFAULT;
+     198             :         for (i = 1; i < wide_argc; i++) {
+     199             :                 if (wcscmp(wide_argv[i], L"-s") == 0 || wcscmp(wide_argv[i], L"--spin-down-on-error") == 0) {
+     200             :                         mode = MODE_SPINDOWN;
+     201             :                 } else {
+     202             :                         pos = argcat(cmd_buffer, sizeof(cmd_buffer), pos, wide_argv[i]);
+     203             :                         if (pos < 0) {
+     204             :                                 exit(EXIT_FAILURE);
+     205             :                         }
+     206             :                         utf8_argv[utf8_argc] = argutf8(wide_argv[i]);
+     207             :                         if (!utf8_argv[utf8_argc]) {
+     208             :                                 exit(EXIT_FAILURE);
+     209             :                         }
+     210             :                         ++utf8_argc;
+     211             :                 }
+     212             :         }
+     213             :         utf8_argv[utf8_argc] = 0;
+     214             :         cmd_buffer[pos] = 0;
+     215             : 
+     216             :         LocalFree(wide_argv);
+     217             : 
+     218             :         if (mode == MODE_DEFAULT) {
+     219             :                 ret = snapraid_main(utf8_argc, utf8_argv);
+     220             :         } else {
+     221             :                 STARTUPINFOW si;
+     222             :                 PROCESS_INFORMATION pi;
+     223             :                 DWORD wait;
+     224             : 
+     225             :                 /* install console control handler */
+     226             :                 if (!SetConsoleCtrlHandler(console_handler, TRUE)) {
+     227             :                         fprintf(stderr, "Failed to set console handler\n");
+     228             :                         exit(EXIT_FAILURE);
+     229             :                 }
+     230             : 
+     231             :                 ZeroMemory(&si, sizeof(si));
+     232             :                 si.cb = sizeof(si);
+     233             :                 ZeroMemory(&pi, sizeof(pi));
+     234             : 
+     235             :                 if (!CreateProcessW(app_buffer, cmd_buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
+     236             :                         fprintf(stderr, "Failed to exec SnapRAID\n");
+     237             :                         exit(EXIT_FAILURE);
+     238             :                 }
+     239             : 
+     240             :                 /* store child process handle for signal handler */
+     241             :                 child_process = pi.hProcess;
+     242             : 
+     243             :                 wait = WaitForSingleObject(pi.hProcess, INFINITE);
+     244             :                 if (wait != WAIT_OBJECT_0) {
+     245             :                         fprintf(stderr, "WaitForSingleObject failed: %lu\n", (unsigned long)GetLastError());
+     246             :                         TerminateProcess(pi.hProcess, 1);
+     247             :                         CloseHandle(pi.hThread);
+     248             :                         CloseHandle(pi.hProcess);
+     249             :                         return 1;
+     250             :                 }
+     251             : 
+     252             :                 /* clear child process handle */
+     253             :                 child_process = NULL;
+     254             : 
+     255             :                 if (!GetExitCodeProcess(pi.hProcess, &res)) {
+     256             :                         fprintf(stderr, "GetExitCodeProcess failed: %lu\n", (unsigned long)GetLastError());
+     257             :                         CloseHandle(pi.hThread);
+     258             :                         CloseHandle(pi.hProcess);
+     259             :                         return 1;
+     260             :                 }
+     261             : 
+     262             :                 CloseHandle(pi.hThread);
+     263             :                 CloseHandle(pi.hProcess);
+     264             : 
+     265             :                 ret = res;
+     266             : 
+     267             :                 if (ret != 0) {
+     268             :                         char* spindown_argv[3];
+     269             :                         spindown_argv[0] = utf8_argv[0];
+     270             :                         spindown_argv[1] = "down";
+     271             :                         spindown_argv[2] = 0;
+     272             : 
+     273             :                         /* sync all disks */
+     274             :                         printf("Flush...\n");
+     275             :                         if (sync() != 0)
+     276             :                                 printf("WARNING! Failed to flush disks!\n");
+     277             : 
+     278             :                         snapraid_main(2, spindown_argv);
+     279             :                 }
+     280             :         }
+     281             : 
+     282             :         for (i = 0; i < utf8_argc; ++i)
+     283             :                 free(utf8_argv[i]);
+     284             :         free(utf8_argv);
+     285             : 
+     286             :         return ret;
+     287             : }
+     288             : #else
+     289             : 
+     290             : char full_argv0[PATH_MAX];
+     291             : 
+     292           1 : const char* get_argv0(const char* argv0)
+     293             : {
+     294           1 :         ssize_t len = readlink("/proc/self/exe", full_argv0, sizeof(full_argv0) - 1);
+     295           1 :         if (len != -1) {
+     296           1 :                 full_argv0[len] = '\0';
+     297           1 :                 return full_argv0;
+     298             :         } else {
+     299             : #ifdef __APPLE__
+     300             :                 uint32_t size = sizeof(full_argv0);
+     301             :                 if (_NSGetExecutablePath(full_argv0, &size) == 0)
+     302             :                         return full_argv0;
+     303             : #endif
+     304             :         }
+     305           0 :         return argv0;
+     306             : }
+     307             : 
+     308             : /* global variable to store child PID for signal handler */
+     309             : static volatile pid_t child_pid = 0;
+     310             : 
+     311             : /* signal handler that forwards signals to child */
+     312             : /* LCOV_EXCL_START */
+     313             : static void forward_signal(int sig)
+     314             : {
+     315             :         if (child_pid > 0) {
+     316             :                 kill(child_pid, sig);
+     317             :         }
+     318             : }
+     319             : /* LCOV_EXCL_STOP */
+     320             : 
+     321         280 : int main(int argc, char* argv[])
+     322             : {
+     323             :         int mode;
+     324             :         int i, j;
+     325             :         int ret;
+     326             : 
+     327         280 :         mode = MODE_DEFAULT;
+     328         280 :         j = 1;
+     329        4192 :         for (i = 1; i < argc; ++i) {
+     330        3912 :                 if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--spin-down-on-error") == 0) {
+     331           1 :                         mode = MODE_SPINDOWN;
+     332             :                 } else {
+     333        3911 :                         argv[j] = argv[i];
+     334        3911 :                         ++j;
+     335             :                 }
+     336             :         }
+     337         280 :         argc = j;
+     338         280 :         argv[argc] = 0;
+     339             : 
+     340         280 :         if (mode == MODE_DEFAULT) {
+     341         279 :                 ret = snapraid_main(argc, argv);
+     342             :         } else {
+     343             :                 struct sigaction sa;
+     344             : 
+     345             :                 /* set up signal handler to ignore signals */
+     346           1 :                 sa.sa_handler = forward_signal;
+     347           1 :                 sigemptyset(&sa.sa_mask);
+     348           1 :                 sa.sa_flags = SA_RESTART; /* restart interrupted system calls */
+     349             : 
+     350             :                 /* install forwarding handler for signals */
+     351           1 :                 sigaction(SIGINT, &sa, NULL); /* Ctrl+C */
+     352           1 :                 sigaction(SIGQUIT, &sa, NULL); /* Ctrl+\ */
+     353           1 :                 sigaction(SIGTERM, &sa, NULL); /* termination */
+     354           1 :                 sigaction(SIGHUP, &sa, NULL); /* hangup */
+     355           1 :                 sigaction(SIGUSR1, &sa, NULL); /* user-defined 1 */
+     356           1 :                 sigaction(SIGUSR2, &sa, NULL); /* user-defined 2 */
+     357             : 
+     358           1 :                 pid_t pid = fork();
+     359           2 :                 if (pid == -1) {
+     360             :                         /* LCOV_EXCL_START */
+     361             :                         perror("Failed to fork SnapRAID");
+     362             :                         exit(EXIT_FAILURE);
+     363             :                         /* LCOV_EXCL_STOP */
+     364             :                 }
+     365             : 
+     366           2 :                 if (pid == 0) {
+     367             :                         /* child process */
+     368             : 
+     369             :                         /* restore default signal handlers */
+     370           1 :                         signal(SIGINT, SIG_DFL);
+     371           1 :                         signal(SIGQUIT, SIG_DFL);
+     372           1 :                         signal(SIGTERM, SIG_DFL);
+     373           1 :                         signal(SIGHUP, SIG_DFL);
+     374           1 :                         signal(SIGUSR1, SIG_DFL);
+     375           1 :                         signal(SIGUSR2, SIG_DFL);
+     376             : 
+     377           1 :                         execvp(get_argv0(argv[0]), argv);
+     378             : 
+     379             :                         /* here it's an error */
+     380             :                         /* LCOV_EXCL_START */
+     381             :                         perror("Failed to exec SnapRAID");
+     382             :                         exit(EXIT_FAILURE);
+     383             :                         /* LCOV_EXCL_STOP */
+     384             :                 } else {
+     385             :                         /* parent process */
+     386             :                         int status;
+     387             : 
+     388             :                         /* store child PID so signal handler can forward signals */
+     389           1 :                         child_pid = pid;
+     390             : 
+     391             :                         do {
+     392           1 :                                 ret = waitpid(pid, &status, 0);
+     393           1 :                         } while (ret == -1 && errno == EINTR); /* retry if interrupted by signal */
+     394             : 
+     395           1 :                         if (ret == -1) {
+     396             :                                 /* LCOV_EXCL_START */
+     397             :                                 perror("Failed to wait for SnapRAID");
+     398             :                                 exit(EXIT_FAILURE);
+     399             :                                 /* LCOV_EXCL_STOP */
+     400             :                         }
+     401             : 
+     402             :                         /* clear child PID */
+     403           1 :                         child_pid = 0;
+     404             : 
+     405             :                         /* restore default signal handlers */
+     406           1 :                         signal(SIGINT, SIG_DFL);
+     407           1 :                         signal(SIGQUIT, SIG_DFL);
+     408           1 :                         signal(SIGTERM, SIG_DFL);
+     409           1 :                         signal(SIGHUP, SIG_DFL);
+     410           1 :                         signal(SIGUSR1, SIG_DFL);
+     411           1 :                         signal(SIGUSR2, SIG_DFL);
+     412             : 
+     413           1 :                         if (WIFEXITED(status)) {
+     414           1 :                                 ret = WEXITSTATUS(status);
+     415           0 :                         } else if (WIFSIGNALED(status)) {
+     416             :                                 /* terminated by signal */
+     417           0 :                                 ret = 128 + WTERMSIG(status);
+     418             :                         } else {
+     419           0 :                                 ret = -1;
+     420             :                         }
+     421             : 
+     422           1 :                         if (ret != 0) {
+     423             :                                 char* spindown_argv[3];
+     424           0 :                                 spindown_argv[0] = argv[0];
+     425           0 :                                 spindown_argv[1] = "down";
+     426           0 :                                 spindown_argv[2] = 0;
+     427             : 
+     428             :                                 /* ignore sigpipe to allow spindown even if the terminal is closed */
+     429           0 :                                 signal(SIGPIPE, SIG_IGN);
+     430             : 
+     431             :                                 /* sync all disks */
+     432           0 :                                 printf("Flush...\n");
+     433           0 :                                 sync();
+     434             : 
+     435           0 :                                 snapraid_main(2, spindown_argv);
+     436             :                         }
+     437             :                 }
+     438             :         }
+     439             : 
+     440         246 :         return ret;
+     441             : }
+     442             : #endif
+
+
+
+ + + + +
Generated by: LCOV version 1.0
+
+ + + diff --git a/cov/cmdline/metro.c.func-sort-c.html b/cov/cmdline/metro.c.func-sort-c.html new file mode 100644 index 00000000..0e7ee72b --- /dev/null +++ b/cov/cmdline/metro.c.func-sort-c.html @@ -0,0 +1,76 @@ + + + + + + + LCOV - lcov.info - cmdline/metro.c - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cmdline - metro.c (source / functions)HitTotalCoverage
Test:lcov.infoLines:4545100.0 %
Date:2025-10-28 11:59:11Functions:11100.0 %
+
+ +
+ + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
MetroHash128393
+
+
+ + + +
Generated by: LCOV version 1.0
+
+ + + diff --git a/cov/cmdline/metro.c.func.html b/cov/cmdline/metro.c.func.html new file mode 100644 index 00000000..35b3aad9 --- /dev/null +++ b/cov/cmdline/metro.c.func.html @@ -0,0 +1,76 @@ + + + + + + + LCOV - lcov.info - cmdline/metro.c - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cmdline - metro.c (source / functions)HitTotalCoverage
Test:lcov.infoLines:4545100.0 %
Date:2025-10-28 11:59:11Functions:11100.0 %
+
+ +
+ + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
MetroHash128393
+
+
+ + + +
Generated by: LCOV version 1.0
+
+ + + diff --git a/cov/cmdline/metro.c.gcov.html b/cov/cmdline/metro.c.gcov.html new file mode 100644 index 00000000..2cc3136a --- /dev/null +++ b/cov/cmdline/metro.c.gcov.html @@ -0,0 +1,184 @@ + + + + + + + LCOV - lcov.info - cmdline/metro.c + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cmdline - metro.c (source / functions)HitTotalCoverage
Test:lcov.infoLines:4545100.0 %
Date:2025-10-28 11:59:11Functions:11100.0 %
+
+ + + + + + + + +

+
          Line data    Source code
+
+       1             : /*
+       2             :  * Copyright (C) 2019 Andrea Mazzoleni
+       3             :  *
+       4             :  * This program is free software: you can redistribute it and/or modify
+       5             :  * it under the terms of the GNU General Public License as published by
+       6             :  * the Free Software Foundation, either version 3 of the License, or
+       7             :  * (at your option) any later version.
+       8             :  *
+       9             :  * This program is distributed in the hope that it will be useful,
+      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      12             :  * GNU General Public License for more details.
+      13             :  *
+      14             :  * You should have received a copy of the GNU General Public License
+      15             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+      16             :  */
+      17             : 
+      18             : /*
+      19             :  * Derivative work from metrohash128.cpp
+      20             :  *
+      21             :  * https://github.com/jandrewrogers/MetroHash/blob/master/src/metrohash128.cpp
+      22             :  *
+      23             :  * Copyright 2015-2018 J. Andrew Rogers
+      24             :  *
+      25             :  * Licensed under the Apache License, Version 2.0 (the "License");
+      26             :  * you may not use this file except in compliance with the License.
+      27             :  * You may obtain a copy of the License at
+      28             :  *
+      29             :  *     http://www.apache.org/licenses/LICENSE-2.0
+      30             :  *
+      31             :  * Unless required by applicable law or agreed to in writing, software
+      32             :  * distributed under the License is distributed on an "AS IS" BASIS,
+      33             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      34             :  * See the License for the specific language governing permissions and
+      35             :  * limitations under the License.
+      36             :  */
+      37             : 
+      38             : static const uint64_t k0 = 0xC83A91E1;
+      39             : static const uint64_t k1 = 0x8648DBDB;
+      40             : static const uint64_t k2 = 0x7BDEC03B;
+      41             : static const uint64_t k3 = 0x2F5870A5;
+      42             : 
+      43         393 : void MetroHash128(const void* data, size_t size, const uint8_t* seed, uint8_t* digest)
+      44             : {
+      45         393 :         const uint8_t* ptr = data;
+      46             :         uint64_t v[4];
+      47             : 
+      48         393 :         v[0] = (util_read64(seed) - k0) * k3;
+      49         393 :         v[1] = (util_read64(seed + 8) + k1) * k2;
+      50             : 
+      51         393 :         if (size >= 32) {
+      52         357 :                 v[2] = (util_read64(seed) + k0) * k2;
+      53         357 :                 v[3] = (util_read64(seed + 8) - k1) * k3;
+      54             : 
+      55             :                 do {
+      56     1049484 :                         v[0] += util_read64(ptr) * k0; ptr += 8; v[0] = util_rotr64(v[0], 29) + v[2];
+      57     1049484 :                         v[1] += util_read64(ptr) * k1; ptr += 8; v[1] = util_rotr64(v[1], 29) + v[3];
+      58     1049484 :                         v[2] += util_read64(ptr) * k2; ptr += 8; v[2] = util_rotr64(v[2], 29) + v[0];
+      59     1049484 :                         v[3] += util_read64(ptr) * k3; ptr += 8; v[3] = util_rotr64(v[3], 29) + v[1];
+      60     1049484 :                         size -= 32;
+      61     1049484 :                 } while (size >= 32);
+      62             : 
+      63         357 :                 v[2] ^= util_rotr64(((v[0] + v[3]) * k0) + v[1], 21) * k1;
+      64         357 :                 v[3] ^= util_rotr64(((v[1] + v[2]) * k1) + v[0], 21) * k0;
+      65         357 :                 v[0] ^= util_rotr64(((v[0] + v[2]) * k0) + v[3], 21) * k1;
+      66         357 :                 v[1] ^= util_rotr64(((v[1] + v[3]) * k1) + v[2], 21) * k0;
+      67             :         }
+      68             : 
+      69         393 :         if (size >= 16) {
+      70         132 :                 v[0] += util_read64(ptr) * k2; ptr += 8; v[0] = util_rotr64(v[0], 33) * k3;
+      71         132 :                 v[1] += util_read64(ptr) * k2; ptr += 8; v[1] = util_rotr64(v[1], 33) * k3;
+      72         132 :                 v[0] ^= util_rotr64((v[0] * k2) + v[1], 45) * k1;
+      73         132 :                 v[1] ^= util_rotr64((v[1] * k3) + v[0], 45) * k0;
+      74         132 :                 size -= 16;
+      75             :         }
+      76             : 
+      77         393 :         if (size >= 8) {
+      78         134 :                 v[0] += util_read64(ptr) * k2; ptr += 8; v[0] = util_rotr64(v[0], 33) * k3;
+      79         134 :                 v[0] ^= util_rotr64((v[0] * k2) + v[1], 27) * k1;
+      80         134 :                 size -= 8;
+      81             :         }
+      82             : 
+      83         393 :         if (size >= 4) {
+      84         132 :                 v[1] += util_read32(ptr) * k2; ptr += 4; v[1] = util_rotr64(v[1], 33) * k3;
+      85         132 :                 v[1] ^= util_rotr64((v[1] * k3) + v[0], 46) * k0;
+      86         132 :                 size -= 4;
+      87             :         }
+      88             : 
+      89         393 :         if (size >= 2) {
+      90         135 :                 v[0] += util_read16(ptr) * k2; ptr += 2; v[0] = util_rotr64(v[0], 33) * k3;
+      91         135 :                 v[0] ^= util_rotr64((v[0] * k2) + v[1], 22) * k1;
+      92         135 :                 size -= 2;
+      93             :         }
+      94             : 
+      95         393 :         if (size >= 1) {
+      96         133 :                 v[1] += util_read8(ptr) * k2; v[1] = util_rotr64(v[1], 33) * k3;
+      97         133 :                 v[1] ^= util_rotr64((v[1] * k3) + v[0], 58) * k0;
+      98             :         }
+      99             : 
+     100         393 :         v[0] += util_rotr64((v[0] * k0) + v[1], 13);
+     101         393 :         v[1] += util_rotr64((v[1] * k1) + v[0], 37);
+     102         393 :         v[0] += util_rotr64((v[0] * k2) + v[1], 13);
+     103         393 :         v[1] += util_rotr64((v[1] * k3) + v[0], 37);
+     104             : 
+     105         393 :         util_write64(digest, v[0]);
+     106         393 :         util_write64(digest + 8, v[1]);
+     107         393 : }
+     108             : 
+
+
+
+ + + + +
Generated by: LCOV version 1.0
+
+ + + diff --git a/cov/cmdline/thermal.c.func-sort-c.html b/cov/cmdline/thermal.c.func-sort-c.html new file mode 100644 index 00000000..41f805e8 --- /dev/null +++ b/cov/cmdline/thermal.c.func-sort-c.html @@ -0,0 +1,104 @@ + + + + + + + LCOV - lcov.info - cmdline/thermal.c - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cmdline - thermal.c (source / functions)HitTotalCoverage
Test:lcov.infoLines:15919382.4 %
Date:2025-10-28 11:59:11Functions:7887.5 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
state_thermal_cooldown0
fit_thermal_model12
thermal_alloc12
thermal_free12
state_thermal_begin222
state_thermal2609
smart_temp72510
state_thermal_alarm1060633
+
+
+ + + +
Generated by: LCOV version 1.0
+
+ + + diff --git a/cov/cmdline/thermal.c.func.html b/cov/cmdline/thermal.c.func.html new file mode 100644 index 00000000..b9225e58 --- /dev/null +++ b/cov/cmdline/thermal.c.func.html @@ -0,0 +1,104 @@ + + + + + + + LCOV - lcov.info - cmdline/thermal.c - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cmdline - thermal.c (source / functions)HitTotalCoverage
Test:lcov.infoLines:15919382.4 %
Date:2025-10-28 11:59:11Functions:7887.5 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Function Name Sort by function nameHit count Sort by hit count
fit_thermal_model12
smart_temp72510
state_thermal2609
state_thermal_alarm1060633
state_thermal_begin222
state_thermal_cooldown0
thermal_alloc12
thermal_free12
+
+
+ + + +
Generated by: LCOV version 1.0
+
+ + + diff --git a/cov/cmdline/thermal.c.gcov.html b/cov/cmdline/thermal.c.gcov.html new file mode 100644 index 00000000..27b21c08 --- /dev/null +++ b/cov/cmdline/thermal.c.gcov.html @@ -0,0 +1,472 @@ + + + + + + + LCOV - lcov.info - cmdline/thermal.c + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cmdline - thermal.c (source / functions)HitTotalCoverage
Test:lcov.infoLines:15919382.4 %
Date:2025-10-28 11:59:11Functions:7887.5 %
+
+ + + + + + + + +

+
          Line data    Source code
+
+       1             : /*
+       2             :  * Copyright (C) 2025 Andrea Mazzoleni
+       3             :  *
+       4             :  * This program is free software: you can redistribute it and/or modify
+       5             :  * it under the terms of the GNU General Public License as published by
+       6             :  * the Free Software Foundation, either version 3 of the License, or
+       7             :  * (at your option) any later version.
+       8             :  *
+       9             :  * This program is distributed in the hope that it will be useful,
+      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      12             :  * GNU General Public License for more details.
+      13             :  *
+      14             :  * You should have received a copy of the GNU General Public License
+      15             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+      16             :  */
+      17             : 
+      18             : #include "portable.h"
+      19             : 
+      20             : #include "thermal.h"
+      21             : #include "state.h"
+      22             : #include "io.h"
+      23             : 
+      24             : #include <math.h>
+      25             : 
+      26          12 : struct snapraid_thermal* thermal_alloc(uint64_t dev, const char* name)
+      27             : {
+      28          12 :         struct snapraid_thermal* thermal = malloc_nofail(sizeof(struct snapraid_thermal));
+      29             : 
+      30          12 :         thermal->device = dev;
+      31          12 :         thermal->latest_temperature = 0;
+      32          12 :         thermal->count = 0;
+      33          12 :         pathcpy(thermal->name, sizeof(thermal->name), name);
+      34             : 
+      35          12 :         return thermal;
+      36             : }
+      37             : 
+      38          12 : void thermal_free(struct snapraid_thermal* thermal)
+      39             : {
+      40          12 :         free(thermal);
+      41          12 : }
+      42             : 
+      43             : /*
+      44             :  * Fit exponential heating model to data using least squares
+      45             :  */
+      46          12 : struct snapraid_thermal_params fit_thermal_model(const struct snapraid_thermal_point* points, int n_points, double t_ambient)
+      47             : {
+      48             :         struct snapraid_thermal_params model;
+      49             :         double t_steady_try;
+      50             :         double k_try;
+      51             : 
+      52          12 :         memset(&model, 0, sizeof(model));
+      53             : 
+      54          12 :         model.t_ambient = t_ambient;
+      55             : 
+      56             :         /* at least four points to have a result */
+      57          12 :         if (n_points < 4)
+      58           0 :                 return model;
+      59             : 
+      60          12 :         double last_temp = points[n_points - 1].temperature;
+      61             : 
+      62             :         /* iterative refinement to find best k_heat and t_steady */
+      63          12 :         double best_error = 1e10;
+      64          12 :         double best_k = 0;
+      65          12 :         double best_t_steady = 0;
+      66             : 
+      67             :         /* grid search for parameters */
+      68         576 :         for (t_steady_try = last_temp + 2.0; t_steady_try <= last_temp + 25.0; t_steady_try += 0.5) {
+      69       15228 :                 for (k_try = 0.00001; k_try <= 0.001; k_try *= 1.2) {
+      70       14664 :                         double error = 0.0;
+      71             :                         int i;
+      72             : 
+      73             :                         /* calculate error for this parameter set */
+      74       87984 :                         for (i = 0; i < n_points; i++) {
+      75       73320 :                                 double t = points[i].time;
+      76       73320 :                                 double t_predicted = t_steady_try - (t_steady_try - points[0].temperature) * exp(-k_try * t);
+      77       73320 :                                 double diff = points[i].temperature - t_predicted;
+      78       73320 :                                 error += diff * diff;
+      79             :                         }
+      80             : 
+      81       14664 :                         if (error < best_error) {
+      82         312 :                                 best_error = error;
+      83         312 :                                 best_k = k_try;
+      84         312 :                                 best_t_steady = t_steady_try;
+      85             :                         }
+      86             :                 }
+      87             :         }
+      88             : 
+      89          12 :         model.k_heat = best_k;
+      90          12 :         model.t_steady = best_t_steady;
+      91             : 
+      92             :         /* calculate quality metrics */
+      93          12 :         double sum_squared_residuals = 0.0;
+      94          12 :         double sum_total = 0.0;
+      95          12 :         double mean_temp = 0.0;
+      96          12 :         model.max_error = 0.0;
+      97             :         int i;
+      98             : 
+      99             :         /* calculate mean temperature */
+     100          72 :         for (i = 0; i < n_points; i++)
+     101          60 :                 mean_temp += points[i].temperature;
+     102          12 :         mean_temp /= n_points;
+     103             : 
+     104             :         /* calculate R-squared and errors */
+     105          72 :         for (i = 0; i < n_points; i++) {
+     106          60 :                 double t = points[i].time;
+     107          60 :                 double t_predicted = model.t_steady - (model.t_steady - points[0].temperature) * exp(-model.k_heat * t);
+     108          60 :                 double residual = points[i].temperature - t_predicted;
+     109             : 
+     110          60 :                 sum_squared_residuals += residual * residual;
+     111          60 :                 sum_total += (points[i].temperature - mean_temp) * (points[i].temperature - mean_temp);
+     112             : 
+     113          60 :                 double abs_error = fabs(residual);
+     114          60 :                 if (abs_error > model.max_error) {
+     115          36 :                         model.max_error = abs_error;
+     116             :                 }
+     117             :         }
+     118             : 
+     119          12 :         model.rmse = sqrt(sum_squared_residuals / n_points);
+     120          12 :         model.r_squared = 1.0 - (sum_squared_residuals / sum_total);
+     121             : 
+     122          12 :         return model;
+     123             : }
+     124             : 
+     125       72510 : static int smart_temp(devinfo_t* devinfo)
+     126             : {
+     127       72510 :         uint64_t t = devinfo->smart[SMART_TEMPERATURE_CELSIUS];
+     128             : 
+     129             :         /* validate temperature */
+     130       72510 :         if (t == SMART_UNASSIGNED)
+     131           0 :                 return -1;
+     132       72510 :         if (t == 0)
+     133           0 :                 return -1;
+     134       72510 :         if (t > 100)
+     135           0 :                 return -1;
+     136             : 
+     137       72510 :         return t;
+     138             : }
+     139             : 
+     140        2609 : void state_thermal(struct snapraid_state* state, time_t now)
+     141             : {
+     142             :         tommy_node* i;
+     143             :         unsigned j;
+     144             :         tommy_list high;
+     145             :         tommy_list low;
+     146             :         int ret;
+     147             : 
+     148        2609 :         if (state->thermal_temperature_limit == 0)
+     149         193 :                 return;
+     150             : 
+     151        2416 :         tommy_list_init(&high);
+     152        2416 :         tommy_list_init(&low);
+     153             : 
+     154             :         /* for all disks */
+     155       16912 :         for (i = state->disklist; i != 0; i = i->next) {
+     156       14496 :                 struct snapraid_disk* disk = i->data;
+     157             :                 devinfo_t* entry;
+     158             : 
+     159       14496 :                 entry = calloc_nofail(1, sizeof(devinfo_t));
+     160             : 
+     161       14496 :                 entry->device = disk->device;
+     162       14496 :                 pathcpy(entry->name, sizeof(entry->name), disk->name);
+     163       14496 :                 pathcpy(entry->mount, sizeof(entry->mount), disk->dir);
+     164       14496 :                 pathcpy(entry->smartctl, sizeof(entry->smartctl), disk->smartctl);
+     165       14496 :                 memcpy(entry->smartignore, disk->smartignore, sizeof(entry->smartignore));
+     166             : 
+     167       14496 :                 tommy_list_insert_tail(&high, &entry->node, entry);
+     168             :         }
+     169             : 
+     170             :         /* for all parities */
+     171       16912 :         for (j = 0; j < state->level; ++j) {
+     172             :                 devinfo_t* entry;
+     173             :                 unsigned s;
+     174             : 
+     175       72480 :                 for (s = 0; s < state->parity[j].split_mac; ++s) {
+     176       57984 :                         entry = calloc_nofail(1, sizeof(devinfo_t));
+     177             : 
+     178       57984 :                         entry->device = state->parity[j].split_map[s].device;
+     179       57984 :                         pathcpy(entry->name, sizeof(entry->name), lev_config_name(j));
+     180       57984 :                         pathcpy(entry->mount, sizeof(entry->mount), state->parity[j].split_map[s].path);
+     181       57984 :                         pathcpy(entry->smartctl, sizeof(entry->smartctl), state->parity[j].smartctl);
+     182       57984 :                         memcpy(entry->smartignore, state->parity[j].smartignore, sizeof(entry->smartignore));
+     183       57984 :                         pathcut(entry->mount); /* remove the parity file */
+     184             : 
+     185       57984 :                         tommy_list_insert_tail(&high, &entry->node, entry);
+     186             :                 }
+     187             :         }
+     188             : 
+     189        2416 :         if (state->opt.fake_device) {
+     190        2416 :                 ret = devtest(&high, &low, DEVICE_SMART);
+     191             :         } else {
+     192           0 :                 ret = devquery(&high, &low, DEVICE_SMART, 0 /* only disks in the array */);
+     193             :         }
+     194             : 
+     195             :         /* on error, just disable thermal gathering */
+     196        2416 :         if (ret != 0)
+     197           0 :                 return;
+     198             : 
+     199             :         /* if the list is empty, it's not supported in this platform */
+     200        2416 :         if (tommy_list_empty(&low))
+     201           0 :                 return;
+     202             : 
+     203             :         /* if ambient temperature is not set, set it now with the lowest HD temperature */
+     204        2416 :         if (state->thermal_ambient_temperature == 0) {
+     205           1 :                 state->thermal_ambient_temperature = ambient_temperature();
+     206             : 
+     207          31 :                 for (i = tommy_list_head(&low); i != 0; i = i->next) {
+     208          30 :                         devinfo_t* devinfo = i->data;
+     209             : 
+     210          30 :                         int temp = smart_temp(devinfo);
+     211          30 :                         if (temp < 0)
+     212           0 :                                 continue;
+     213             : 
+     214          30 :                         log_tag("thermal:system:candidate:%d\n", temp);
+     215             : 
+     216          30 :                         if (state->thermal_ambient_temperature == 0 || state->thermal_ambient_temperature > temp)
+     217           1 :                                 state->thermal_ambient_temperature = temp;
+     218             :                 }
+     219             : 
+     220           1 :                 log_tag("thermal:system:final:%d\n", state->thermal_ambient_temperature);
+     221             :         }
+     222             : 
+     223        2416 :         int highest_temperature = 0;
+     224       74896 :         for (i = tommy_list_head(&low); i != 0; i = i->next) {
+     225             :                 tommy_node* t;
+     226             :                 struct snapraid_thermal* found;
+     227       72480 :                 devinfo_t* devinfo = i->data;
+     228             :                 unsigned k;
+     229             : 
+     230       72480 :                 int temperature = smart_temp(devinfo);
+     231       72480 :                 if (temperature < 0)
+     232           0 :                         continue;
+     233             : 
+     234             :                 /* search of the entry */
+     235       72480 :                 found = 0;
+     236      601584 :                 for (t = tommy_list_head(&state->thermallist); t != 0; t = t->next) {
+     237      601572 :                         struct snapraid_thermal* thermal = t->data;
+     238      601572 :                         if (thermal->device == devinfo->device) {
+     239       72468 :                                 found = thermal;
+     240       72468 :                                 break;
+     241             :                         }
+     242             :                 }
+     243             : 
+     244             :                 /* if not found, create it */
+     245       72480 :                 if (found == 0) {
+     246          12 :                         found = thermal_alloc(devinfo->device, devinfo->name);
+     247          12 :                         tommy_list_insert_tail(&state->thermallist, &found->node, found);
+     248             :                 }
+     249             : 
+     250       72480 :                 found->latest_temperature = temperature;
+     251             : 
+     252       72480 :                 if (highest_temperature < temperature)
+     253        2416 :                         highest_temperature = temperature;
+     254             : 
+     255       72480 :                 log_tag("thermal:current:%s:%" PRIu64 ":%d\n", devinfo->name, devinfo->device, temperature);
+     256             : 
+     257       72480 :                 if (state->thermal_stop_gathering)
+     258           0 :                         continue;
+     259             : 
+     260       72480 :                 if (found->count + 1 >= THERMAL_MAX) /* keep one extra space at the end */
+     261           0 :                         continue;
+     262             : 
+     263             :                 /* only monotone increasing temperature */
+     264       72480 :                 if (found->count > 0 && found->data[found->count - 1].temperature >= temperature)
+     265       72468 :                         continue;
+     266             : 
+     267             :                 /* insert the new data point */
+     268          12 :                 found->data[found->count].temperature = temperature;
+     269          12 :                 found->data[found->count].time = now - state->thermal_first;
+     270          12 :                 ++found->count;
+     271             : 
+     272          12 :                 if (state->opt.fake_device) {
+     273             :                         /* fill with fake data */
+     274          12 :                         found->data[0].time = 0;
+     275          12 :                         found->data[0].temperature = 27;
+     276          12 :                         found->data[1].time = 100;
+     277          12 :                         found->data[1].temperature = 28;
+     278          12 :                         found->data[2].time = 300;
+     279          12 :                         found->data[2].temperature = 29;
+     280          12 :                         found->data[3].time = 700;
+     281          12 :                         found->data[3].temperature = 30;
+     282          12 :                         found->data[4].time = 1500;
+     283          12 :                         found->data[4].temperature = 31;
+     284          12 :                         found->count = 5;
+     285             :                 }
+     286             : 
+     287             :                 /* log the new data */
+     288          12 :                 log_tag("thermal:heat:%s:%" PRIu64 ":%u:", devinfo->name, devinfo->device, found->count);
+     289          72 :                 for (k = 0; k < found->count; ++k)
+     290          60 :                         log_tag("%s%d/%d", k > 0 ? "," : "", (int)found->data[k].temperature, (int)found->data[k].time);
+     291          12 :                 log_tag("\n");
+     292             : 
+     293             :                 /* estimate parameters */
+     294          12 :                 found->params = fit_thermal_model(found->data, found->count, state->thermal_ambient_temperature);
+     295             : 
+     296          12 :                 log_tag("thermal:params:%s:%" PRIu64 ":%g:%g:%g:%g:%g:%g\n", devinfo->name, devinfo->device,
+     297             :                         found->params.k_heat, found->params.t_ambient, found->params.t_steady,
+     298             :                         found->params.rmse, found->params.r_squared, found->params.max_error);
+     299             :         }
+     300             : 
+     301             :         /* always update the highest temperature */
+     302        2416 :         state->thermal_highest_temperature = highest_temperature;
+     303             : 
+     304        2416 :         log_tag("thermal:highest:%d\n", highest_temperature);
+     305        2416 :         log_flush();
+     306             : 
+     307        2416 :         tommy_list_foreach(&high, free);
+     308        2416 :         tommy_list_foreach(&low, free);
+     309             : }
+     310             : 
+     311     1060633 : int state_thermal_alarm(struct snapraid_state* state)
+     312             : {
+     313             :         /* if no limit, there is no thermal support */
+     314     1060633 :         if (state->thermal_temperature_limit == 0)
+     315     1058217 :                 return 0;
+     316             : 
+     317        2416 :         if (state->thermal_highest_temperature <= state->thermal_temperature_limit)
+     318        2416 :                 return 0;
+     319             : 
+     320           0 :         return 1;
+     321             : }
+     322             : 
+     323           0 : void state_thermal_cooldown(struct snapraid_state* state)
+     324             : {
+     325           0 :         int sleep_time = state->thermal_cooldown_time;
+     326             : 
+     327           0 :         if (sleep_time == 0)
+     328           0 :                 sleep_time = 5 * 60; /* default sleep time */
+     329           0 :         if (sleep_time < 5 * 60)
+     330           0 :                 sleep_time = 5 * 60; /* minimum sleep time */
+     331             : 
+     332             :         /* from now on, stop any further data gathering as the heating is interrupted */
+     333           0 :         state->thermal_stop_gathering = 1;
+     334             : 
+     335           0 :         log_tag("thermal:spindown\n");
+     336           0 :         state_device(state, DEVICE_DOWN, 0);
+     337             : 
+     338           0 :         msg_progress("Cooldown...\n");
+     339             : 
+     340           0 :         log_tag("thermal:cooldown:%d\n", sleep_time);
+     341           0 :         printf("Waiting for %d minutes...\n", sleep_time / 60);
+     342             : 
+     343           0 :         log_flush();
+     344             : 
+     345             :         /* every 30 seconds spin down any disk that was spunup */
+     346           0 :         while (sleep_time > 0) {
+     347           0 :                 state_device(state, DEVICE_DOWNIFUP, 0);
+     348             : 
+     349           0 :                 sleep(30);
+     350           0 :                 sleep_time -= 30;
+     351             :         }
+     352             : 
+     353           0 :         if (!global_interrupt) { /* don't wake-up if we are interrupting */
+     354           0 :                 log_tag("thermal:spinup\n");
+     355             : 
+     356             :                 /* spinup */
+     357           0 :                 state_device(state, DEVICE_UP, 0);
+     358             : 
+     359             :                 /* log new thermal info */
+     360           0 :                 state_thermal(state, 0);
+     361             :         }
+     362           0 : }
+     363             : 
+     364         222 : int state_thermal_begin(struct snapraid_state* state, time_t now)
+     365             : {
+     366         222 :         if (state->thermal_temperature_limit == 0)
+     367         221 :                 return 1;
+     368             : 
+     369             :         /* initial thermal measure */
+     370           1 :         state->thermal_first = now;
+     371           1 :         state->thermal_latest = now;
+     372           1 :         state_thermal(state, now);
+     373             : 
+     374           1 :         if (state->thermal_ambient_temperature != 0) {
+     375           1 :                 printf("System temperature is %u degrees\n", state->thermal_ambient_temperature);
+     376             : 
+     377           1 :                 if (state->thermal_temperature_limit != 0 && state->thermal_temperature_limit <= state->thermal_ambient_temperature) {
+     378             :                         /* LCOV_EXCL_START */
+     379             :                         log_fatal("DANGER! System temperature of %d degrees is higher than the temperature limit of %d degrees. Unable to proceeed!\n", state->thermal_ambient_temperature, state->thermal_temperature_limit);
+     380             :                         log_flush();
+     381             :                         return 0;
+     382             :                         /* LCOV_EXCL_STOP */
+     383             :                 }
+     384             :         }
+     385             : 
+     386           1 :         if (state_thermal_alarm(state)) {
+     387             :                 /* LCOV_EXCL_START */
+     388             :                 log_fatal("DANGER! Hard disk temperature of %d degrees is already outside the operating range. Unable to proceeed!\n", state->thermal_highest_temperature);
+     389             :                 log_flush();
+     390             :                 return 0;
+     391             :                 /* LCOV_EXCL_STOP */
+     392             :         }
+     393             : 
+     394           1 :         return 1;
+     395             : }
+     396             : 
+
+
+
+ + + + +
Generated by: LCOV version 1.0
+
+ + +