|
|
- /***************************************************************************
- * Copyright (C) 05/2011 by Olaf Rempel *
- * razzor@kopf-tisch.de *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, write to the *
- * Free Software Foundation, Inc., *
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
- ***************************************************************************/
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <ctype.h>
- #include <time.h>
-
- #include "configfile.h"
- #include "diskwatch.h"
- #include "event.h"
- #include "lcd.h"
- #include "list.h"
- #include "logging.h"
-
- #include "sgio.h"
- int verbose = 0;
- int prefer_ata12 = 0;
-
- #define F_PRESENT 0x0001
- #define F_STANDBY 0x0002
-
- struct disk_entry {
- struct list_head list;
- const char device[32];
- unsigned int timeout_sec;
-
- int flags;
-
- time_t standby_timeout;
- unsigned int sectors_rd;
- unsigned int sectors_wr;
- };
-
- struct diskwatch {
- struct list_head disk_list;
- struct lcddev *lcd;
- struct event_timeout *check_timeout;
- };
-
- static struct diskwatch diskwatch_glob;
-
- static int diskwatch_check_standby(struct disk_entry *entry);
-
- static int lcdpage_diskwatch(struct lcddev *lcd, int event, void *privdata)
- {
- static struct disk_entry *entry;
- struct diskwatch *dwatch = (struct diskwatch *)privdata;
-
- if (entry == NULL) {
- if (list_empty(&dwatch->disk_list)) {
- return LCDPAGE_COMMAND_NEXT;
- }
-
- entry = list_entry(dwatch->disk_list.next, struct disk_entry, list);
- }
-
- switch (event) {
- case LCDPAGE_EVENT_BUTTON1:
- if (entry->list.next == &dwatch->disk_list) {
- entry = NULL;
- return LCDPAGE_COMMAND_NEXT;
- }
-
- entry = list_entry(entry->list.next, struct disk_entry, list);
- lcd_set_backlight(lcd, 1);
- break;
-
- case LCDPAGE_EVENT_ENTER:
- entry = list_entry(dwatch->disk_list.next, struct disk_entry, list);
- lcd_set_backlight(lcd, 1);
- break;
-
- case LCDPAGE_EVENT_BACKLIGHT:
- case LCDPAGE_EVENT_EXIT:
- return 0;
-
- default:
- break;
- }
-
- char line1[32];
- char line2[32];
-
- snprintf(line1, sizeof(line1), "DISK(%s):", entry->device);
- if (entry->flags & F_PRESENT) {
- diskwatch_check_standby(entry);
-
- if (entry->flags & F_STANDBY) {
- snprintf(line2, sizeof(line2), "STANDBY");
- } else {
- int timeout = entry->standby_timeout - time(NULL);
- int hours = timeout / 3600;
- timeout = timeout % 3600;
- int minutes = timeout / 60;
- timeout = timeout % 60;
-
- snprintf(line2, sizeof(line2), "RUN -%02d:%02d:%02d", hours, minutes, timeout);
- }
-
- } else {
- snprintf(line2, sizeof(line2), "not present");
- }
-
- lcd_setlines(lcd, line1, line2);
-
- return 1000;
- }
-
- static int diskwatch_check_standby(struct disk_entry *entry)
- {
- if (!(entry->flags & F_PRESENT))
- return 0;
-
- int fd = open(entry->device, O_RDONLY | O_NONBLOCK);
- if (fd < 0) {
- log_print(LOG_ERROR, "%s(): failed to open %s", __FUNCTION__, entry->device);
- return -1;
- }
-
- unsigned char args[4] = { ATA_OP_CHECKPOWERMODE1, 0, 0, 0 };
- if (do_drive_cmd(fd, args)) {
- args[0] = ATA_OP_CHECKPOWERMODE2;
- if (do_drive_cmd(fd, args)) {
- log_print(LOG_WARN, "%s: do_drive_cmd(ATA_OP_CHECKPOWERMODEx) failed on %s", __FUNCTION__, entry->device);
- close(fd);
- return -1;
- }
- }
-
- time_t now = time(NULL);
-
- /* args[2]:
- * 0x00 - standby
- * 0x40 - NVcache_spindown
- * 0x41 - NVcache_spinup
- * 0x80 - idle
- * 0xFF - active/idle
- */
- /* drive is in standby */
- if (args[2] == 0x00) {
- entry->flags |= F_STANDBY;
-
- /* drive was in standby, and is now active */
- } else if (entry->flags & F_STANDBY) {
- entry->flags &= ~(F_STANDBY);
- entry->standby_timeout = now + entry->timeout_sec;
-
- /* drive is active, and timeout is up */
- } else if (now >= entry->standby_timeout) {
- unsigned char args[4] = { ATA_OP_STANDBYNOW1, 0, 0, 0};
- if (do_drive_cmd(fd, args)) {
- args[0] = ATA_OP_STANDBYNOW2;
- if (do_drive_cmd(fd, args)) {
- log_print(LOG_WARN, "%s: do_drive_cmd(ATA_OP_STANDBYNOWx) failed on %s", __FUNCTION__, entry->device);
- close(fd);
- return -1;
- }
- }
- entry->flags |= F_STANDBY;
- }
-
- close(fd);
- return 0;
- }
-
- static int diskwatch_check_stats(int timerid, void *privdata)
- {
- struct diskwatch *dwatch = (struct diskwatch *)privdata;
-
- struct disk_entry *entry;
- list_for_each_entry(entry, &dwatch->disk_list, list) {
- entry->flags &= ~(F_PRESENT);
- }
-
- FILE *fp = fopen("/proc/diskstats", "r");
- if (fp == NULL) {
- log_print(LOG_WARN, "%s(): fopen()", __FUNCTION__);
- return -1;
- }
-
- time_t now = time(NULL);
-
- char buffer[256];
- while (fgets(buffer, sizeof(buffer), fp) != NULL) {
- int major, minor;
- char device[32];
- unsigned int val[11];
-
- /* field - description
- * 1 - major number
- * 2 - minor mumber
- * 3 - device name
- * 4 - reads completed successfully
- * 5 - reads merged
- * 6 - sectors read
- * 7 - time spent reading (ms)
- * 8 - writes completed
- * 9 - writes merged
- * 10 - sectors written
- * 11 - time spent writing (ms)
- * 12 - I/Os currently in progress
- * 13 - time spent doing I/Os (ms)
- * 14 - weighted time spent doing I/Os (ms)
- */
- int cnt = sscanf(buffer, "%d %d %s %u %u %u %u %u %u %u %u %u %u %u",
- &major, &minor, device, &val[0],
- &val[1], &val[2], &val[3], &val[4],
- &val[5], &val[6], &val[7], &val[8],
- &val[9], &val[10]);
-
- if (cnt != 14) {
- log_print(LOG_WARN, "%s(): invalid diskstat line: '%s'", __FUNCTION__, buffer);
- continue;
- }
-
- struct disk_entry *entry;
- list_for_each_entry(entry, &dwatch->disk_list, list) {
- char *dev = strrchr(entry->device, '/');
- if (dev == NULL || *(dev +1) == '\0')
- continue;
-
- if (strncmp(dev +1, device, strlen(device)) != 0)
- continue;
-
- entry->flags |= F_PRESENT;
-
- if (entry->sectors_rd == val[2] && entry->sectors_wr == val[6])
- continue;
-
- /* sector counts changed, disk is not in standby */
- entry->flags &= ~(F_STANDBY);
- entry->sectors_rd = val[2];
- entry->sectors_wr = val[6];
- entry->standby_timeout = now + entry->timeout_sec;
- }
- }
-
- fclose(fp);
-
- list_for_each_entry(entry, &dwatch->disk_list, list) {
- diskwatch_check_standby(entry);
- }
-
- return 0;
- }
-
- static int diskwatch_add_disk(struct strtoken *tokens, void *privdata)
- {
- struct diskwatch *dwatch = (struct diskwatch *)privdata;
-
- if (tokens->count != 2) {
- log_print(LOG_WARN, "%s(): invalid config line '%s'", __FUNCTION__, tokens->input);
- return -1;
- }
-
- struct disk_entry *entry = malloc(sizeof(struct disk_entry));
- if (entry == NULL) {
- log_print(LOG_WARN, "%s(): out of memory", __FUNCTION__);
- return -1;
- }
-
- memset(entry, 0x00, sizeof(struct disk_entry));
-
- strncpy((char *)entry->device, tokens->field[0], sizeof(entry->device));
-
- char *tmp;
- entry->timeout_sec = strtol(tokens->field[1], &tmp, 0);
- if (*tmp != '\0' && !isspace(*tmp)) {
- log_print(LOG_WARN, "%s(): invalid timeout value '%s'", __FUNCTION__, tokens->field[1]);
- free(entry);
- return -1;
- }
-
- log_print(LOG_INFO, "diskwatch: watching '%s' (standby timeout: %ds)", entry->device, entry->timeout_sec);
- list_add_tail(&entry->list, &dwatch->disk_list);
- return 0;
- }
-
- void diskwatch_exit(void)
- {
- struct diskwatch *dwatch = &diskwatch_glob;
- struct disk_entry *entry, *tmp;
-
- list_for_each_entry_safe(entry, tmp, &dwatch->disk_list, list) {
- list_del(&entry->list);
- free(entry);
- }
-
- if (dwatch->check_timeout != NULL) {
- event_remove_timeout(dwatch->check_timeout);
- dwatch->check_timeout = NULL;
- }
- }
-
- int diskwatch_init(struct lcddev *lcd)
- {
- struct diskwatch *dwatch = &diskwatch_glob;
-
- INIT_LIST_HEAD(&dwatch->disk_list);
-
- int diskcount = config_get_strtokens("diskwatch", "disk", ",", 2, diskwatch_add_disk, dwatch);
- if (diskcount <= 0)
- return 0;
-
- diskwatch_check_stats(0, dwatch);
-
- int check_interval = 0;
- config_get_int("diskwatch", "check_interval", &check_interval, 60);
- dwatch->check_timeout = event_add_timeout_ms(check_interval * 1000, diskwatch_check_stats, 0, dwatch);
-
- dwatch->lcd = lcd;
- if (dwatch->lcd != NULL) {
- lcd_addpage_cb(dwatch->lcd, 150, lcdpage_diskwatch, dwatch);
- }
-
- return 0;
- }
|