Add some more information to display
[hdapsd.git] / src / hdapsd.c
blobf542e4cdbb0aa361dc042c7f9c50d6ed6c59e467
1 /*
2 * hdapsd.c - Read from the HDAPS (HardDrive Active Protection System)
3 * and protect the drive if motion over threshold...
5 * Derived from pivot.c by Robert Love.
7 * Copyright (C) 2005-2009 Jon Escombe <lists@dresco.co.uk>
8 * Robert Love <rml@novell.com>
9 * Shem Multinymous <multinymous@gmail.com>
10 * Elias Oltmanns <eo@nebensachen.de>
11 * Evgeni Golov <sargentd@die-welt.net>
13 * "Why does that kid keep dropping his laptop?"
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30 #include "config.h"
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <time.h>
37 #include <signal.h>
38 #include <errno.h>
39 #include <ctype.h>
40 #include <sys/utsname.h>
41 #include <sys/mman.h>
42 #include <sys/time.h>
43 #include <getopt.h>
44 #include <linux/input.h>
45 #include <syslog.h>
47 #define PID_FILE "/var/run/hdapsd.pid"
48 #define SYSFS_POSITION_FILE "/sys/devices/platform/hdaps/position"
49 #define MOUSE_ACTIVITY_FILE "/sys/devices/platform/hdaps/keyboard_activity"
50 #define KEYBD_ACTIVITY_FILE "/sys/devices/platform/hdaps/mouse_activity"
51 #define SAMPLING_RATE_FILE "/sys/devices/platform/hdaps/sampling_rate"
52 #define POSITION_INPUTDEV "/dev/input/hdaps/accelerometer-event"
53 #define BUF_LEN 40
55 #define FREEZE_SECONDS 1 /* period to freeze disk */
56 #define REFREEZE_SECONDS 0.1 /* period after which to re-freeze disk */
57 #define FREEZE_EXTRA_SECONDS 4 /* additional timeout for kernel timer */
58 #define DEFAULT_SAMPLING_RATE 50 /* default sampling frequency */
59 #define SIGUSR1_SLEEP_SEC 8 /* how long to sleep upon SIGUSR1 */
60 /* Magic threshold tweak factors, determined experimentally to make a
61 * threshold of 10-20 behave reasonably.
63 #define VELOC_ADJUST 30.0
64 #define ACCEL_ADJUST (VELOC_ADJUST * 60)
65 #define AVG_VELOC_ADJUST 3.0
67 /* History depth for velocity average, in seconds */
68 #define AVG_DEPTH_SEC 0.3
70 /* Parameters for adaptive threshold */
71 #define RECENT_PARK_SEC 3.0 /* How recent is "recently parked"? */
72 #define THRESH_ADAPT_SEC 1.0 /* How often to (potentially) change
73 * the adaptive threshold? */
74 #define THRESH_INCREASE_FACTOR 1.1 /* Increase factor when recently
75 * parked but user is typing */
76 #define THRESH_DECREASE_FACTOR 0.9985 /* Decrease factor when not recently
77 * parked, per THRESH_ADAPT_SEC sec. */
78 #define NEAR_THRESH_FACTOR 0.8 /* Fraction of threshold considered
79 * being near the threshold. */
81 /* Threshold for *continued* parking, as fraction of normal threshold */
82 #define PARKED_THRESH_FACTOR NEAR_THRESH_FACTOR /* >= NEAR_THRESH_FACTOR */
84 static int verbose = 0;
85 static int pause_now = 0;
86 static int dry_run = 0;
87 static int poll_sysfs = 0;
88 static int sampling_rate;
90 char pid_file[BUF_LEN] = "";
91 int hdaps_input_fd = 0;
93 struct list {
94 char name[BUF_LEN];
95 char protect_file[BUF_LEN];
96 struct list *next;
99 struct list *disklist = NULL;
102 * slurp_file - read the content of a file (up to BUF_LEN-1) into a string.
104 * We open and close the file on every invocation, which is lame but due to
105 * several features of sysfs files:
107 * (a) Sysfs files are seekable.
108 * (b) Seeking to zero and then rereading does not seem to work.
110 * If I were king--and I will be one day--I would have made sysfs files
111 * nonseekable and only able to return full-size reads.
113 static int slurp_file(const char* filename, char* buf)
115 int ret;
116 int fd = open (filename, O_RDONLY);
117 if (fd < 0) {
118 fprintf(stderr, "open(%s): %s\n", filename, strerror(errno));
119 return fd;
122 ret = read (fd, buf, BUF_LEN-1);
123 if (ret < 0) {
124 fprintf(stderr, "read(%s): %s\n", filename, strerror(errno));
125 } else {
126 buf[ret] = 0; /* null-terminate so we can parse safely */
127 ret = 0;
130 if (close (fd))
131 fprintf(stderr, "close(%s): %s\n", filename, strerror(errno));
133 return ret;
137 * read_position_from_sysfs() - read the (x,y) position pair from hdaps via sysfs files
138 * This method is not recommended for frequent polling, since it causes unnecessary interrupts
139 * and a phase difference between hdaps-to-EC polling vs. hdapsd-to-hdaps polling.
141 static int read_position_from_sysfs (int *x, int *y)
143 char buf[BUF_LEN];
144 int ret;
145 if ((ret = slurp_file(SYSFS_POSITION_FILE, buf)))
146 return ret;
147 return (sscanf (buf, "(%d,%d)\n", x, y) != 2);
151 * read_int() - read an integer from a file
153 static int read_int (const char* filename)
155 char buf[BUF_LEN];
156 int ret;
157 if ((ret = slurp_file(filename, buf)))
158 return ret;
159 if (sscanf (buf, "%d\n", &ret) != 1)
160 return -EIO;
161 return ret;
165 * get_km_activity() - returns 1 if there is keyboard or mouse activity
167 static int get_km_activity()
169 if (read_int(MOUSE_ACTIVITY_FILE)==1)
170 return 1;
171 if (read_int(KEYBD_ACTIVITY_FILE)==1)
172 return 1;
173 return 0;
178 * read_position_from_inputdev() - read the (x,y) position pair and time from hdaps
179 * via the hdaps input device. Blocks there is a change in position.
180 * The x and y arguments should contain the last read values, since if one of them
181 * doesn't change it will not be assigned.
183 static int read_position_from_inputdev (int *x, int *y, double *utime)
185 struct input_event ev;
186 int len, done = 0;
187 *utime = 0;
188 while (1) {
189 len = read(hdaps_input_fd, &ev, sizeof(struct input_event));
190 if (len < 0) {
191 fprintf(stderr, "ERROR: failed reading %s (%s).\n", POSITION_INPUTDEV, strerror(errno));
192 return len;
194 if (len < (int)sizeof(struct input_event)) {
195 fprintf(stderr, "ERROR: short read from %s (%d bytes).\n", POSITION_INPUTDEV, len);
196 return -EIO;
198 switch (ev.type) {
199 case EV_ABS: /* new X or Y */
200 switch (ev.code) {
201 case ABS_X:
202 *x = ev.value;
203 break;
204 case ABS_Y:
205 *y = ev.value;
206 break;
207 default:
208 continue;
210 break;
211 case EV_SYN: /* X and Y now reflect latest measurement */
212 done = 1;
213 break;
214 default:
215 continue;
217 if (!*utime) /* first event's time is closest to reality */
218 *utime = ev.time.tv_sec + ev.time.tv_usec/1000000.0;
219 if (done)
220 return 0;
226 * write_protect() - park/unpark
228 static int write_protect (const char *path, int val)
230 int fd, ret;
231 char buf[BUF_LEN];
233 if (dry_run)
234 return 0;
236 snprintf(buf, BUF_LEN, "%d", val);
238 fd = open (path, O_WRONLY);
239 if (fd < 0) {
240 perror ("open");
241 return fd;
244 ret = write (fd, buf, strlen(buf));
246 if (ret < 0) {
247 perror ("write");
248 goto out;
250 ret = 0;
252 out:
253 if (close (fd))
254 perror ("close");
256 return ret;
259 double get_utime (void)
261 struct timeval tv;
262 int ret = gettimeofday(&tv, NULL);
263 if (ret) {
264 perror("gettimeofday");
265 exit(1);
267 return tv.tv_sec + tv.tv_usec/1000000.0;
270 /* Handler for SIGUSR1, sleeps for a few seconds. Useful when suspending laptop. */
271 void SIGUSR1_handler(int sig)
273 signal(SIGUSR1, SIGUSR1_handler);
274 pause_now=1;
277 /* Handler for SIGTERM, deletes the pidfile and exits. */
278 void SIGTERM_handler(int sig)
280 signal(SIGTERM, SIGTERM_handler);
281 unlink(pid_file);
282 exit(0);
286 * version() - display version information and exit
288 void version()
290 printf(PACKAGE_STRING"\n");
291 exit(0);
295 * usage() - display usage instructions and exit
297 void usage()
299 printf("Usage: "PACKAGE_NAME" [OPTIONS]\n");
300 printf("\n");
301 printf("Required options:\n");
302 printf(" -d --device=<device> <device> is likely to be hda or sda.\n");
303 printf(" Can be given multiple times\n");
304 printf(" to protect multiple devices.\n");
305 printf(" -s --sensitivity=<sensitivity> A suggested starting <sensitivity> is 15.\n");
306 printf("\n");
307 printf("Additional options:\n");
308 printf(" -a --adaptive Adaptive threshold (automatic\n");
309 printf(" increase when the built-in\n");
310 printf(" keyboard/mouse are used).\n");
311 printf(" -v --verbose Get verbose statistics.\n");
312 printf(" -b --background Run the process in the background.\n");
313 printf(" -p --pidfile[=<pidfile>] Create a pid file when running\n");
314 printf(" in background.\n");
315 printf(" If <pidfile> is not specified,\n");
316 printf(" it's set to %s.\n", PID_FILE);
317 printf(" -t --dry-run Don't actually park the drive.\n");
318 printf(" -y --poll-sysfs Force use of sysfs interface to accelerometer.\n");
319 printf(" -V --version Display version information and exit.\n");
320 printf(" -h --help Display this message and exit.\n");
321 printf("\n");
322 printf("You can send SIGUSR1 to deactivate "PACKAGE_NAME" for %d seconds.\n",
323 SIGUSR1_SLEEP_SEC);
324 printf("\n");
325 printf("Send bugs, coments and suggestions to "PACKAGE_BUGREPORT"\n");
326 exit(1);
330 * check_thresh() - compare a value to the threshold
332 void check_thresh(double val_sqr, double thresh, int* above, int* near,
333 char* reason_out, char reason_mark)
335 if (val_sqr > thresh*thresh*NEAR_THRESH_FACTOR*NEAR_THRESH_FACTOR) {
336 *near = 1;
337 *reason_out = tolower(reason_mark);
339 if (val_sqr > thresh*thresh) {
340 *above = 1;
341 *reason_out = toupper(reason_mark);
346 * analyze() - make a decision on whether to park given present readouts
347 * (remembers some past data in local static variables).
348 * Computes and checks 3 values:
349 * velocity: current position - prev position / time delta
350 * acceleration: current velocity - prev velocity / time delta
351 * average velocity: exponentially decaying average of velocity,
352 * weighed by time delta.
353 * The velocity and acceleration tests respond quickly to short sharp shocks,
354 * while the average velocity test catches long, smooth movements (and
355 * averages out measurement noise).
356 * The adaptive threshold, if enabled, increases when (built-in) keyboard or
357 * mouse activity happens shortly after some value was above or near the
358 * adaptive threshold. The adaptive threshold slowly decreases back to the
359 * base threshold when no value approaches it.
362 int analyze(int x, int y, double unow, double base_threshold,
363 int adaptive, int parked)
365 static int x_last = 0, y_last = 0;
366 static double unow_last = 0, x_veloc_last = 0, y_veloc_last = 0;
367 static double x_avg_veloc = 0, y_avg_veloc = 0;
368 static int history = 0; /* how many recent valid samples? */
369 static double adaptive_threshold = -1; /* current adaptive thresh */
370 static int last_thresh_change = 0; /* last adaptive thresh change */
371 static int last_near_thresh = 0; /* last time we were near thresh */
372 static int last_km_activity; /* last time kbd/mouse activity seen */
374 double udelta, x_delta, y_delta, x_veloc, y_veloc, x_accel, y_accel;
375 double veloc_sqr, accel_sqr, avg_veloc_sqr;
376 double exp_weight;
377 double threshold; /* transient threshold for this iteration */
378 char reason[4]; /* "which threshold reached?" string for verbose */
379 int recently_near_thresh;
380 int above=0, near=0; /* above threshold, near threshold */
382 /* Adaptive threshold adjustment */
383 if (adaptive_threshold<0) /* first invocation */
384 adaptive_threshold = base_threshold;
385 recently_near_thresh = unow < last_near_thresh + RECENT_PARK_SEC;
386 if (adaptive && recently_near_thresh && get_km_activity())
387 last_km_activity = unow;
388 if (adaptive && unow > last_thresh_change + THRESH_ADAPT_SEC) {
389 if (recently_near_thresh) {
390 if (last_km_activity > last_near_thresh &&
391 last_km_activity > last_thresh_change) {
392 /* Near threshold and k/m activity */
393 adaptive_threshold *= THRESH_INCREASE_FACTOR;
394 last_thresh_change = unow;
396 } else {
397 /* Recently never near threshold */
398 adaptive_threshold *= THRESH_DECREASE_FACTOR;
399 if (adaptive_threshold < base_threshold)
400 adaptive_threshold = base_threshold;
401 last_thresh_change = unow;
405 /* compute deltas */
406 udelta = unow - unow_last;
407 x_delta = x - x_last;
408 y_delta = y - y_last;
410 /* compute velocity */
411 x_veloc = x_delta/udelta;
412 y_veloc = y_delta/udelta;
413 veloc_sqr = x_veloc*x_veloc + y_veloc*y_veloc;
415 /* compute acceleration */
416 x_accel = (x_veloc - x_veloc_last)/udelta;
417 y_accel = (y_veloc - y_veloc_last)/udelta;
418 accel_sqr = x_accel*x_accel + y_accel*y_accel;
420 /* compute exponentially-decaying velocity average */
421 exp_weight = udelta/AVG_DEPTH_SEC; /* weight of this sample */
422 exp_weight = 1 - 1.0/(1+exp_weight); /* softly clamped to 1 */
423 x_avg_veloc = exp_weight*x_veloc + (1-exp_weight)*x_avg_veloc;
424 y_avg_veloc = exp_weight*y_veloc + (1-exp_weight)*y_avg_veloc;
425 avg_veloc_sqr = x_avg_veloc*x_avg_veloc + y_avg_veloc*y_avg_veloc;
427 threshold = adaptive_threshold;
428 if (parked) /* when parked, be reluctant to unpark */
429 threshold *= PARKED_THRESH_FACTOR;
431 /* Threshold test (uses Pythagoras's theorem) */
432 strcpy(reason, " ");
434 check_thresh(veloc_sqr, threshold*VELOC_ADJUST,
435 &above, &near, reason+0, 'V');
436 check_thresh(accel_sqr, threshold*ACCEL_ADJUST,
437 &above, &near, reason+1, 'A');
438 check_thresh(avg_veloc_sqr, threshold*AVG_VELOC_ADJUST,
439 &above, &near, reason+2, 'X');
441 if (verbose) {
442 printf("dt=%5.3f "
443 "dpos=(%3g,%3g) "
444 "vel=(%6.1f,%6.1f)*%g "
445 "acc=(%6.1f,%6.1f)*%g "
446 "avg_vel=(%6.1f,%6.1f)*%g "
447 "thr=%.1f "
448 "%s\n",
449 udelta,
450 x_delta, y_delta,
451 x_veloc/VELOC_ADJUST,
452 y_veloc/VELOC_ADJUST,
453 VELOC_ADJUST*1.0,
454 x_accel/ACCEL_ADJUST,
455 y_accel/ACCEL_ADJUST,
456 ACCEL_ADJUST*1.0,
457 x_avg_veloc/AVG_VELOC_ADJUST,
458 y_avg_veloc/AVG_VELOC_ADJUST,
459 AVG_VELOC_ADJUST*1.0,
460 threshold,
461 reason);
464 if (udelta>1.0) { /* Too much time since last (resume from suspend?) */
465 history = 0;
466 x_avg_veloc = y_avg_veloc = 0;
469 if (history<2) { /* Not enough data for meaningful result */
470 above = 0;
471 near = 0;
472 ++history;
475 if (near)
476 last_near_thresh = unow;
478 x_last = x;
479 y_last = y;
480 x_veloc_last = x_veloc;
481 y_veloc_last = y_veloc;
482 unow_last = unow;
484 return above;
488 * add_disk (disk) - add the given disk to the global disklist
490 void add_disk (char* disk) {
491 struct utsname sysinfo;
492 char protect_file[BUF_LEN] = "";
493 if (uname(&sysinfo) < 0 || strcmp("2.6.27", sysinfo.release) <= 0)
494 snprintf(protect_file, BUF_LEN, "/sys/block/%s/device/unload_heads", disk);
495 else
496 snprintf(protect_file, BUF_LEN, "/sys/block/%s/queue/protect", disk);
498 if (disklist == NULL) {
499 disklist = (struct list *)malloc(sizeof(struct list));
500 if (disklist == NULL) {
501 printf("Error allocating memory\n");
502 exit(EXIT_FAILURE);
504 else {
505 strncpy(disklist->name,disk,BUF_LEN);
506 strncpy(disklist->protect_file,protect_file,BUF_LEN);
507 disklist->next = NULL;
510 else {
511 struct list *p = disklist;
512 while (p->next != NULL)
513 p = p->next;
514 p->next = (struct list *)malloc(sizeof(struct list));
515 if (p->next == NULL) {
516 printf("Error allocating memory\n");
517 exit(EXIT_FAILURE);
519 else {
520 strncpy(p->next->name,disk,BUF_LEN);
521 strncpy(p->next->protect_file,protect_file,BUF_LEN);
522 p->next->next = NULL;
528 * free_disk (disk) - free the allocated memory
530 void free_disk (struct list *disk) {
531 if (disk != NULL) {
532 if (disk->next != NULL)
533 free_disk(disk->next);
534 free(disk);
539 * printlog (msg) - either print the message to stdout
540 * or post it to the syslog
543 void printlog (char *msg, int background)
545 time_t now;
547 if (background)
548 syslog(LOG_INFO, msg);
549 else {
550 now = time((time_t *)NULL);
551 printf("%.24s: %s\n", ctime(&now), msg);
556 * main() - loop forever, reading the hdaps values and
557 * parking/unparking as necessary
559 int main (int argc, char** argv)
561 struct utsname sysinfo;
562 int c, park_now, protect_factor;
563 int x=0, y=0;
564 int fd, i, ret, threshold = 0, background = 0, adaptive=0,
565 pidfile = 0, parked = 0;
566 double unow = 0, parked_utime = 0;
567 time_t now;
569 if (uname(&sysinfo) < 0 || strcmp("2.6.27", sysinfo.release) <= 0)
570 protect_factor = 1000;
571 else
572 protect_factor = 1;
574 struct option longopts[] =
576 {"device", required_argument, NULL, 'd'},
577 {"sensitivity", required_argument, NULL, 's'},
578 {"adaptive", no_argument, NULL, 'a'},
579 {"verbose", no_argument, NULL, 'v'},
580 {"background", no_argument, NULL, 'b'},
581 {"pidfile", optional_argument, NULL, 'p'},
582 {"dry-run", no_argument, NULL, 't'},
583 {"poll-sysfs", no_argument, NULL, 'y'},
584 {"version", no_argument, NULL, 'V'},
585 {"help", no_argument, NULL, 'h'},
586 {NULL, 0, NULL, 0}
589 openlog(PACKAGE_NAME, LOG_PID, LOG_DAEMON);
591 while ((c = getopt_long(argc, argv, "d:s:vbap::tyVh", longopts, NULL)) != -1) {
592 switch (c) {
593 case 'd':
594 add_disk(optarg);
595 break;
596 case 's':
597 threshold = atoi(optarg);
598 break;
599 case 'b':
600 background = 1;
601 break;
602 case 'a':
603 adaptive = 1;
604 break;
605 case 'v':
606 verbose = 1;
607 break;
608 case 'p':
609 pidfile = 1;
610 if (optarg == NULL) {
611 snprintf(pid_file, BUF_LEN, "%s", PID_FILE);
612 } else {
613 snprintf(pid_file, BUF_LEN, "%s", optarg);
615 break;
616 case 't':
617 printlog("Dry run, will not actually park heads or freeze queue.", background);
618 dry_run = 1;
619 break;
620 case 'y':
621 poll_sysfs = 1;
622 break;
623 case 'V':
624 version();
625 break;
626 case 'h':
627 default:
628 usage();
629 break;
633 if (!threshold || disklist == NULL)
634 usage(argv);
636 if (!poll_sysfs) {
637 hdaps_input_fd = open(POSITION_INPUTDEV, O_RDONLY);
638 if (hdaps_input_fd<0) {
639 if (!background)
640 fprintf(stderr,
641 "WARNING: Cannot open hdaps position input file %s (%s). "
642 "You may be using an incompatible version of the hdaps module, "
643 "or missing the required udev rule. "
644 "Falling back to reading the position from sysfs (uses more power). "
645 "Use '-y' to silence this warning.\n",
646 POSITION_INPUTDEV, strerror(errno));
647 poll_sysfs = 1;
651 if (background) {
652 verbose = 0;
653 if (pidfile) {
654 fd = open (pid_file, O_WRONLY | O_CREAT, 0644);
655 if (fd < 0) {
656 perror ("open(pid_file)");
657 return 1;
660 daemon(0,0);
661 if (pidfile) {
662 char buf[BUF_LEN];
663 snprintf (buf, BUF_LEN, "%d\n", getpid());
664 ret = write (fd, buf, strlen(buf));
665 if (ret < 0) {
666 perror ("write(pid_file)");
667 return 1;
669 if (close (fd)) {
670 perror ("close(pid_file)");
671 return 1;
676 mlockall(MCL_FUTURE);
678 if (verbose) {
679 struct list *p = disklist;
680 while (p != NULL) {
681 printf("disk: %s\n", p->name);
682 p = p->next;
684 printf("threshold: %i\n", threshold);
685 printf("read_method: %s\n", poll_sysfs ? "poll-sysfs" : "input-dev");
688 printlog("Starting "PACKAGE_NAME, background);
690 /* check the protect attribute exists */
691 /* wait for it if it's not there (in case the attribute hasn't been created yet) */
692 struct list *p = disklist;
693 while (p != NULL) {
694 fd = open (p->protect_file, O_RDWR);
695 if (background)
696 for (i=0; fd < 0 && i < 100; ++i) {
697 usleep (100000); /* 10 Hz */
698 fd = open (p->protect_file, O_RDWR);
700 if (fd < 0) {
701 char errmsg[BUF_LEN];
702 snprintf(errmsg, BUF_LEN, "Could not open %s", p->protect_file);
703 printlog (errmsg, background);
704 free_disk(disklist);
705 return 1;
707 close (fd);
708 p = p->next;
711 /* see if we can read the sensor */
712 /* wait for it if it's not there (in case the attribute hasn't been created yet) */
713 ret = read_position_from_sysfs (&x, &y);
714 if (background)
715 for (i=0; ret && i < 100; ++i) {
716 usleep (100000); /* 10 Hz */
717 ret = read_position_from_sysfs (&x, &y);
719 if (ret)
720 return 1;
722 /* adapt to the driver's sampling rate */
723 sampling_rate = read_int(SAMPLING_RATE_FILE);
724 if (sampling_rate <= 0)
725 sampling_rate = DEFAULT_SAMPLING_RATE;;
726 if (verbose)
727 printf("sampling_rate: %d\n", sampling_rate);
729 signal(SIGUSR1, SIGUSR1_handler);
731 if (background && pidfile) {
732 signal(SIGTERM, SIGTERM_handler);
735 while (1) {
736 if (poll_sysfs) {
737 usleep (1000000/sampling_rate);
738 ret = read_position_from_sysfs (&x, &y);
739 unow = get_utime(); /* microsec */
740 } else {
741 double oldunow = unow;
742 int oldx = x, oldy = y;
743 ret = read_position_from_inputdev (&x, &y, &unow);
745 /* The input device issues events only when the position changed.
746 * The analysis state needs to know how long the position remained
747 * unchanged, so send analyze() a fake retroactive update before sending
748 * the new one. */
749 if (!ret && oldunow && unow-oldunow > 1.5/sampling_rate)
750 analyze(oldx, oldy, unow-1.0/sampling_rate, threshold, adaptive, parked);
754 if (ret) {
755 if (verbose)
756 printf("readout error (%d)\n", ret);
757 continue;
760 now = time((time_t *)NULL); /* sec */
762 park_now = analyze(x, y, unow, threshold, adaptive, parked);
764 if (park_now && !pause_now) {
765 if (!parked || unow>parked_utime+REFREEZE_SECONDS) {
766 /* Not frozen or freeze about to expire */
767 struct list *p = disklist;
768 while (p != NULL) {
769 write_protect(p->protect_file,
770 (FREEZE_SECONDS+FREEZE_EXTRA_SECONDS) * protect_factor);
771 p = p->next;
773 /* Write protect before any output (xterm, or
774 * whatever else is handling our stdout, may be
775 * swapped out).
777 if (!parked)
778 printlog("parking", background);
779 parked = 1;
780 parked_utime = unow;
782 } else {
783 if (parked &&
784 (pause_now || unow>parked_utime+FREEZE_SECONDS)) {
785 /* Sanity check */
786 struct list *p = disklist;
787 while (p != NULL) {
788 if (!dry_run && !read_int(p->protect_file))
789 printlog("Error! Not parked when we "
790 "thought we were... (paged out "
791 "and timer expired?)", background);
792 /* Freeze has expired */
793 write_protect(p->protect_file, 0); /* unprotect */
794 p = p->next;
796 parked = 0;
797 printlog("un-parking", background);
799 while (pause_now) {
800 pause_now=0;
801 char pause_msg[BUF_LEN];
802 snprintf(pause_msg, BUF_LEN, "pausing for %d seconds", SIGUSR1_SLEEP_SEC);
803 printlog(pause_msg, background);
804 sleep(SIGUSR1_SLEEP_SEC);
810 free_disk(disklist);
811 printlog("Terminating "PACKAGE_NAME, background);
812 closelog();
813 munlockall();
814 return ret;