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