LCOV - code coverage report
Current view: top level - cmdline - unix.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 313 556 56.3 %
Date: 2025-10-28 11:59:11 Functions: 26 37 70.3 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2013 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             : #ifndef __MINGW32__ /* Only for Unix */
      21             : 
      22             : #include "support.h"
      23             : 
      24             : /**
      25             :  * Exit codes.
      26             :  */
      27             : int exit_success = 0;
      28             : int exit_failure = 1;
      29             : int exit_sync_needed = 2;
      30             : 
      31     1716677 : int open_noatime(const char* file, int flags)
      32             : {
      33             : #ifdef O_NOATIME
      34     1716677 :         int f = open(file, flags | O_NOATIME);
      35             : 
      36             :         /* only root is allowed to use O_NOATIME, in case retry without it */
      37     1716677 :         if (f == -1 && errno == EPERM)
      38           0 :                 f = open(file, flags);
      39     1716677 :         return f;
      40             : #else
      41             :         return open(file, flags);
      42             : #endif
      43             : }
      44             : 
      45           0 : int dirent_hidden(struct dirent* dd)
      46             : {
      47           0 :         return dd->d_name[0] == '.';
      48             : }
      49             : 
      50           0 : const char* stat_desc(struct stat* st)
      51             : {
      52           0 :         if (S_ISREG(st->st_mode))
      53           0 :                 return "regular";
      54           0 :         if (S_ISDIR(st->st_mode))
      55           0 :                 return "directory";
      56           0 :         if (S_ISCHR(st->st_mode))
      57           0 :                 return "character";
      58           0 :         if (S_ISBLK(st->st_mode))
      59           0 :                 return "block-device";
      60           0 :         if (S_ISFIFO(st->st_mode))
      61           0 :                 return "fifo";
      62           0 :         if (S_ISLNK(st->st_mode))
      63           0 :                 return "link";
      64           0 :         if (S_ISLNK(st->st_mode))
      65           0 :                 return "symbolic-link";
      66           0 :         if (S_ISSOCK(st->st_mode))
      67           0 :                 return "socket";
      68           0 :         return "unknown";
      69             : }
      70             : 
      71             : /**
      72             :  * Read a file from sys
      73             :  *
      74             :  * Return -1 on error, otherwise the data read
      75             :  */
      76             : #if HAVE_LINUX_DEVICE
      77         177 : static int sysread(const char* path, char* buf, size_t buf_size)
      78             : {
      79             :         int f;
      80             :         int ret;
      81             :         int len;
      82             : 
      83         177 :         f = open(path, O_RDONLY);
      84         177 :         if (f == -1) {
      85             :                 /* LCOV_EXCL_START */
      86             :                 log_fatal("Failed to open '%s'.\n", path);
      87             :                 return -1;
      88             :                 /* LCOV_EXCL_STOP */
      89             :         }
      90             : 
      91         177 :         len = read(f, buf, buf_size);
      92         177 :         if (len < 0) {
      93             :                 /* LCOV_EXCL_START */
      94             :                 close(f);
      95             :                 log_fatal("Failed to read '%s'.\n", path);
      96             :                 return -1;
      97             :                 /* LCOV_EXCL_STOP */
      98             :         }
      99             : 
     100         177 :         ret = close(f);
     101         177 :         if (ret != 0) {
     102             :                 /* LCOV_EXCL_START */
     103             :                 log_fatal("Failed to close '%s'.\n", path);
     104             :                 return -1;
     105             :                 /* LCOV_EXCL_STOP */
     106             :         }
     107             : 
     108         177 :         if (len == (int)buf_size)
     109           0 :                 buf[buf_size - 1] = 0;
     110             :         else
     111         177 :                 buf[len] = 0;
     112             : 
     113         177 :         return len;
     114             : }
     115             : #endif
     116             : 
     117             : /**
     118             :  * Get the device file from the device number.
     119             :  *
     120             :  * It uses /proc/self/mountinfo.
     121             :  *
     122             :  * Null devices (major==0) are resolved to the device indicated in mountinfo.
     123             :  */
     124             : #if HAVE_LINUX_DEVICE
     125           4 : static int devresolve_proc(uint64_t device, char* path, size_t path_size)
     126             : {
     127             :         FILE* f;
     128             :         char match[32];
     129             : 
     130             :         /* generate the matching string */
     131           4 :         snprintf(match, sizeof(match), "%u:%u", major(device), minor(device));
     132             : 
     133           4 :         f = fopen("/proc/self/mountinfo", "r");
     134           4 :         if (!f) {
     135           0 :                 log_tag("resolve:proc:%u:%u: failed to open /proc/self/mountinfo\n", major(device), minor(device));
     136           0 :                 return -1;
     137             :         }
     138             : 
     139             :         /*
     140             :          * mountinfo format
     141             :          * 0 - mount ID
     142             :          * 1 - parent ID
     143             :          * 2 - major:minor
     144             :          * 3 - root
     145             :          * 4 - mount point
     146             :          * 5 - options
     147             :          * 6 - "-" (separator)
     148             :          * 7 - fs
     149             :          * 8 - mount source - /dev/device
     150             :          */
     151             : 
     152         184 :         while (1) {
     153             :                 char buf[256];
     154             :                 char* first_map[8];
     155             :                 unsigned first_mac;
     156             :                 char* second_map[8];
     157             :                 unsigned second_mac;
     158             :                 char* s;
     159             :                 struct stat st;
     160             :                 char* separator;
     161             :                 char* majorminor;
     162             :                 char* mountpoint;
     163             :                 char* fs;
     164             :                 char* mountsource;
     165             : 
     166         188 :                 s = fgets(buf, sizeof(buf), f);
     167         188 :                 if (s == 0)
     168           4 :                         break;
     169             : 
     170             :                 /* find the separator position */
     171         184 :                 separator = strstr(s, " - ");
     172         184 :                 if (!separator)
     173           0 :                         continue;
     174             : 
     175             :                 /* skip the separator */
     176         184 :                 *separator = 0;
     177         184 :                 separator += 3;
     178             : 
     179             :                 /* split the line */
     180         184 :                 first_mac = strsplit(first_map, 8, s, " \t\r\n");
     181         184 :                 second_mac = strsplit(second_map, 8, separator, " \t\r\n");
     182             : 
     183             :                 /* if too short, it's the wrong line */
     184         184 :                 if (first_mac < 5)
     185           0 :                         continue;
     186         184 :                 if (second_mac < 2)
     187           0 :                         continue;
     188             : 
     189         184 :                 majorminor = first_map[2];
     190         184 :                 mountpoint = first_map[4];
     191         184 :                 fs = second_map[0];
     192         184 :                 mountsource = second_map[1];
     193             : 
     194             :                 /* compare major:minor from mountinfo */
     195         184 :                 if (strcmp(majorminor, match) == 0) {
     196             :                         /*
     197             :                          * Accept only /dev/... mountsource
     198             :                          *
     199             :                          * This excludes ZFS that uses a bare label for mountsource, like "tank".
     200             :                          *
     201             :                          * 410 408 0:193 / /XXX rw,relatime shared:217 - zfs tank/system/data/var/lib/docker/XXX rw,xattr,noacl
     202             :                          *
     203             :                          * Also excludes AUTOFS unmounted devices that point to a fake filesystem
     204             :                          * used to remount them at the first use.
     205             :                          *
     206             :                          * 97 25 0:42 / /XXX rw,relatime shared:76 - autofs /etc/auto.seed rw,fd=6,pgrp=952,timeout=30,minproto=5,maxproto=5,indirect
     207             :                          */
     208           0 :                         if (strncmp(mountsource, "/dev/", 5) != 0) {
     209           0 :                                 log_tag("resolve:proc:%u:%u: match skipped for not /dev/ mountsource for %s %s\n", major(device), minor(device), fs, mountsource);
     210           0 :                                 continue;
     211             :                         }
     212             : 
     213           0 :                         pathcpy(path, path_size, mountsource);
     214             : 
     215           0 :                         log_tag("resolve:proc:%u:%u: found device %s matching device %s\n", major(device), minor(device), path, match);
     216             : 
     217           0 :                         fclose(f);
     218           0 :                         return 0;
     219             :                 }
     220             : 
     221             :                 /* get the device of the mount point */
     222             :                 /* in Btrfs it could be different than the one in mountinfo */
     223         184 :                 if (stat(mountpoint, &st) == 0 && st.st_dev == device) {
     224           0 :                         if (strncmp(mountsource, "/dev/", 5) != 0) {
     225           0 :                                 log_tag("resolve:proc:%u:%u: match skipped for not /dev/ mountsource for %s %s\n", major(device), minor(device), fs, mountsource);
     226           0 :                                 continue;
     227             :                         }
     228             : 
     229           0 :                         pathcpy(path, path_size, mountsource);
     230             : 
     231           0 :                         log_tag("resolve:proc:%u:%u: found device %s matching mountpoint %s\n", major(device), minor(device), path, mountpoint);
     232             : 
     233           0 :                         fclose(f);
     234           0 :                         return 0;
     235             :                 }
     236             :         }
     237             : 
     238           4 :         log_tag("resolve:proc:%u:%u: not found\n", major(device), minor(device));
     239             : 
     240           4 :         fclose(f);
     241           4 :         return -1;
     242             : }
     243             : #endif
     244             : 
     245             : /**
     246             :  * Get the device of a virtual superblock.
     247             :  *
     248             :  * This is intended to resolve the case of Btrfs filesystems that
     249             :  * create a virtual superblock (major==0) not backed by any low
     250             :  * level device.
     251             :  *
     252             :  * See:
     253             :  * Bug 711881 - too funny btrfs st_dev numbers
     254             :  * https://bugzilla.redhat.com/show_bug.cgi?id=711881
     255             :  */
     256             : #if HAVE_LINUX_DEVICE
     257           4 : static int devdereference(uint64_t device, uint64_t* new_device)
     258             : {
     259             :         char path[PATH_MAX];
     260             :         struct stat st;
     261             : 
     262             :         /* use the proc interface to get the device containing the filesystem */
     263           4 :         if (devresolve_proc(device, path, sizeof(path)) != 0) {
     264             :                 /* LCOV_EXCL_START */
     265             :                 return -1;
     266             :                 /* LCOV_EXCL_STOP */
     267             :         }
     268             : 
     269             :         /* check the device */
     270           0 :         if (stat(path, &st) != 0) {
     271             :                 /* LCOV_EXCL_START */
     272             :                 log_tag("dereference:%u:%u: failed to stat %s\n", major(device), minor(device), path);
     273             :                 return -1;
     274             :                 /* LCOV_EXCL_STOP */
     275             :         }
     276             : 
     277           0 :         if (major(st.st_rdev) == 0) {
     278             :                 /* LCOV_EXCL_START */
     279             :                 log_tag("dereference:%u:%u: still null device %s -> %u:%u\n", major(device), minor(device), path, major(st.st_rdev), minor(st.st_rdev));
     280             :                 return -1;
     281             :                 /* LCOV_EXCL_STOP */
     282             :         }
     283             : 
     284           0 :         *new_device = st.st_rdev;
     285           0 :         log_tag("dereference:%u:%u: found %u:%u\n", major(device), minor(device), major(st.st_rdev), minor(st.st_rdev));
     286           0 :         return 0;
     287             : }
     288             : #endif
     289             : 
     290             : /**
     291             :  * Read a file extracting the specified tag TAG=VALUE format.
     292             :  * Return !=0 on error.
     293             :  */
     294             : #if HAVE_LINUX_DEVICE
     295         165 : static int tagread(const char* path, const char* tag, char* value, size_t value_size)
     296             : {
     297             :         int ret;
     298             :         char buf[512];
     299             :         size_t tag_len;
     300             :         char* i;
     301             :         char* e;
     302             : 
     303         165 :         ret = sysread(path, buf, sizeof(buf));
     304         165 :         if (ret < 0)
     305           0 :                 return -1;
     306         165 :         if (ret == sizeof(buf)) {
     307             :                 /* LCOV_EXCL_START */
     308             :                 log_fatal("Too long read '%s'.\n", path);
     309             :                 return -1;
     310             :                 /* LCOV_EXCL_STOP */
     311             :         }
     312             : 
     313         165 :         tag_len = strlen(tag);
     314             : 
     315        2870 :         for (i = buf; *i; ++i) {
     316        2870 :                 char* p = i;
     317             : 
     318             :                 /* start with a space */
     319        2870 :                 if (p != buf) {
     320        2705 :                         if (!isspace(*p))
     321        2375 :                                 continue;
     322         330 :                         ++p;
     323             :                 }
     324             : 
     325         495 :                 if (strncmp(p, tag, tag_len) != 0)
     326         330 :                         continue;
     327         165 :                 p += tag_len;
     328             : 
     329             :                 /* end with a = */
     330         165 :                 if (*p != '=')
     331           0 :                         continue;
     332         165 :                 ++p;
     333             : 
     334             :                 /* found */
     335         165 :                 i = p;
     336         165 :                 break;
     337             :         }
     338         165 :         if (!*i) {
     339             :                 /* LCOV_EXCL_START */
     340             :                 log_fatal("Missing tag '%s' for '%s'.\n", tag, path);
     341             :                 return -1;
     342             :                 /* LCOV_EXCL_STOP */
     343             :         }
     344             : 
     345             :         /* terminate at the first space */
     346         165 :         e = i;
     347         725 :         while (*e != 0 && !isspace(*e))
     348         560 :                 ++e;
     349         165 :         *e = 0;
     350             : 
     351         165 :         if (!*i) {
     352             :                 /* LCOV_EXCL_START */
     353             :                 log_fatal("Empty tag '%s' for '%s'.\n", tag, path);
     354             :                 return -1;
     355             :                 /* LCOV_EXCL_STOP */
     356             :         }
     357             : 
     358         165 :         pathprint(value, value_size, "%s", i);
     359             : 
     360         165 :         return 0;
     361             : }
     362             : #endif
     363             : 
     364             : /**
     365             :  * Get the device file from the device number.
     366             :  *
     367             :  * It uses /sys/dev/block/.../uevent.
     368             :  *
     369             :  * For null device (major==0) it fails.
     370             :  */
     371             : #if HAVE_LINUX_DEVICE
     372         165 : static int devresolve_sys(dev_t device, char* path, size_t path_size)
     373             : {
     374             :         struct stat st;
     375             :         char buf[PATH_MAX];
     376             : 
     377             :         /* default device path from device number */
     378         165 :         pathprint(path, path_size, "/sys/dev/block/%u:%u/uevent", major(device), minor(device));
     379             : 
     380         165 :         if (tagread(path, "DEVNAME", buf, sizeof(buf)) != 0) {
     381             :                 /* LCOV_EXCL_START */
     382             :                 log_tag("resolve:sys:%u:%u: failed to read DEVNAME tag '%s'\n", major(device), minor(device), path);
     383             :                 return -1;
     384             :                 /* LCOV_EXCL_STOP */
     385             :         }
     386             : 
     387             :         /* set the real device path */
     388         165 :         pathprint(path, path_size, "/dev/%s", buf);
     389             : 
     390             :         /* check the device */
     391         165 :         if (stat(path, &st) != 0) {
     392             :                 /* LCOV_EXCL_START */
     393             :                 log_tag("resolve:sys:%u:%u: failed to stat '%s'\n", major(device), minor(device), path);
     394             :                 return -1;
     395             :                 /* LCOV_EXCL_STOP */
     396             :         }
     397         165 :         if (st.st_rdev != device) {
     398             :                 /* LCOV_EXCL_START */
     399             :                 log_tag("resolve:sys:%u:%u: unexpected device '%u:%u' for '%s'.\n", major(device), minor(device), major(st.st_rdev), minor(st.st_rdev), path);
     400             :                 return -1;
     401             :                 /* LCOV_EXCL_STOP */
     402             :         }
     403             : 
     404         165 :         log_tag("resolve:sys:%u:%u:%s: found\n", major(device), minor(device), path);
     405             : 
     406         165 :         return 0;
     407             : }
     408             : #endif
     409             : 
     410             : /**
     411             :  * Get the device file from the device number.
     412             :  */
     413             : #if HAVE_LINUX_DEVICE
     414         165 : static int devresolve(uint64_t device, char* path, size_t path_size)
     415             : {
     416         165 :         if (devresolve_sys(device, path, path_size) == 0)
     417         165 :                 return 0;
     418             : 
     419           0 :         return -1;
     420             : }
     421             : #endif
     422             : 
     423             : /**
     424             :  * Cache used by blkid.
     425             :  */
     426             : #if HAVE_BLKID
     427             : static blkid_cache cache = 0;
     428             : #endif
     429             : 
     430             : /**
     431             :  * Get the UUID using the /dev/disk/by-uuid/ links.
     432             :  * It doesn't require root permission, and the uuid are always updated.
     433             :  * It doesn't work with Btrfs file-systems that don't export the main UUID
     434             :  * in /dev/disk/by-uuid/.
     435             :  */
     436             : #if HAVE_LINUX_DEVICE
     437        5668 : static int devuuid_dev(uint64_t device, char* uuid, size_t uuid_size)
     438             : {
     439             :         int ret;
     440             :         DIR* d;
     441             :         struct dirent* dd;
     442             :         struct stat st;
     443             : 
     444             :         /* scan the UUID directory searching for the device */
     445        5668 :         d = opendir("/dev/disk/by-uuid");
     446        5668 :         if (!d) {
     447           0 :                 log_tag("uuid:by-uuid:%u:%u: opendir(/dev/disk/by-uuid) failed, %s\n", major(device), minor(device), strerror(errno));
     448             :                 /* directory missing?, likely we are not in Linux */
     449           0 :                 return -1;
     450             :         }
     451             : 
     452       90688 :         while ((dd = readdir(d)) != 0) {
     453             :                 /* skip "." and ".." files, UUIDs never start with '.' */
     454       90688 :                 if (dd->d_name[0] == '.')
     455       11336 :                         continue;
     456             : 
     457       79352 :                 ret = fstatat(dirfd(d), dd->d_name, &st, 0);
     458       79352 :                 if (ret != 0) {
     459           0 :                         log_tag("uuid:by-uuid:%u:%u: fstatat(%s) failed, %s\n", major(device), minor(device), dd->d_name, strerror(errno));
     460             :                         /* generic error, ignore and continue the search */
     461           0 :                         continue;
     462             :                 }
     463             : 
     464             :                 /* if it matches, we have the uuid */
     465       79352 :                 if (S_ISBLK(st.st_mode) && st.st_rdev == (dev_t)device) {
     466             :                         char buf[PATH_MAX];
     467             :                         char path[PATH_MAX];
     468             : 
     469             :                         /* resolve the link */
     470        5668 :                         pathprint(path, sizeof(path), "/dev/disk/by-uuid/%s", dd->d_name);
     471        5668 :                         ret = readlink(path, buf, sizeof(buf));
     472        5668 :                         if (ret < 0 || ret >= PATH_MAX) {
     473           0 :                                 log_tag("uuid:by-uuid:%u:%u: readlink(/dev/disk/by-uuid/%s) failed, %s\n", major(device), minor(device), dd->d_name, strerror(errno));
     474             :                                 /* generic error, ignore and continue the search */
     475           0 :                                 continue;
     476             :                         }
     477        5668 :                         buf[ret] = 0;
     478             : 
     479             :                         /* found */
     480        5668 :                         pathcpy(uuid, uuid_size, dd->d_name);
     481             : 
     482        5668 :                         log_tag("uuid:by-uuid:%u:%u:%s: found %s\n", major(device), minor(device), uuid, buf);
     483             : 
     484        5668 :                         closedir(d);
     485        5668 :                         return 0;
     486             :                 }
     487             :         }
     488             : 
     489           0 :         log_tag("uuid:by-uuid:%u:%u: /dev/disk/by-uuid doesn't contain a matching block device\n", major(device), minor(device));
     490             : 
     491             :         /* not found */
     492           0 :         closedir(d);
     493           0 :         return -1;
     494             : }
     495             : #endif
     496             : 
     497             : /**
     498             :  * Get the UUID using libblkid.
     499             :  * It uses a cache to work without root permission, resulting in UUID
     500             :  * not necessarily recent.
     501             :  * We could call blkid_probe_all() to refresh the UUID, but it would
     502             :  * require root permission to read the superblocks, and resulting in
     503             :  * all the disks spinning.
     504             :  */
     505             : #if HAVE_BLKID
     506           0 : static int devuuid_blkid(uint64_t device, char* uuid, size_t uuid_size)
     507             : {
     508             :         char* devname;
     509             :         char* uuidname;
     510             : 
     511           0 :         devname = blkid_devno_to_devname(device);
     512           0 :         if (!devname) {
     513           0 :                 log_tag("uuid:blkid:%u:%u: blkid_devno_to_devname() failed, %s\n", major(device), minor(device), strerror(errno));
     514             :                 /* device mapping failed */
     515           0 :                 return -1;
     516             :         }
     517             : 
     518           0 :         uuidname = blkid_get_tag_value(cache, "UUID", devname);
     519           0 :         if (!uuidname) {
     520           0 :                 log_tag("uuid:blkid:%u:%u: blkid_get_tag_value(UUID,%s) failed, %s\n", major(device), minor(device), devname, strerror(errno));
     521             :                 /* uuid mapping failed */
     522           0 :                 free(devname);
     523           0 :                 return -1;
     524             :         }
     525             : 
     526           0 :         pathcpy(uuid, uuid_size, uuidname);
     527             : 
     528           0 :         log_tag("uuid:blkid:%u:%u:%s: found %s\n", major(device), minor(device), uuid, devname);
     529             : 
     530           0 :         free(devname);
     531           0 :         free(uuidname);
     532           0 :         return 0;
     533             : }
     534             : #endif
     535             : 
     536             : #ifdef __APPLE__
     537             : static int devuuid_darwin(const char* path, char* uuid, size_t uuid_size)
     538             : {
     539             :         CFStringRef path_apple = CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8);
     540             :         DASessionRef session = DASessionCreate(kCFAllocatorDefault);
     541             : 
     542             :         CFURLRef path_appler = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_apple, kCFURLPOSIXPathStyle, false);
     543             :         DADiskRef disk;
     544             :         do {
     545             :                 disk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, path_appler);
     546             :                 if (disk) {
     547             :                         CFRelease(path_appler);
     548             :                         break;
     549             :                 } else {
     550             :                         CFURLRef parent_path_appler = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, path_appler);
     551             :                         CFRelease(path_appler);
     552             :                         path_appler = parent_path_appler;
     553             :                 }
     554             :         } while (true); // This is guaranteed to succeed eventually because it'll hit `/`.
     555             : 
     556             :         CFDictionaryRef description = DADiskCopyDescription(disk);
     557             :         CFUUIDRef uuid_apple = CFDictionaryGetValue(description, kDADiskDescriptionVolumeUUIDKey);
     558             :         CFStringRef uuid_string = CFUUIDCreateString(kCFAllocatorDefault, uuid_apple);
     559             :         bool success = CFStringGetCString(uuid_string, uuid, uuid_size, kCFStringEncodingUTF8);
     560             :         CFRelease(uuid_string);
     561             :         CFRelease(description);
     562             :         CFRelease(disk);
     563             :         CFRelease(session);
     564             :         CFRelease(path_apple);
     565             : 
     566             :         if (success) {
     567             :                 return 0;
     568             :         } else {
     569             :                 return 1;
     570             :         }
     571             : }
     572             : #endif
     573             : 
     574        5672 : int devuuid(uint64_t device, const char* path, char* uuid, size_t uuid_size)
     575             : {
     576             :         (void)path;
     577             :         (void)device;
     578             : 
     579             : #ifdef __APPLE__
     580             :         if (devuuid_darwin(path, uuid, uuid_size) == 0)
     581             :                 return 0;
     582             : #endif
     583             : 
     584             : #if HAVE_LINUX_DEVICE
     585             :         /* if the major is the null device */
     586        5672 :         if (major(device) == 0) {
     587             :                 /* obtain the real device */
     588           4 :                 if (devdereference(device, &device) != 0) {
     589             :                         /* LCOV_EXCL_START */
     590             :                         return -1;
     591             :                         /* LCOV_EXCL_STOP */
     592             :                 }
     593             :         }
     594             : #endif
     595             : 
     596             :         /* first try with the /dev/disk/by-uuid version */
     597             : #if HAVE_LINUX_DEVICE
     598        5668 :         if (devuuid_dev(device, uuid, uuid_size) == 0)
     599        5668 :                 return 0;
     600             : #else
     601             :         log_tag("uuid:by-uuid:%u:%u: by-uuid not supported\n", major(device), minor(device));
     602             : #endif
     603             : 
     604             :         /* fall back to blkid for other cases */
     605             : #if HAVE_BLKID
     606           0 :         if (devuuid_blkid(device, uuid, uuid_size) == 0)
     607           0 :                 return 0;
     608             : #else
     609             :         log_tag("uuid:blkid:%u:%u: blkid support not compiled in\n", major(device), minor(device));
     610             : #endif
     611             : 
     612           0 :         log_tag("uuid:notfound:%u:%u:\n", major(device), minor(device));
     613             : 
     614             :         /* not supported */
     615             :         (void)uuid;
     616             :         (void)uuid_size;
     617           0 :         return -1;
     618             : }
     619             : 
     620       11432 : int filephy(const char* path, uint64_t size, uint64_t* physical)
     621             : {
     622             : #if HAVE_LINUX_FIEMAP_H
     623             :         /* In Linux get the real physical address of the file */
     624             :         /* Note that FIEMAP doesn't require root permission */
     625             :         int f;
     626             :         struct {
     627             :                 struct fiemap fiemap;
     628             :                 struct fiemap_extent extent;
     629             :         } fm;
     630             :         unsigned int blknum;
     631             : 
     632       11432 :         f = open(path, O_RDONLY);
     633       11432 :         if (f == -1) {
     634           0 :                 return -1;
     635             :         }
     636             : 
     637             :         /* first try with FIEMAP */
     638             :         /* if works for ext2, ext3, ext4, xfs, btrfs */
     639       11432 :         memset(&fm, 0, sizeof(fm));
     640       11432 :         fm.fiemap.fm_start = 0;
     641       11432 :         fm.fiemap.fm_length = ~0ULL;
     642       11432 :         fm.fiemap.fm_flags = FIEMAP_FLAG_SYNC; /* required to ensure that just created files report a valid address and not 0 */
     643       11432 :         fm.fiemap.fm_extent_count = 1; /* we are interested only at the first block */
     644             : 
     645       11432 :         if (ioctl(f, FS_IOC_FIEMAP, &fm) != -1) {
     646       11432 :                 uint32_t flags = fm.fiemap.fm_extents[0].fe_flags;
     647       11432 :                 uint64_t offset = fm.fiemap.fm_extents[0].fe_physical;
     648             : 
     649             :                 /* check some condition for validating the offset */
     650       11432 :                 if (flags & FIEMAP_EXTENT_DATA_INLINE) {
     651             :                         /* if the data is inline, we don't have an offset to report */
     652           0 :                         *physical = FILEPHY_WITHOUT_OFFSET;
     653       11432 :                 } else if (flags & FIEMAP_EXTENT_UNKNOWN) {
     654             :                         /* if the offset is unknown, we don't have an offset to report */
     655           0 :                         *physical = FILEPHY_WITHOUT_OFFSET;
     656       11432 :                 } else if (offset == 0) {
     657             :                         /* 0 is the general fallback for file-systems when */
     658             :                         /* they don't have an offset to report */
     659          14 :                         *physical = FILEPHY_WITHOUT_OFFSET;
     660             :                 } else {
     661             :                         /* finally report the real offset */
     662       11418 :                         *physical = offset + FILEPHY_REAL_OFFSET;
     663             :                 }
     664             : 
     665       11432 :                 if (close(f) == -1)
     666           0 :                         return -1;
     667       11432 :                 return 0;
     668             :         }
     669             : 
     670             :         /* if the file is empty, FIBMAP doesn't work, and we don't even try to use it */
     671           0 :         if (size == 0) {
     672           0 :                 *physical = FILEPHY_WITHOUT_OFFSET;
     673           0 :                 if (close(f) == -1)
     674           0 :                         return -1;
     675           0 :                 return 0;
     676             :         }
     677             : 
     678             :         /* then try with FIBMAP */
     679             :         /* it works for jfs, reiserfs, ntfs-3g */
     680             :         /* in exfat it always returns 0, that it's anyway better than the fake inodes */
     681           0 :         blknum = 0; /* first block */
     682           0 :         if (ioctl(f, FIBMAP, &blknum) != -1) {
     683           0 :                 *physical = blknum + FILEPHY_REAL_OFFSET;
     684           0 :                 if (close(f) == -1)
     685           0 :                         return -1;
     686           0 :                 return 0;
     687             :         }
     688             : 
     689             :         /* otherwise don't use anything, and keep the directory traversal order */
     690             :         /* at now this should happen only for vfat */
     691             :         /* and it's surely better than using fake inodes */
     692           0 :         *physical = FILEPHY_UNREPORTED_OFFSET;
     693           0 :         if (close(f) == -1)
     694           0 :                 return -1;
     695             : #else
     696             :         /* In a generic Unix use a dummy value for all the files */
     697             :         /* We don't want to risk to use the inode without knowing */
     698             :         /* if it really improves performance. */
     699             :         /* In this way we keep them in the directory traversal order */
     700             :         /* that at least keeps files in the same directory together. */
     701             :         /* Note also that in newer file-system with snapshot, like ZFS, */
     702             :         /* the inode doesn't represent even more the disk position, because files */
     703             :         /* are not overwritten in place, but rewritten in another location */
     704             :         /* of the disk. */
     705             :         *physical = FILEPHY_UNREPORTED_OFFSET;
     706             : 
     707             :         (void)path; /* not used here */
     708             :         (void)size;
     709             : #endif
     710             : 
     711           0 :         return 0;
     712             : }
     713             : 
     714        5483 : int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_hardlinks, uint64_t* total_space, uint64_t* free_space)
     715             : {
     716             :         char type[64];
     717             :         const char* ptype;
     718             : 
     719             : #if HAVE_STATFS
     720             :         struct statfs st;
     721             : 
     722        5483 :         if (statfs(path, &st) != 0) {
     723             :                 char dir[PATH_MAX];
     724             :                 char* slash;
     725             : 
     726          24 :                 if (errno != ENOENT) {
     727           0 :                         return -1;
     728             :                 }
     729             : 
     730             :                 /* if it doesn't exist, we assume a file */
     731             :                 /* and we check for the containing dir */
     732          24 :                 if (strlen(path) + 1 > sizeof(dir)) {
     733           0 :                         errno = ENAMETOOLONG;
     734           0 :                         return -1;
     735             :                 }
     736             : 
     737          24 :                 strcpy(dir, path);
     738             : 
     739          24 :                 slash = strrchr(dir, '/');
     740          24 :                 if (!slash)
     741           0 :                         return -1;
     742             : 
     743          24 :                 *slash = 0;
     744          24 :                 if (statfs(dir, &st) != 0)
     745           0 :                         return -1;
     746             :         }
     747             : #endif
     748             : 
     749             :         /* to get the fs type check "man stat" or "stat -f -t FILE" */
     750        5483 :         if (has_persistent_inode) {
     751             : #if HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
     752         575 :                 switch (st.f_type) {
     753           0 :                 case 0x65735546 : /* FUSE, "fuseblk" in the stat command */
     754             :                 case 0x4d44 : /* VFAT, "msdos" in the stat command */
     755           0 :                         *has_persistent_inode = 0;
     756           0 :                         break;
     757         575 :                 default :
     758             :                         /* by default assume yes */
     759         575 :                         *has_persistent_inode = 1;
     760         575 :                         break;
     761             :                 }
     762             : #else
     763             :                 /* in Unix inodes are persistent by default */
     764             :                 *has_persistent_inode = 1;
     765             : #endif
     766             :         }
     767             : 
     768        5483 :         if (has_syncronized_hardlinks) {
     769             : #if HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
     770         575 :                 switch (st.f_type) {
     771           0 :                 case 0x5346544E : /* NTFS */
     772             :                 case 0x4d44 : /* VFAT, "msdos" in the stat command */
     773           0 :                         *has_syncronized_hardlinks = 0;
     774           0 :                         break;
     775         575 :                 default :
     776             :                         /* by default assume yes */
     777         575 :                         *has_syncronized_hardlinks = 1;
     778         575 :                         break;
     779             :                 }
     780             : #else
     781             :                 /* in Unix hardlinks share the same metadata by default */
     782             :                 *has_syncronized_hardlinks = 1;
     783             : #endif
     784             :         }
     785             : 
     786        5483 :         if (total_space) {
     787             : #if HAVE_STATFS
     788        4908 :                 *total_space = st.f_bsize * (uint64_t)st.f_blocks;
     789             : #else
     790             :                 *total_space = 0;
     791             : #endif
     792             :         }
     793             : 
     794        5483 :         if (free_space) {
     795             : #if HAVE_STATFS
     796        4908 :                 *free_space = st.f_bsize * (uint64_t)st.f_bfree;
     797             : #else
     798             :                 *free_space = 0;
     799             : #endif
     800             :         }
     801             : 
     802             : #if HAVE_STATFS && HAVE_STRUCT_STATFS_F_FSTYPENAME
     803             :         /* get the filesystem type directly from the struct (Mac OS X) */
     804             :         (void)type;
     805             :         ptype = st.f_fstypename;
     806             : #elif HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
     807             :         /* get the filesystem type from f_type (Linux) */
     808             :         /* from: https://github.com/influxdata/gopsutil/blob/master/disk/disk_linux.go */
     809        5483 :         switch (st.f_type) {
     810           0 :         case 0x65735546 : ptype = "fuseblk"; break;
     811           0 :         case 0x4D44 : ptype = "vfat/msdos"; break;
     812        5483 :         case 0xEF53 : ptype = "ext2/3/4"; break;
     813           0 :         case 0x6969 : ptype = "nfs"; break; /* remote */
     814           0 :         case 0x6E667364 : ptype = "nfsd"; break; /* remote */
     815           0 :         case 0x517B : ptype = "smb"; break; /* remote */
     816           0 :         case 0x5346544E : ptype = "ntfs"; break;
     817           0 :         case 0x52654973 : ptype = "reiserfs"; break;
     818           0 :         case 0x3153464A : ptype = "jfs"; break;
     819           0 :         case 0x58465342 : ptype = "xfs"; break;
     820           0 :         case 0x9123683E : ptype = "btrfs"; break;
     821           0 :         case 0x2FC12FC1 : ptype = "zfs"; break;
     822           0 :         default :
     823           0 :                 snprintf(type, sizeof(type), "0x%X", (unsigned)st.f_type);
     824           0 :                 ptype = type;
     825             :         }
     826             : #else
     827             :         (void)type;
     828             :         ptype = "unknown";
     829             : #endif
     830             : 
     831        5483 :         log_tag("statfs:%s: %s \n", ptype, path);
     832             : 
     833        5483 :         return 0;
     834             : }
     835             : 
     836     3744805 : uint64_t tick(void)
     837             : {
     838             : #if HAVE_MACH_ABSOLUTE_TIME
     839             :         /* for Mac OS X */
     840             :         return mach_absolute_time();
     841             : #elif HAVE_CLOCK_GETTIME && (defined(CLOCK_MONOTONIC) || defined(CLOCK_MONOTONIC_RAW))
     842             :         /* for Linux */
     843             :         struct timespec tv;
     844             : 
     845             :         /* nanosecond precision with clock_gettime() */
     846             : #if defined(CLOCK_MONOTONIC_RAW)
     847     3744805 :         if (clock_gettime(CLOCK_MONOTONIC_RAW, &tv) != 0) {
     848             : #else
     849             :         if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
     850             : #endif
     851           0 :                 return 0;
     852             :         }
     853             : 
     854     3744805 :         return tv.tv_sec * 1000000000ULL + tv.tv_nsec;
     855             : #else
     856             :         /* other platforms */
     857             :         struct timeval tv;
     858             : 
     859             :         /* microsecond precision with gettimeofday() */
     860             :         if (gettimeofday(&tv, 0) != 0) {
     861             :                 return 0;
     862             :         }
     863             : 
     864             :         return tv.tv_sec * 1000000ULL + tv.tv_usec;
     865             : #endif
     866             : }
     867             : 
     868        3362 : uint64_t tick_ms(void)
     869             : {
     870             :         struct timeval tv;
     871             : 
     872        3362 :         if (gettimeofday(&tv, 0) != 0)
     873           0 :                 return 0;
     874             : 
     875        3362 :         return tv.tv_sec * 1000ULL + tv.tv_usec / 1000;
     876             : }
     877             : 
     878         284 : int randomize(void* ptr, size_t size)
     879             : {
     880             :         int f;
     881             :         ssize_t ret;
     882             : 
     883         284 :         f = open("/dev/urandom", O_RDONLY);
     884         284 :         if (f == -1)
     885           0 :                 return -1;
     886             : 
     887         284 :         ret = read(f, ptr, size);
     888         284 :         if (ret < 0 || (size_t)ret != size) {
     889           0 :                 close(f);
     890           0 :                 return -1;
     891             :         }
     892             : 
     893         284 :         if (close(f) != 0)
     894           0 :                 return -1;
     895             : 
     896         284 :         return 0;
     897             : }
     898             : 
     899             : /**
     900             :  * Read a file extracting the contained device number in %u:%u format.
     901             :  * Return 0 on error.
     902             :  */
     903             : #if HAVE_LINUX_DEVICE
     904         130 : static dev_t devread(const char* path)
     905             : {
     906             :         int f;
     907             :         int ret;
     908             :         int len;
     909             :         char buf[64];
     910             :         char* e;
     911             :         unsigned ma;
     912             :         unsigned mi;
     913             : 
     914         130 :         f = open(path, O_RDONLY);
     915         130 :         if (f == -1) {
     916             :                 /* LCOV_EXCL_START */
     917             :                 log_fatal("Failed to open '%s'.\n", path);
     918             :                 return 0;
     919             :                 /* LCOV_EXCL_STOP */
     920             :         }
     921             : 
     922         130 :         len = read(f, buf, sizeof(buf));
     923         130 :         if (len < 0) {
     924             :                 /* LCOV_EXCL_START */
     925             :                 close(f);
     926             :                 log_fatal("Failed to read '%s'.\n", path);
     927             :                 return 0;
     928             :                 /* LCOV_EXCL_STOP */
     929             :         }
     930         130 :         if (len == sizeof(buf)) {
     931             :                 /* LCOV_EXCL_START */
     932             :                 close(f);
     933             :                 log_fatal("Too long read '%s'.\n", path);
     934             :                 return 0;
     935             :                 /* LCOV_EXCL_STOP */
     936             :         }
     937             : 
     938         130 :         ret = close(f);
     939         130 :         if (ret != 0) {
     940             :                 /* LCOV_EXCL_START */
     941             :                 log_fatal("Failed to close '%s'.\n", path);
     942             :                 return 0;
     943             :                 /* LCOV_EXCL_STOP */
     944             :         }
     945             : 
     946         130 :         buf[len] = 0;
     947             : 
     948         130 :         ma = strtoul(buf, &e, 10);
     949         130 :         if (*e != ':') {
     950             :                 /* LCOV_EXCL_START */
     951             :                 log_fatal("Invalid format in '%s' for '%s'.\n", path, buf);
     952             :                 return 0;
     953             :                 /* LCOV_EXCL_STOP */
     954             :         }
     955             : 
     956         130 :         mi = strtoul(e + 1, &e, 10);
     957         130 :         if (*e != 0 && !isspace(*e)) {
     958             :                 /* LCOV_EXCL_START */
     959             :                 log_fatal("Invalid format in '%s' for '%s'.\n", path, buf);
     960             :                 return 0;
     961             :                 /* LCOV_EXCL_STOP */
     962             :         }
     963             : 
     964         130 :         return makedev(ma, mi);
     965             : }
     966             : #endif
     967             : 
     968             : /**
     969             :  * Read a device tree filling the specified list of disk_t entries.
     970             :  */
     971             : #if HAVE_LINUX_DEVICE
     972         130 : static int devtree(const char* name, const char* smartctl, const int* smartignore, dev_t device, devinfo_t* parent, tommy_list* list)
     973             : {
     974             :         char path[PATH_MAX];
     975             :         DIR* d;
     976         130 :         int slaves = 0;
     977             : 
     978         130 :         pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/slaves", major(device), minor(device));
     979             : 
     980             :         /* check if there is a slaves list */
     981         130 :         d = opendir(path);
     982         130 :         if (d != 0) {
     983             :                 struct dirent* dd;
     984             : 
     985         260 :                 while ((dd = readdir(d)) != 0) {
     986         195 :                         if (dd->d_name[0] != '.') {
     987             :                                 dev_t subdev;
     988             : 
     989             :                                 /* for each slave, expand the full potential tree */
     990          65 :                                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/slaves/%s/dev", major(device), minor(device), dd->d_name);
     991             : 
     992          65 :                                 subdev = devread(path);
     993          65 :                                 if (!subdev) {
     994             :                                         /* LCOV_EXCL_START */
     995             :                                         closedir(d);
     996             :                                         return -1;
     997             :                                         /* LCOV_EXCL_STOP */
     998             :                                 }
     999             : 
    1000          65 :                                 if (devtree(name, smartctl, smartignore, subdev, parent, list) != 0) {
    1001             :                                         /* LCOV_EXCL_START */
    1002             :                                         closedir(d);
    1003             :                                         return -1;
    1004             :                                         /* LCOV_EXCL_STOP */
    1005             :                                 }
    1006             : 
    1007          65 :                                 ++slaves;
    1008             :                         }
    1009             :                 }
    1010             : 
    1011          65 :                 closedir(d);
    1012             :         }
    1013             : 
    1014             :         /* if no slaves found */
    1015         130 :         if (!slaves) {
    1016             :                 /* this is a raw device */
    1017             :                 devinfo_t* devinfo;
    1018             : 
    1019             :                 /* check if it's a real device */
    1020          65 :                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/device", major(device), minor(device));
    1021          65 :                 if (access(path, F_OK) != 0) {
    1022             :                         /* get the parent device */
    1023          65 :                         pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/../dev", major(device), minor(device));
    1024             : 
    1025          65 :                         device = devread(path);
    1026          65 :                         if (!device) {
    1027             :                                 /* LCOV_EXCL_START */
    1028             :                                 return -1;
    1029             :                                 /* LCOV_EXCL_STOP */
    1030             :                         }
    1031             :                 }
    1032             : 
    1033             :                 /* get the device file */
    1034          65 :                 if (devresolve(device, path, sizeof(path)) != 0) {
    1035             :                         /* LCOV_EXCL_START */
    1036             :                         log_fatal("Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    1037             :                         return -1;
    1038             :                         /* LCOV_EXCL_STOP */
    1039             :                 }
    1040             : 
    1041          65 :                 devinfo = calloc_nofail(1, sizeof(devinfo_t));
    1042             : 
    1043          65 :                 devinfo->device = device;
    1044          65 :                 pathcpy(devinfo->name, sizeof(devinfo->name), name);
    1045          65 :                 pathcpy(devinfo->smartctl, sizeof(devinfo->smartctl), smartctl);
    1046          65 :                 memcpy(devinfo->smartignore, smartignore, sizeof(devinfo->smartignore));
    1047          65 :                 pathcpy(devinfo->file, sizeof(devinfo->file), path);
    1048          65 :                 devinfo->parent = parent;
    1049             : 
    1050             :                 /* insert in the list */
    1051          65 :                 tommy_list_insert_tail(list, &devinfo->node, devinfo);
    1052             :         }
    1053             : 
    1054         130 :         return 0;
    1055             : }
    1056             : #endif
    1057             : 
    1058             : /**
    1059             :  * Scan all the devices.
    1060             :  *
    1061             :  * If a device is already in, it's not added again.
    1062             :  */
    1063             : #if HAVE_LINUX_DEVICE
    1064           0 : static int devscan(tommy_list* list)
    1065             : {
    1066             :         char dir[PATH_MAX];
    1067             :         DIR* d;
    1068             :         struct dirent* dd;
    1069             : 
    1070           0 :         pathprint(dir, sizeof(dir), "/sys/dev/block/");
    1071             : 
    1072             :         /* check if there is a slaves list */
    1073           0 :         d = opendir(dir);
    1074           0 :         if (d == 0) {
    1075             :                 /* LCOV_EXCL_START */
    1076             :                 log_fatal("Failed to open dir '%s'.\n", dir);
    1077             :                 return -1;
    1078             :                 /* LCOV_EXCL_STOP */
    1079             :         }
    1080             : 
    1081           0 :         while ((dd = readdir(d)) != 0) {
    1082             :                 char path[PATH_MAX];
    1083             :                 tommy_node* i;
    1084             :                 dev_t device;
    1085             :                 devinfo_t* devinfo;
    1086             : 
    1087           0 :                 if (dd->d_name[0] == '.')
    1088           0 :                         continue;
    1089             : 
    1090           0 :                 pathprint(path, sizeof(path), "/sys/dev/block/%s/device", dd->d_name);
    1091             : 
    1092             :                 /* check if it's a real device */
    1093           0 :                 if (access(path, F_OK) != 0)
    1094           0 :                         continue;
    1095             : 
    1096           0 :                 pathprint(path, sizeof(path), "/sys/dev/block/%s/dev", dd->d_name);
    1097             : 
    1098           0 :                 device = devread(path);
    1099           0 :                 if (!device) {
    1100             :                         /* LCOV_EXCL_START */
    1101             :                         log_tag("scan:skip: Skipping device %s because failed to read its device number.\n", dd->d_name);
    1102             :                         continue;
    1103             :                         /* LCOV_EXCL_STOP */
    1104             :                 }
    1105             : 
    1106             :                 /* check if already present */
    1107           0 :                 for (i = tommy_list_head(list); i != 0; i = i->next) {
    1108           0 :                         devinfo = i->data;
    1109           0 :                         if (devinfo->device == device)
    1110           0 :                                 break;
    1111             :                 }
    1112             : 
    1113             :                 /* if already present */
    1114           0 :                 if (i != 0)
    1115           0 :                         continue;
    1116             : 
    1117             :                 /* get the device file */
    1118           0 :                 if (devresolve(device, path, sizeof(path)) != 0) {
    1119             :                         /* LCOV_EXCL_START */
    1120             :                         log_tag("scan:skip: Skipping device %u:%u because failed to resolve.\n", major(device), minor(device));
    1121             :                         continue;
    1122             :                         /* LCOV_EXCL_STOP */
    1123             :                 }
    1124             : 
    1125           0 :                 devinfo = calloc_nofail(1, sizeof(devinfo_t));
    1126             : 
    1127           0 :                 devinfo->device = device;
    1128           0 :                 pathcpy(devinfo->file, sizeof(devinfo->file), path);
    1129             : 
    1130             :                 /* insert in the list */
    1131           0 :                 tommy_list_insert_tail(list, &devinfo->node, devinfo);
    1132             :         }
    1133             : 
    1134           0 :         closedir(d);
    1135           0 :         return 0;
    1136             : }
    1137             : #endif
    1138             : 
    1139             : /**
    1140             :  * Get SMART attributes.
    1141             :  */
    1142             : #if HAVE_LINUX_DEVICE
    1143           0 : static int devsmart(dev_t device, const char* name, const char* smartctl, uint64_t* smart, char* serial, char* vendor, char* model)
    1144             : {
    1145             :         char cmd[PATH_MAX + 64];
    1146             :         char file[PATH_MAX];
    1147             :         FILE* f;
    1148             :         int ret;
    1149             : 
    1150           0 :         if (devresolve(device, file, sizeof(file)) != 0) {
    1151             :                 /* LCOV_EXCL_START */
    1152             :                 log_fatal("Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    1153             :                 return -1;
    1154             :                 /* LCOV_EXCL_STOP */
    1155             :         }
    1156             : 
    1157             :         /* if there is a custom smartctl command */
    1158           0 :         if (smartctl[0]) {
    1159             :                 char option[PATH_MAX];
    1160           0 :                 snprintf(option, sizeof(option), smartctl, file);
    1161           0 :                 snprintf(cmd, sizeof(cmd), "smartctl -a %s", option);
    1162             :         } else {
    1163           0 :                 snprintf(cmd, sizeof(cmd), "smartctl -a %s", file);
    1164             :         }
    1165             : 
    1166           0 :         log_tag("smartctl:%s:%s:run: %s\n", file, name, cmd);
    1167             : 
    1168           0 :         f = popen(cmd, "r");
    1169           0 :         if (!f) {
    1170             :                 /* LCOV_EXCL_START */
    1171             :                 log_fatal("Failed to run '%s' (from popen).\n", cmd);
    1172             :                 return -1;
    1173             :                 /* LCOV_EXCL_STOP */
    1174             :         }
    1175             : 
    1176           0 :         if (smartctl_attribute(f, file, name, smart, serial, vendor, model) != 0) {
    1177             :                 /* LCOV_EXCL_START */
    1178             :                 pclose(f);
    1179             :                 return -1;
    1180             :                 /* LCOV_EXCL_STOP */
    1181             :         }
    1182             : 
    1183           0 :         ret = pclose(f);
    1184             : 
    1185           0 :         log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
    1186             : 
    1187           0 :         if (!WIFEXITED(ret)) {
    1188             :                 /* LCOV_EXCL_START */
    1189             :                 log_fatal("Failed to run '%s' (not exited).\n", cmd);
    1190             :                 return -1;
    1191             :                 /* LCOV_EXCL_STOP */
    1192             :         }
    1193           0 :         if (WEXITSTATUS(ret) == 127) {
    1194             :                 /* LCOV_EXCL_START */
    1195             :                 log_fatal("Failed to run '%s' (from sh).\n", cmd);
    1196             :                 return -1;
    1197             :                 /* LCOV_EXCL_STOP */
    1198             :         }
    1199             : 
    1200             :         /* store the return smartctl return value */
    1201           0 :         smart[SMART_FLAGS] = WEXITSTATUS(ret);
    1202             : 
    1203           0 :         return 0;
    1204             : }
    1205             : #endif
    1206             : 
    1207             : /**
    1208             :  * Get POWER state.
    1209             :  */
    1210             : #if HAVE_LINUX_DEVICE
    1211           0 : static int devprobe(dev_t device, const char* name, const char* smartctl, int* power)
    1212             : {
    1213             :         char cmd[PATH_MAX + 64];
    1214             :         char file[PATH_MAX];
    1215             :         FILE* f;
    1216             :         int ret;
    1217             : 
    1218           0 :         if (devresolve(device, file, sizeof(file)) != 0) {
    1219             :                 /* LCOV_EXCL_START */
    1220             :                 log_fatal("Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    1221             :                 return -1;
    1222             :                 /* LCOV_EXCL_STOP */
    1223             :         }
    1224             : 
    1225             :         /* if there is a custom smartctl command */
    1226           0 :         if (smartctl[0]) {
    1227             :                 char option[PATH_MAX];
    1228           0 :                 snprintf(option, sizeof(option), smartctl, file);
    1229           0 :                 snprintf(cmd, sizeof(cmd), "smartctl -n standby,3 -i %s", option);
    1230             :         } else {
    1231           0 :                 snprintf(cmd, sizeof(cmd), "smartctl -n standby,3 -i %s", file);
    1232             :         }
    1233             : 
    1234           0 :         log_tag("smartctl:%s:%s:run: %s\n", file, name, cmd);
    1235             : 
    1236           0 :         f = popen(cmd, "r");
    1237           0 :         if (!f) {
    1238             :                 /* LCOV_EXCL_START */
    1239             :                 log_fatal("Failed to run '%s' (from popen).\n", cmd);
    1240             :                 return -1;
    1241             :                 /* LCOV_EXCL_STOP */
    1242             :         }
    1243             : 
    1244           0 :         if (smartctl_flush(f, file, name) != 0) {
    1245             :                 /* LCOV_EXCL_START */
    1246             :                 pclose(f);
    1247             :                 return -1;
    1248             :                 /* LCOV_EXCL_STOP */
    1249             :         }
    1250             : 
    1251           0 :         ret = pclose(f);
    1252             : 
    1253           0 :         log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
    1254             : 
    1255           0 :         if (!WIFEXITED(ret)) {
    1256             :                 /* LCOV_EXCL_START */
    1257             :                 log_fatal("Failed to run '%s' (not exited).\n", cmd);
    1258             :                 return -1;
    1259             :                 /* LCOV_EXCL_STOP */
    1260             :         }
    1261           0 :         if (WEXITSTATUS(ret) == 127) {
    1262             :                 /* LCOV_EXCL_START */
    1263             :                 log_fatal("Failed to run '%s' (from sh).\n", cmd);
    1264             :                 return -1;
    1265             :                 /* LCOV_EXCL_STOP */
    1266             :         }
    1267           0 :         if (WEXITSTATUS(ret) == 0) {
    1268           0 :                 *power = POWER_ACTIVE;
    1269           0 :         } else if (WEXITSTATUS(ret) == 3) {
    1270           0 :                 *power = POWER_STANDBY;
    1271             :         } else {
    1272             :                 /* LCOV_EXCL_START */
    1273             :                 log_fatal("Failed to run '%s' with return code %xh.\n", cmd, WEXITSTATUS(ret));
    1274             :                 return -1;
    1275             :                 /* LCOV_EXCL_STOP */
    1276             :         }
    1277             : 
    1278           0 :         return 0;
    1279             : }
    1280             : #endif
    1281             : 
    1282             : /**
    1283             :  * Spin down a specific device.
    1284             :  */
    1285             : #if HAVE_LINUX_DEVICE
    1286           5 : static int devdown(dev_t device, const char* name, const char* smartctl)
    1287             : {
    1288             :         char cmd[PATH_MAX + 64];
    1289             :         char file[PATH_MAX];
    1290             :         FILE* f;
    1291             :         int ret;
    1292             : 
    1293           5 :         if (devresolve(device, file, sizeof(file)) != 0) {
    1294             :                 /* LCOV_EXCL_START */
    1295             :                 log_fatal("Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    1296             :                 return -1;
    1297             :                 /* LCOV_EXCL_STOP */
    1298             :         }
    1299             : 
    1300             :         /* if there is a custom smartctl command */
    1301           5 :         if (smartctl[0]) {
    1302             :                 char option[PATH_MAX];
    1303           5 :                 snprintf(option, sizeof(option), smartctl, file);
    1304           5 :                 snprintf(cmd, sizeof(cmd), "smartctl -s standby,now %s", option);
    1305             :         } else {
    1306           0 :                 snprintf(cmd, sizeof(cmd), "smartctl -s standby,now %s", file);
    1307             :         }
    1308             : 
    1309           5 :         log_tag("smartctl:%s:%s:run: %s\n", file, name, cmd);
    1310             : 
    1311           5 :         f = popen(cmd, "r");
    1312           5 :         if (!f) {
    1313             :                 /* LCOV_EXCL_START */
    1314             :                 log_fatal("Failed to run '%s' (from popen).\n", cmd);
    1315             :                 return -1;
    1316             :                 /* LCOV_EXCL_STOP */
    1317             :         }
    1318             : 
    1319           5 :         if (smartctl_flush(f, file, name) != 0) {
    1320             :                 /* LCOV_EXCL_START */
    1321             :                 pclose(f);
    1322             :                 return -1;
    1323             :                 /* LCOV_EXCL_STOP */
    1324             :         }
    1325             : 
    1326           5 :         ret = pclose(f);
    1327             : 
    1328           5 :         log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
    1329             : 
    1330           5 :         if (!WIFEXITED(ret)) {
    1331             :                 /* LCOV_EXCL_START */
    1332             :                 log_fatal("Failed to run '%s' (not exited).\n", cmd);
    1333             :                 return -1;
    1334             :                 /* LCOV_EXCL_STOP */
    1335             :         }
    1336           5 :         if (WEXITSTATUS(ret) == 127) {
    1337             :                 /* LCOV_EXCL_START */
    1338             :                 log_fatal("Failed to run '%s' (from sh).\n", cmd);
    1339             :                 return -1;
    1340             :                 /* LCOV_EXCL_STOP */
    1341             :         }
    1342           0 :         if (WEXITSTATUS(ret) != 0) {
    1343             :                 /* LCOV_EXCL_START */
    1344             :                 log_fatal("Failed to run '%s' with return code %xh.\n", cmd, WEXITSTATUS(ret));
    1345             :                 return -1;
    1346             :                 /* LCOV_EXCL_STOP */
    1347             :         }
    1348             : 
    1349           0 :         return 0;
    1350             : }
    1351             : #endif
    1352             : 
    1353             : /**
    1354             :  * Spin down a specific device if it's up.
    1355             :  */
    1356             : #if HAVE_LINUX_DEVICE
    1357           0 : static int devdownifup(dev_t device, const char* name, const char* smartctl, int* power)
    1358             : {
    1359           0 :         *power = POWER_UNKNOWN;
    1360             : 
    1361           0 :         if (devprobe(device, name, smartctl, power) != 0)
    1362           0 :                 return -1;
    1363             : 
    1364           0 :         if (*power == POWER_ACTIVE)
    1365           0 :                 return devdown(device, name, smartctl);
    1366             : 
    1367           0 :         return 0;
    1368             : }
    1369             : #endif
    1370             : 
    1371             : /**
    1372             :  * Spin up a specific device.
    1373             :  */
    1374             : #if HAVE_LINUX_DEVICE
    1375          30 : static int devup(dev_t device)
    1376             : {
    1377             :         char file[PATH_MAX];
    1378             :         int ret;
    1379             :         int f;
    1380             :         void* buf;
    1381             : 
    1382          30 :         if (devresolve(device, file, sizeof(file)) != 0) {
    1383             :                 /* LCOV_EXCL_START */
    1384             :                 log_fatal("Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    1385             :                 return -1;
    1386             :                 /* LCOV_EXCL_STOP */
    1387             :         }
    1388             : 
    1389             :         /* O_DIRECT requires memory aligned to the block size */
    1390          30 :         if (posix_memalign(&buf, 512, 512) != 0) {
    1391             :                 /* LCOV_EXCL_START */
    1392             :                 log_fatal("Failed to allocate aligned memory for device '%u:%u'.\n", major(device), minor(device));
    1393             :                 return -1;
    1394             :                 /* LCOV_EXCL_STOP */
    1395             :         }
    1396             : 
    1397          30 :         f = open(file, O_RDONLY | O_DIRECT);
    1398          30 :         if (f < 0) {
    1399             :                 /* LCOV_EXCL_START */
    1400             :                 free(buf);
    1401             :                 log_fatal("Failed to open device '%u:%u'.\n", major(device), minor(device));
    1402             :                 return -1;
    1403             :                 /* LCOV_EXCL_STOP */
    1404             :         }
    1405             : 
    1406           0 :         ret = read(f, buf, 512);
    1407           0 :         if (ret < 0) {
    1408             :                 /* LCOV_EXCL_START */
    1409             :                 close(f);
    1410             :                 free(buf);
    1411             :                 log_fatal("Failed to read device '%u:%u'.\n", major(device), minor(device));
    1412             :                 return -1;
    1413             :                 /* LCOV_EXCL_STOP */
    1414             :         }
    1415             : 
    1416           0 :         ret = close(f);
    1417           0 :         if (ret < 0) {
    1418             :                 /* LCOV_EXCL_START */
    1419             :                 free(buf);
    1420             :                 log_fatal("Failed to close device '%u:%u'.\n", major(device), minor(device));
    1421             :                 return -1;
    1422             :                 /* LCOV_EXCL_STOP */
    1423             :         }
    1424             : 
    1425           0 :         free(buf);
    1426           0 :         return 0;
    1427             : }
    1428             : #endif
    1429             : 
    1430             : /**
    1431             :  * Thread for spinning up.
    1432             :  *
    1433             :  * Note that filling up the devinfo object is done inside this thread,
    1434             :  * to avoid to block the main thread if the device need to be spin up
    1435             :  * to handle stat/resolve requests.
    1436             :  */
    1437          30 : static void* thread_spinup(void* arg)
    1438             : {
    1439             : #if HAVE_LINUX_DEVICE
    1440          30 :         devinfo_t* devinfo = arg;
    1441             :         uint64_t start;
    1442             : 
    1443          30 :         start = tick_ms();
    1444             : 
    1445          30 :         if (devup(devinfo->device) != 0) {
    1446             :                 /* LCOV_EXCL_START */
    1447             :                 return (void*)-1;
    1448             :                 /* LCOV_EXCL_STOP */
    1449             :         }
    1450             : 
    1451           0 :         msg_status("Spunup device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, tick_ms() - start);
    1452             : 
    1453           0 :         return 0;
    1454             : #else
    1455             :         (void)arg;
    1456             :         return (void*)-1;
    1457             : #endif
    1458             : }
    1459             : 
    1460             : /**
    1461             :  * Thread for spinning down.
    1462             :  */
    1463           5 : static void* thread_spindown(void* arg)
    1464             : {
    1465             : #if HAVE_LINUX_DEVICE
    1466           5 :         devinfo_t* devinfo = arg;
    1467             :         uint64_t start;
    1468             : 
    1469           5 :         start = tick_ms();
    1470             : 
    1471           5 :         if (devdown(devinfo->device, devinfo->name, devinfo->smartctl) != 0) {
    1472             :                 /* LCOV_EXCL_START */
    1473             :                 return (void*)-1;
    1474             :                 /* LCOV_EXCL_STOP */
    1475             :         }
    1476             : 
    1477           0 :         msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, tick_ms() - start);
    1478             : 
    1479           0 :         return 0;
    1480             : #else
    1481             :         (void)arg;
    1482             :         return (void*)-1;
    1483             : #endif
    1484             : }
    1485             : 
    1486             : /**
    1487             :  * Thread for spinning down.
    1488             :  */
    1489           0 : static void* thread_spindownifup(void* arg)
    1490             : {
    1491             : #if HAVE_LINUX_DEVICE
    1492           0 :         devinfo_t* devinfo = arg;
    1493             :         uint64_t start;
    1494             :         int power;
    1495             : 
    1496           0 :         start = tick_ms();
    1497             : 
    1498           0 :         if (devdownifup(devinfo->device, devinfo->name, devinfo->smartctl, &power) != 0) {
    1499             :                 /* LCOV_EXCL_START */
    1500             :                 return (void*)-1;
    1501             :                 /* LCOV_EXCL_STOP */
    1502             :         }
    1503             : 
    1504           0 :         if (power == POWER_ACTIVE)
    1505           0 :                 msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, tick_ms() - start);
    1506             : 
    1507           0 :         return 0;
    1508             : #else
    1509             :         (void)arg;
    1510             :         return (void*)-1;
    1511             : #endif
    1512             : }
    1513             : 
    1514             : /**
    1515             :  * Thread for getting smart info.
    1516             :  */
    1517           0 : static void* thread_smart(void* arg)
    1518             : {
    1519             : #if HAVE_LINUX_DEVICE
    1520           0 :         devinfo_t* devinfo = arg;
    1521             : 
    1522           0 :         if (devsmart(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smart, devinfo->smart_serial, devinfo->smart_vendor, devinfo->smart_model) != 0) {
    1523             :                 /* LCOV_EXCL_START */
    1524             :                 return (void*)-1;
    1525             :                 /* LCOV_EXCL_STOP */
    1526             :         }
    1527             : 
    1528           0 :         return 0;
    1529             : #else
    1530             :         (void)arg;
    1531             :         return (void*)-1;
    1532             : #endif
    1533             : }
    1534             : 
    1535             : /**
    1536             :  * Thread for getting power info.
    1537             :  */
    1538           0 : static void* thread_probe(void* arg)
    1539             : {
    1540             : #if HAVE_LINUX_DEVICE
    1541           0 :         devinfo_t* devinfo = arg;
    1542             : 
    1543           0 :         if (devprobe(devinfo->device, devinfo->name, devinfo->smartctl, &devinfo->power) != 0) {
    1544             :                 /* LCOV_EXCL_START */
    1545             :                 return (void*)-1;
    1546             :                 /* LCOV_EXCL_STOP */
    1547             :         }
    1548             : 
    1549           0 :         return 0;
    1550             : #else
    1551             :         (void)arg;
    1552             :         return (void*)-1;
    1553             : #endif
    1554             : }
    1555             : 
    1556           2 : static int device_thread(tommy_list* list, void* (*func)(void* arg))
    1557             : {
    1558           2 :         int fail = 0;
    1559             :         tommy_node* i;
    1560             : 
    1561             : #if HAVE_THREAD
    1562             :         /* start all threads */
    1563          37 :         for (i = tommy_list_head(list); i != 0; i = i->next) {
    1564          35 :                 devinfo_t* devinfo = i->data;
    1565             : 
    1566          35 :                 thread_create(&devinfo->thread, func, devinfo);
    1567             :         }
    1568             : 
    1569             :         /* join all threads */
    1570          37 :         for (i = tommy_list_head(list); i != 0; i = i->next) {
    1571          35 :                 devinfo_t* devinfo = i->data;
    1572             :                 void* retval;
    1573             : 
    1574          35 :                 thread_join(devinfo->thread, &retval);
    1575             : 
    1576          35 :                 if (retval != 0)
    1577          35 :                         ++fail;
    1578             :         }
    1579             : #else
    1580             :         for (i = tommy_list_head(list); i != 0; i = i->next) {
    1581             :                 devinfo_t* devinfo = i->data;
    1582             : 
    1583             :                 if (func(devinfo) != 0)
    1584             :                         ++fail;
    1585             :         }
    1586             : #endif
    1587           2 :         if (fail != 0) {
    1588             :                 /* LCOV_EXCL_START */
    1589             :                 return -1;
    1590             :                 /* LCOV_EXCL_STOP */
    1591             :         }
    1592             : 
    1593           0 :         return 0;
    1594             : }
    1595             : 
    1596           3 : int devquery(tommy_list* high, tommy_list* low, int operation, int others)
    1597             : {
    1598           3 :         void* (*func)(void* arg) = 0;
    1599             : 
    1600           3 :         if (operation == DEVICE_DOWN) {
    1601             :                 /* flush all the disk caches before spinning down */
    1602           1 :                 sync();
    1603             :         }
    1604             : 
    1605             : #if HAVE_LINUX_DEVICE
    1606             :         tommy_node* i;
    1607             :         struct stat st;
    1608             : 
    1609             :         /* sysfs interface is required */
    1610           3 :         if (stat("/sys/dev/block", &st) != 0) {
    1611             :                 /* LCOV_EXCL_START */
    1612             :                 log_fatal("Missing interface /sys/dev/block.\n");
    1613             :                 return -1;
    1614             :                 /* LCOV_EXCL_STOP */
    1615             :         }
    1616             : 
    1617             :         /* for each device */
    1618          68 :         for (i = tommy_list_head(high); i != 0; i = i->next) {
    1619          65 :                 devinfo_t* devinfo = i->data;
    1620          65 :                 uint64_t device = devinfo->device;
    1621             : 
    1622             :                 /* if the major is the null device, find the real one */
    1623          65 :                 if (major(device) == 0) {
    1624             :                         /* obtain the real device */
    1625           0 :                         if (devdereference(device, &device) != 0) {
    1626             :                                 /* LCOV_EXCL_START */
    1627             :                                 log_fatal("Failed to dereference device '%u:%u'.\n", major(device), minor(device));
    1628             :                                 return -1;
    1629             :                                 /* LCOV_EXCL_STOP */
    1630             :                         }
    1631             :                 }
    1632             : 
    1633             :                 /* get the device file */
    1634          65 :                 if (devresolve(device, devinfo->file, sizeof(devinfo->file)) != 0) {
    1635             :                         /* LCOV_EXCL_START */
    1636             :                         log_fatal("Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    1637             :                         return -1;
    1638             :                         /* LCOV_EXCL_STOP */
    1639             :                 }
    1640             : 
    1641             :                 /* expand the tree of devices */
    1642          65 :                 if (devtree(devinfo->name, devinfo->smartctl, devinfo->smartignore, device, devinfo, low) != 0) {
    1643             :                         /* LCOV_EXCL_START */
    1644             :                         log_fatal("Failed to expand device '%u:%u'.\n", major(device), minor(device));
    1645             :                         return -1;
    1646             :                         /* LCOV_EXCL_STOP */
    1647             :                 }
    1648             :         }
    1649             : 
    1650             :         /* add other devices */
    1651           3 :         if (others) {
    1652           0 :                 if (devscan(low) != 0) {
    1653             :                         /* LCOV_EXCL_START */
    1654             :                         log_fatal("Failed to list other devices.\n");
    1655             :                         return -1;
    1656             :                         /* LCOV_EXCL_STOP */
    1657             :                 }
    1658             :         }
    1659             : #else
    1660             :         (void)high;
    1661             :         (void)others;
    1662             : #endif
    1663             : 
    1664           3 :         switch (operation) {
    1665           1 :         case DEVICE_UP : func = thread_spinup; break;
    1666           1 :         case DEVICE_DOWN : func = thread_spindown; break;
    1667           0 :         case DEVICE_SMART : func = thread_smart; break;
    1668           0 :         case DEVICE_PROBE : func = thread_probe; break;
    1669           0 :         case DEVICE_DOWNIFUP : func = thread_spindownifup; break;
    1670             :         }
    1671             : 
    1672           3 :         if (!func)
    1673           1 :                 return 0;
    1674             : 
    1675           2 :         return device_thread(low, func);
    1676             : }
    1677             : 
    1678         273 : void os_init(int opt)
    1679             : {
    1680             : #if HAVE_BLKID
    1681             :         int ret;
    1682         273 :         ret = blkid_get_cache(&cache, NULL);
    1683         273 :         if (ret != 0) {
    1684             :                 /* LCOV_EXCL_START */
    1685             :                 log_fatal("WARNING Failed to get blkid cache\n");
    1686             :                 /* LCOV_EXCL_STOP */
    1687             :         }
    1688             : #endif
    1689             : 
    1690             :         (void)opt;
    1691         273 : }
    1692             : 
    1693         262 : void os_done(void)
    1694             : {
    1695             : #if HAVE_BLKID
    1696         262 :         if (cache != 0)
    1697         262 :                 blkid_put_cache(cache);
    1698             : #endif
    1699         262 : }
    1700             : 
    1701             : /* LCOV_EXCL_START */
    1702             : void os_abort(void)
    1703             : {
    1704             : #if HAVE_BACKTRACE && HAVE_BACKTRACE_SYMBOLS
    1705             :         void* stack[32];
    1706             :         char** messages;
    1707             :         size_t size;
    1708             :         unsigned i;
    1709             : #endif
    1710             : 
    1711             :         printf("Stacktrace of " PACKAGE " v" VERSION);
    1712             : #ifdef _linux
    1713             :         printf(", linux");
    1714             : #endif
    1715             : #ifdef __GNUC__
    1716             :         printf(", gcc " __VERSION__);
    1717             : #endif
    1718             :         printf(", %d-bit", (int)sizeof(void *) * 8);
    1719             :         printf(", PATH_MAX=%d", PATH_MAX);
    1720             :         printf("\n");
    1721             : 
    1722             : #if HAVE_BACKTRACE && HAVE_BACKTRACE_SYMBOLS
    1723             :         size = backtrace(stack, 32);
    1724             : 
    1725             :         messages = backtrace_symbols(stack, size);
    1726             : 
    1727             :         for (i = 1; i < size; ++i) {
    1728             :                 const char* msg;
    1729             : 
    1730             :                 if (messages)
    1731             :                         msg = messages[i];
    1732             :                 else
    1733             :                         msg = "<unknown>";
    1734             : 
    1735             :                 printf("[bt] %02u: %s\n", i, msg);
    1736             : 
    1737             :                 if (messages) {
    1738             :                         int ret;
    1739             :                         char addr2line[1024];
    1740             :                         size_t j = 0;
    1741             :                         while (msg[j] != '(' && msg[j] != ' ' && msg[j] != 0)
    1742             :                                 ++j;
    1743             : 
    1744             :                         snprintf(addr2line, sizeof(addr2line), "addr2line %p -e %.*s", stack[i], (unsigned)j, msg);
    1745             : 
    1746             :                         ret = system(addr2line);
    1747             :                         if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0)
    1748             :                                 printf("exit:%d\n", WEXITSTATUS(ret));
    1749             :                         if (WIFSIGNALED(ret))
    1750             :                                 printf("signal:%d\n", WTERMSIG(ret));
    1751             :                 }
    1752             :         }
    1753             : #endif
    1754             : 
    1755             :         printf("Please report this error to the SnapRAID Forum:\n");
    1756             :         printf("https://sourceforge.net/p/snapraid/discussion/1677233/\n");
    1757             : 
    1758             :         abort();
    1759             : }
    1760             : /* LCOV_EXCL_STOP */
    1761             : 
    1762           0 : void os_clear(void)
    1763             : {
    1764             :         /* ANSI codes */
    1765           0 :         printf("\033[H"); /* cursor at topleft */
    1766           0 :         printf("\033[2J"); /* clear screen */
    1767           0 : }
    1768             : 
    1769           7 : size_t direct_size(void)
    1770             : {
    1771             :         long size;
    1772             : 
    1773           7 :         size = sysconf(_SC_PAGESIZE);
    1774             : 
    1775           7 :         if (size == -1) {
    1776             :                 /* LCOV_EXCL_START */
    1777             :                 log_fatal("No page size\n");
    1778             :                 exit(EXIT_FAILURE);
    1779             :                 /* LCOV_EXCL_STOP */
    1780             :         }
    1781             : 
    1782           7 :         return size;
    1783             : }
    1784             : 
    1785             : #if HAVE_LINUX_DEVICE
    1786             : 
    1787             : /* List of possible ambient temperature labels */
    1788             : const char* AMBIENT_LABEL[] = {
    1789             :         "systin",
    1790             :         "auxtin",
    1791             :         "mb",
    1792             :         "board",
    1793             :         "motherboard",
    1794             :         "system",
    1795             :         "chassis",
    1796             :         "case",
    1797             :         "room",
    1798             :         "ambient",
    1799             :         0
    1800             : };
    1801             : 
    1802           1 : int ambient_temperature(void)
    1803             : {
    1804             :         DIR* dir;
    1805             :         struct dirent* entry;
    1806           1 :         int lowest_temp = 0;
    1807             : 
    1808           1 :         dir = opendir("/sys/class/hwmon");
    1809           1 :         if (!dir)
    1810           0 :                 return 0;
    1811             : 
    1812             :         /* iterate through hwmon devices */
    1813           5 :         while ((entry = readdir(dir)) != NULL) {
    1814             :                 char path[PATH_MAX];
    1815             :                 DIR* hwmon_dir;
    1816             :                 struct dirent* hwmon_entry;
    1817             : 
    1818           4 :                 if (strncmp(entry->d_name, "hwmon", 5) != 0)
    1819           2 :                         continue;
    1820             : 
    1821           2 :                 pathprint(path, sizeof(path), "/sys/class/hwmon/%s", entry->d_name);
    1822             : 
    1823             :                 /* iterate through temp*_input files */
    1824           2 :                 hwmon_dir = opendir(path);
    1825           2 :                 if (!hwmon_dir)
    1826           0 :                         continue;
    1827             : 
    1828          66 :                 while ((hwmon_entry = readdir(hwmon_dir)) != NULL) {
    1829             :                         char value[128];
    1830             :                         char name[128];
    1831             :                         char label[128];
    1832             :                         char* dash;
    1833             :                         int ret;
    1834             :                         char* e;
    1835             :                         int temp;
    1836             : 
    1837          64 :                         if (strncmp(hwmon_entry->d_name, "temp", 4) != 0)
    1838          63 :                                 continue;
    1839             : 
    1840          18 :                         dash = strrchr(hwmon_entry->d_name, '_');
    1841          18 :                         if (dash == 0)
    1842           0 :                                 continue;
    1843             : 
    1844          18 :                         if (strcmp(dash, "_input") != 0)
    1845          14 :                                 continue;
    1846             : 
    1847             :                         /* read the temperature */
    1848           4 :                         pathprint(path, sizeof(path), "/sys/class/hwmon/%s/%s", entry->d_name, hwmon_entry->d_name);
    1849             : 
    1850           4 :                         ret = sysread(path, value, sizeof(value));
    1851           4 :                         if (ret < 0)
    1852           0 :                                 continue;
    1853             : 
    1854           4 :                         temp = strtol(value, &e, 10) / 1000;
    1855           4 :                         if (*e != 0 && !isspace(*e))
    1856           0 :                                 continue;
    1857             : 
    1858             :                         /* cut the file name at "_input" */
    1859           4 :                         *dash = 0;
    1860             : 
    1861             :                         /* read the corresponding name */
    1862           4 :                         pathprint(path, sizeof(path), "/sys/class/hwmon/%s/name", entry->d_name);
    1863           4 :                         ret = sysread(path, name, sizeof(name));
    1864           4 :                         if (ret < 0) {
    1865             :                                 /* fallback to using the hwmon entry */
    1866           0 :                                 pathcpy(name, sizeof(name), entry->d_name);
    1867             :                         }
    1868             : 
    1869             :                         /* read the corresponding label file */
    1870           4 :                         pathprint(path, sizeof(path), "/sys/class/hwmon/%s/%s_label", entry->d_name, hwmon_entry->d_name);
    1871           4 :                         ret = sysread(path, label, sizeof(label));
    1872           4 :                         if (ret < 0) {
    1873             :                                 /* fallback to using the temp* name (e.g., temp1, temp2) */
    1874           0 :                                 pathcpy(label, sizeof(label), hwmon_entry->d_name);
    1875             :                         }
    1876             : 
    1877             :                         /* trim spaces */
    1878           4 :                         strtrim(name);
    1879           4 :                         strtrim(label);
    1880             : 
    1881           4 :                         log_tag("thermal:ambient:device:%s:%s:%s:%s:%d\n", entry->d_name, name, hwmon_entry->d_name, label, temp);
    1882             : 
    1883             :                         /* check if temperature is in reasonable range */
    1884           4 :                         if (temp < 15 || temp > 40)
    1885           3 :                                 continue;
    1886             : 
    1887             :                         /* lower case */
    1888           1 :                         strlwr(label);
    1889             : 
    1890             :                         /* check if label matches possible ambient labels */
    1891           3 :                         for (int i = 0; AMBIENT_LABEL[i]; ++i) {
    1892           3 :                                 if (worddigitstr(label, AMBIENT_LABEL[i]) != 0) {
    1893           1 :                                         log_tag("thermal:ambient:candidate:%d\n", temp);
    1894           1 :                                         if (lowest_temp == 0 || lowest_temp > temp)
    1895           1 :                                                 lowest_temp = temp;
    1896           1 :                                         break;
    1897             :                                 }
    1898             :                         }
    1899             : 
    1900             :                         /* accept also generic "temp1" */
    1901           1 :                         if (strcmp(label, "temp1") == 0) {
    1902           0 :                                 log_tag("thermal:ambient:candidate:%d\n", temp);
    1903           0 :                                 if (lowest_temp == 0 || lowest_temp > temp)
    1904           0 :                                         lowest_temp = temp;
    1905             :                         }
    1906             :                 }
    1907             : 
    1908           2 :                 closedir(hwmon_dir);
    1909             :         }
    1910             : 
    1911           1 :         closedir(dir);
    1912             : 
    1913           1 :         return lowest_temp;
    1914             : }
    1915             : #else
    1916             : int ambient_temperature(void)
    1917             : {
    1918             :         return 0;
    1919             : }
    1920             : #endif
    1921             : 
    1922             : #endif
    1923             : 

Generated by: LCOV version 1.0