freefall.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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. #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. static int noled;
  24. static char unload_heads_path[64];
  25. static char device_path[32];
  26. static const char app_name[] = "FREE FALL";
  27. static int set_unload_heads_path(char *device)
  28. {
  29. if (strlen(device) <= 5 || strncmp(device, "/dev/", 5) != 0)
  30. return -EINVAL;
  31. strncpy(device_path, device, sizeof(device_path) - 1);
  32. snprintf(unload_heads_path, sizeof(unload_heads_path) - 1,
  33. "/sys/block/%s/device/unload_heads", device+5);
  34. return 0;
  35. }
  36. static int valid_disk(void)
  37. {
  38. int fd = open(unload_heads_path, O_RDONLY);
  39. if (fd < 0) {
  40. perror(unload_heads_path);
  41. return 0;
  42. }
  43. close(fd);
  44. return 1;
  45. }
  46. static void write_int(char *path, int i)
  47. {
  48. char buf[1024];
  49. int fd = open(path, O_RDWR);
  50. if (fd < 0) {
  51. perror("open");
  52. exit(1);
  53. }
  54. sprintf(buf, "%d", i);
  55. if (write(fd, buf, strlen(buf)) != strlen(buf)) {
  56. perror("write");
  57. exit(1);
  58. }
  59. close(fd);
  60. }
  61. static void set_led(int on)
  62. {
  63. if (noled)
  64. return;
  65. write_int("/sys/class/leds/hp::hddprotect/brightness", on);
  66. }
  67. static void protect(int seconds)
  68. {
  69. const char *str = (seconds == 0) ? "Unparked" : "Parked";
  70. write_int(unload_heads_path, seconds*1000);
  71. syslog(LOG_INFO, "%s %s disk head\n", str, device_path);
  72. }
  73. static int on_ac(void)
  74. {
  75. /* /sys/class/power_supply/AC0/online */
  76. return 1;
  77. }
  78. static int lid_open(void)
  79. {
  80. /* /proc/acpi/button/lid/LID/state */
  81. return 1;
  82. }
  83. static void ignore_me(int signum)
  84. {
  85. protect(0);
  86. set_led(0);
  87. }
  88. int main(int argc, char **argv)
  89. {
  90. int fd, ret;
  91. struct stat st;
  92. struct sched_param param;
  93. if (argc == 1)
  94. ret = set_unload_heads_path("/dev/sda");
  95. else if (argc == 2)
  96. ret = set_unload_heads_path(argv[1]);
  97. else
  98. ret = -EINVAL;
  99. if (ret || !valid_disk()) {
  100. fprintf(stderr, "usage: %s <device> (default: /dev/sda)\n",
  101. argv[0]);
  102. exit(1);
  103. }
  104. fd = open("/dev/freefall", O_RDONLY);
  105. if (fd < 0) {
  106. perror("/dev/freefall");
  107. return EXIT_FAILURE;
  108. }
  109. if (stat("/sys/class/leds/hp::hddprotect/brightness", &st))
  110. noled = 1;
  111. if (daemon(0, 0) != 0) {
  112. perror("daemon");
  113. return EXIT_FAILURE;
  114. }
  115. openlog(app_name, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
  116. param.sched_priority = sched_get_priority_max(SCHED_FIFO);
  117. sched_setscheduler(0, SCHED_FIFO, &param);
  118. mlockall(MCL_CURRENT|MCL_FUTURE);
  119. signal(SIGALRM, ignore_me);
  120. for (;;) {
  121. unsigned char count;
  122. ret = read(fd, &count, sizeof(count));
  123. alarm(0);
  124. if ((ret == -1) && (errno == EINTR)) {
  125. /* Alarm expired, time to unpark the heads */
  126. continue;
  127. }
  128. if (ret != sizeof(count)) {
  129. perror("read");
  130. break;
  131. }
  132. protect(21);
  133. set_led(1);
  134. if (1 || on_ac() || lid_open())
  135. alarm(2);
  136. else
  137. alarm(20);
  138. }
  139. closelog();
  140. close(fd);
  141. return EXIT_SUCCESS;
  142. }