/* main function */ int main(int argc, char *argv[]) { IDLE_TIME *it; int have_logfile = 0; int min_idle_time; int sleep_time; int opt; /* create default idle-time parameter entry */ if ((it = malloc(sizeof(*it))) == NULL) { fprintf(stderr, "out of memory\n"); exit(1); } it->next = NULL; it->name = NULL; it->idle_time = DEFAULT_IDLE_TIME; it_root = it; /* process command line options */ while ((opt = getopt(argc, argv, "t:a:i:l:dh")) != -1) { switch (opt) { case 't': /* just spin-down the specified disk and exit */ spindown_disk(optarg); return (0); case 'a': /* add a new set of idle-time parameters for this particular disk */ if ((it = malloc(sizeof(*it))) == NULL) { fprintf(stderr, "out of memory\n"); return (2); } it->name = disk_name(optarg); it->idle_time = DEFAULT_IDLE_TIME; it->next = it_root; it_root = it; break; case 'i': /* set idle-time parameters for current (or default) disk */ it->idle_time = atoi(optarg); break; case 'l': logfile = optarg; have_logfile = 1; break; case 'd': debug = 1; break; case 'h': printf("usage: hd-idle [-t <disk>] [-a <name>] [-i <idle_time>] [-l <logfile>] [-d] [-h]\n"); return (0); case ':': fprintf(stderr, "error: option -%c requires an argument\n", optopt); return (1); case '?': fprintf(stderr, "error: unknown option -%c\n", optopt); return (1); } } /* set sleep time to 1/10th of the shortest idle time */ min_idle_time = 1 << 30; for (it = it_root; it != NULL; it = it->next) { if (it->idle_time != 0 && it->idle_time < min_idle_time) { min_idle_time = it->idle_time; } } if ((sleep_time = min_idle_time / 10) == 0) { sleep_time = 1; } /* daemonize unless we're running in debug mode */ if (!debug) { daemonize(); } /* main loop: probe for idle disks and stop them */ for (;;) { DISKSTATS tmp; FILE *fp; char buf[200]; if ((fp = fopen(STAT_FILE, "r")) == NULL) { perror(STAT_FILE); return (2); } memset(&tmp, 0x00, sizeof(tmp)); while (fgets(buf, sizeof(buf), fp) != NULL) { if (sscanf(buf, "%*d %*d %s %*u %*u %u %*u %*u %*u %u %*u %*u %*u %*u", tmp.name, &tmp.reads, &tmp.writes) == 3) { DISKSTATS *ds; time_t now = time(NULL); /* make sure this is a SCSI disk (sd[a-z]) */ if (tmp.name[0] != 's' || tmp.name[1] != 'd' || !isalpha(tmp.name[2]) || tmp.name[3] != '\0') { continue; } dprintf("probing %s: reads: %u, writes: %u\n", tmp.name, tmp.reads, tmp.writes); /* get previous statistics for this disk */ ds = get_diskstats(tmp.name); if (ds == NULL) { /* new disk; just add it to the linked list */ if ((ds = malloc(sizeof(*ds))) == NULL) { fprintf(stderr, "out of memory\n"); return (2); } memcpy(ds, &tmp, sizeof(*ds)); ds->last_io = now; ds->spinup = ds->last_io; ds->next = ds_root; ds_root = ds; /* find idle time for this disk (falling-back to default; default means * 'it->name == NULL' and this entry will always be the last due to the * way this single-linked list is built when parsing command line * arguments) */ for (it = it_root; it != NULL; it = it->next) { if (it->name == NULL || !strcmp(ds->name, it->name)) { ds->idle_time = it->idle_time; break; } } } else if (ds->reads == tmp.reads && ds->writes == tmp.writes) { if (!ds->spun_down) { /* no activity on this disk and still running */ if (ds->idle_time != 0 && now - ds->last_io >= ds->idle_time) { spindown_disk(ds->name); ds->spindown = now; ds->spun_down = 1; } } } else { /* disk had some activity */ if (ds->spun_down) { /* disk was spun down, thus it has just spun up */ if (have_logfile) { log_spinup(ds); } ds->spinup = now; } ds->reads = tmp.reads; ds->writes = tmp.writes; ds->last_io = now; ds->spun_down = 0; } } } fclose(fp); sleep(sleep_time); } return (0); }
int main(int argc, char *argv[]) { IDLE_TIME *it; int min_idle_time; int sleep_time; int opt; if ((it = malloc(sizeof(*it))) == NULL) { fprintf(stderr, "out of memory\n"); exit(1); } it->next = NULL; it->name = NULL; it->idle_time = DEFAULT_IDLE_TIME; it_root = it; while ((opt = getopt(argc, argv, "t:a:i:l:dnhI")) != -1) { switch (opt) { case 't': spindown_disk(optarg); return(0); case 'a': if ((it = malloc(sizeof(*it))) == NULL) { fprintf(stderr, "out of memory\n"); return(2); } it->name = disk_name(optarg); it->idle_time = DEFAULT_IDLE_TIME; it->next = it_root; it_root = it; break; case 'i': it->idle_time = atoi(optarg); break; case 'd': loglevel = LOG_DEBUG; break; case 'n': asdaemon = 0; break; case 'I': loglevel = LOG_DEBUG; usesyslog = 0; break; case 'h': printf("usage: hd-idle [-t <disk>] [-a <name>] [-i <idle_time>] [-d (debug) ]\n" " [-n (no daemon)] [-I (interactive)] [-h]\n"); return(0); case ':': fprintf(stderr, "error: option -%c requires an argument\n", optopt); return(1); case '?': fprintf(stderr, "error: unknown option -%c\n", optopt); return(1); } } /* set sleep time to 1/10th of the shortest idle time */ min_idle_time = 1 << 30; for (it = it_root; it != NULL; it = it->next) { if (it->idle_time != 0 && it->idle_time < min_idle_time) { min_idle_time = it->idle_time; } } if ((sleep_time = min_idle_time / 10) == 0) { sleep_time = 1; } if (asdaemon) daemonize(); if (usesyslog) openlog("hdidle", LOG_PID, LOG_DAEMON); for (;;) { DISKSTATS tmp; char buf[200]; FILE *fp; if ((fp = fopen(STAT_FILE, "r")) == NULL) { perror(STAT_FILE); return(2); } memset(&tmp, 0x00, sizeof(tmp)); while (fgets(buf, sizeof(buf), fp) != NULL) { if (!(sscanf(buf, "%*d %*d %s %*u %*u %u %*u %*u %*u %u %*u %*u %*u %*u", tmp.name, &tmp.reads, &tmp.writes) == 3)) continue; DISKSTATS *ds; time_t now = time(NULL); /* make sure this is a SCSI disk (sd[a-z]) */ if (tmp.name[0] != 's' || tmp.name[1] != 'd' || !isalpha(tmp.name[2]) || tmp.name[3] != '\0') continue; write_log(LOG_DEBUG, "probing %s: reads: %u, writes: %u", tmp.name, tmp.reads, tmp.writes); /* get previous statistics for this disk */ ds = get_diskstats(tmp.name); if (ds == NULL) { /* new disk; just add it to the linked list */ if ((ds = malloc(sizeof(*ds))) == NULL) { fprintf(stderr, "out of memory\n"); return(2); } memcpy(ds, &tmp, sizeof(*ds)); ds->last_io = now; ds->spinup = ds->last_io; ds->next = ds_root; ds_root = ds; /* find idle time for this disk (falling-back to default; default means * 'it->name == NULL' and this entry will always be the last due to the * way this single-linked list is built when parsing command line * arguments) */ for (it = it_root; it != NULL; it = it->next) if (it->name == NULL || !strcmp(ds->name, it->name)) { ds->idle_time = it->idle_time; write_log(LOG_INFO, "disk %s: standby timeout %u seconds", ds->name, ds->idle_time); break; } continue; } /* no activity */ if (ds->reads == tmp.reads && ds->writes == tmp.writes) { if (ds->spun_down) continue; /* no activity on this disk and still running */ if (ds->idle_time != 0 && now - ds->last_io >= ds->idle_time) { spindown_disk(ds->name); ds->spindown = now; ds->spun_down = 1; } continue; } ds->reads = tmp.reads; ds->writes = tmp.writes; ds->last_io = now; ds->spun_down = 0; if (!ds->spun_down) continue; /* disk was spun down, thus it has just spun up */ write_log(LOG_INFO, "disk %s: spinup (running: %ld, stopped: %ld)", ds->name, (long) ds->spindown - (long) ds->spinup, (long) time(NULL) - (long) ds->spindown); ds->spinup = now; } fclose(fp); sleep(sleep_time); } return(0); }