freefall.c 3.1 KB

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