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 25 static int noled; 26 static char unload_heads_path[64]; 27 static char device_path[32]; 28 static const char app_name[] = "FREE FALL"; 29 30 static 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 41 static 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 54 static 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 74 static void set_led(int on) 75 { 76 if (noled) 77 return; 78 write_int("/sys/class/leds/hp::hddprotect/brightness", on); 79 } 80 81 static 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 89 static int on_ac(void) 90 { 91 /* /sys/class/power_supply/AC0/online */ 92 return 1; 93 } 94 95 static int lid_open(void) 96 { 97 /* /proc/acpi/button/lid/LID/state */ 98 return 1; 99 } 100 101 static void ignore_me(int signum) 102 { 103 protect(0); 104 set_led(0); 105 } 106 107 int 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