linux/tools/laptop/freefall/freefall.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/* Disk protection for HP/DELL machines.
   3 *
   4 * Copyright 2008 Eric Piel
   5 * Copyright 2009 Pavel Machek <pavel@ucw.cz>
   6 * Copyright 2012 Sonal Santan
   7 * Copyright 2014 Pali Rohár <pali@kernel.org>
   8 */
   9
  10#include <stdio.h>
  11#include <stdlib.h>
  12#include <unistd.h>
  13#include <fcntl.h>
  14#include <sys/stat.h>
  15#include <sys/types.h>
  16#include <string.h>
  17#include <stdint.h>
  18#include <errno.h>
  19#include <signal.h>
  20#include <sys/mman.h>
  21#include <sched.h>
  22#include <syslog.h>
  23
  24static int noled;
  25static char unload_heads_path[64];
  26static char device_path[32];
  27static const char app_name[] = "FREE FALL";
  28
  29static int set_unload_heads_path(char *device)
  30{
  31        if (strlen(device) <= 5 || strncmp(device, "/dev/", 5) != 0)
  32                return -EINVAL;
  33        strncpy(device_path, device, sizeof(device_path) - 1);
  34
  35        snprintf(unload_heads_path, sizeof(unload_heads_path) - 1,
  36                                "/sys/block/%s/device/unload_heads", device+5);
  37        return 0;
  38}
  39
  40static int valid_disk(void)
  41{
  42        int fd = open(unload_heads_path, O_RDONLY);
  43
  44        if (fd < 0) {
  45                perror(unload_heads_path);
  46                return 0;
  47        }
  48
  49        close(fd);
  50        return 1;
  51}
  52
  53static void write_int(char *path, int i)
  54{
  55        char buf[1024];
  56        int fd = open(path, O_RDWR);
  57
  58        if (fd < 0) {
  59                perror("open");
  60                exit(1);
  61        }
  62
  63        sprintf(buf, "%d", i);
  64
  65        if (write(fd, buf, strlen(buf)) != strlen(buf)) {
  66                perror("write");
  67                exit(1);
  68        }
  69
  70        close(fd);
  71}
  72
  73static void set_led(int on)
  74{
  75        if (noled)
  76                return;
  77        write_int("/sys/class/leds/hp::hddprotect/brightness", on);
  78}
  79
  80static void protect(int seconds)
  81{
  82        const char *str = (seconds == 0) ? "Unparked" : "Parked";
  83
  84        write_int(unload_heads_path, seconds*1000);
  85        syslog(LOG_INFO, "%s %s disk head\n", str, device_path);
  86}
  87
  88static int on_ac(void)
  89{
  90        /* /sys/class/power_supply/AC0/online */
  91        return 1;
  92}
  93
  94static int lid_open(void)
  95{
  96        /* /proc/acpi/button/lid/LID/state */
  97        return 1;
  98}
  99
 100static void ignore_me(int signum)
 101{
 102        protect(0);
 103        set_led(0);
 104}
 105
 106int main(int argc, char **argv)
 107{
 108        int fd, ret;
 109        struct stat st;
 110        struct sched_param param;
 111
 112        if (argc == 1)
 113                ret = set_unload_heads_path("/dev/sda");
 114        else if (argc == 2)
 115                ret = set_unload_heads_path(argv[1]);
 116        else
 117                ret = -EINVAL;
 118
 119        if (ret || !valid_disk()) {
 120                fprintf(stderr, "usage: %s <device> (default: /dev/sda)\n",
 121                                argv[0]);
 122                exit(1);
 123        }
 124
 125        fd = open("/dev/freefall", O_RDONLY);
 126        if (fd < 0) {
 127                perror("/dev/freefall");
 128                return EXIT_FAILURE;
 129        }
 130
 131        if (stat("/sys/class/leds/hp::hddprotect/brightness", &st))
 132                noled = 1;
 133
 134        if (daemon(0, 0) != 0) {
 135                perror("daemon");
 136                return EXIT_FAILURE;
 137        }
 138
 139        openlog(app_name, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
 140
 141        param.sched_priority = sched_get_priority_max(SCHED_FIFO);
 142        sched_setscheduler(0, SCHED_FIFO, &param);
 143        mlockall(MCL_CURRENT|MCL_FUTURE);
 144
 145        signal(SIGALRM, ignore_me);
 146
 147        for (;;) {
 148                unsigned char count;
 149
 150                ret = read(fd, &count, sizeof(count));
 151                alarm(0);
 152                if ((ret == -1) && (errno == EINTR)) {
 153                        /* Alarm expired, time to unpark the heads */
 154                        continue;
 155                }
 156
 157                if (ret != sizeof(count)) {
 158                        perror("read");
 159                        break;
 160                }
 161
 162                protect(21);
 163                set_led(1);
 164                if (1 || on_ac() || lid_open())
 165                        alarm(2);
 166                else
 167                        alarm(20);
 168        }
 169
 170        closelog();
 171        close(fd);
 172        return EXIT_SUCCESS;
 173}
 174