usbmodeswitch: Updated to v.1.2.6 from shibby's branch.
[tomato.git] / release / src / router / scsi-idle / scsi-idle.c
blobc08179580a7a8d5e9c531cb59dbbf981fc2096a7
1 /* scsi-idle
2 * Copyright (C) 19?? Christer Weinigel
3 * Copyright (C) 1999 Trent Piepho <xyzzy@speakeasy.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 /* Changelog:
21 * August 25, 2002:
22 * Daniel Sterling (dan@lost-habit.com):
23 added #include <unistd.h>
24 removed unused variables
25 down variable set when hd is down instead of vice versa
26 don't go into daemon mode if timeout is 30 seconds or less
27 warn if timeout is less than 60 seconds.... that's still very low--
28 I recommend timeouts of at least 20 minutes
29 fixed daemon to spin down drive more than just one time (oops!!)
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <signal.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <sys/ioctl.h>
38 #include <sys/stat.h>
39 #include <linux/major.h>
40 #include <linux/kdev_t.h>
41 #include <scsi/scsi_ioctl.h>
43 /* Kernel 2.0 and 2.2 differ on how SCSI_DISK_MAJOR works */
44 #ifdef SCSI_DISK0_MAJOR
45 #define IS_SCSI_DISK(rdev) SCSI_DISK_MAJOR(MAJOR(rdev))
46 #else
47 #define IS_SCSI_DISK(rdev) (MAJOR(rdev)==SCSI_DISK_MAJOR)
48 #endif
50 #ifndef SD_IOCTL_IDLE
51 #define SD_IOCTL_IDLE 4746 /* get idle time */
52 #endif
54 #define DEBUG 0
56 char lockfn[64];
58 void handler()
60 unlink(lockfn);
61 exit(0);
64 int main(int argc, char *argv[])
66 int fd;
67 int down = 0;
68 struct stat statbuf;
70 if (argc < 2 || argc > 3) {
71 fprintf(stderr,
72 "usage: %s device [timeout]\n"
73 " where timeout is the time until motor off in seconds\n"
74 " to idle the disk immediately, use a timeout of zero\n",
75 argv[0]);
76 exit(1);
78 if ((fd = open(argv[1], O_RDWR)) < 0) {
79 perror(argv[1]);
80 exit(1);
82 if ((fstat(fd, &statbuf)) < 0) {
83 perror(argv[1]);
84 close(fd);
85 exit(1);
87 if (!S_ISBLK(statbuf.st_mode)
88 || !IS_SCSI_DISK(statbuf.st_rdev)) {
89 fprintf(stderr, "%s is not a SCSI disk device\n", argv[1]);
90 close(fd);
91 exit(1);
93 if (argc == 2) {
94 long last;
96 /* Report idle time */
98 if ((last = ioctl(fd, SD_IOCTL_IDLE, &last)) < 0) {
99 perror(argv[1]);
100 close(fd);
101 exit(1);
103 if (last == 0)
104 printf("%s has not been accessed yet\n", argv[1]);
105 else
106 printf("%s has been idle for %i second%s\n", argv[1],
107 (int) last, (last == 1 ? "" : "s"));
108 } else {
109 long timeout;
111 if ((timeout = atol(argv[2])) < 0) {
112 fprintf(stderr, "timeout may not be negative\n");
113 close(fd);
114 exit(1);
116 if (timeout <= 30) {
117 if (timeout != 0) fprintf(stderr,
118 "Really low timeouts are a bad, "
119 "bad idea.\nI'll spin down immediately for you. Use a higher "
120 "timeout for daemon mode.");
121 /* Spin down disk immediately */
123 if (ioctl(fd, SCSI_IOCTL_STOP_UNIT) < 0) {
124 perror(argv[1]);
125 close(fd);
126 exit(1);
128 } else {
129 FILE *fp;
130 int dev;
131 long last;
132 long llast = 0;
133 pid_t pid = 0;
135 /* Spin down disk after an idle period */
136 if (timeout < 60)
137 fprintf(stderr,
138 "Warning: timeouts less than 60 seconds are really "
139 "not such a good idea..\n");
141 /* This is ugly, but I guess there is no portable way to do this */
142 dev = ((statbuf.st_rdev >> 4) & 15) + 'a';
144 sprintf(lockfn, "/var/run/scsi-idle.sd%c.pid", dev);
146 if ((fp = fopen(lockfn, "r")) != NULL) {
147 fscanf(fp, "%i", &pid);
148 fclose(fp);
150 kill(pid, SIGTERM);
152 unlink(lockfn);
154 switch (fork()) {
155 case -1:
156 perror("fork failed");
157 close(fd);
158 exit(1);
160 case 0:
161 signal(SIGINT, handler);
162 signal(SIGTERM, handler);
164 if ((fp = fopen(lockfn, "w")) == NULL) {
165 perror(lockfn);
166 close(fd);
167 exit(1);
169 fprintf(fp, "%i\n", (int)getpid());
170 fclose(fp);
172 nice(10);
174 /* main daemon loop.
175 * obtain idle time.
176 * have we shut down the drive already?
177 * IF yes: has the idle time reset since the last check?
178 * IF yes: reset our drive status variable
179 * IF the drive is running AND idle time > user-specified timeout,
180 send STOP_UNIT and set our status variable.
181 * IF idle time < timeout, sleep until idle time would be > timeout.
182 * ELSE sleep for timeout.
184 for (;;) {
185 if ((last = ioctl(fd, SD_IOCTL_IDLE, &last)) < 0) {
186 perror(argv[1]);
187 close(fd);
188 exit(1);
190 #if DEBUG
191 if (last == 0)
192 printf("%s has not been accessed\n", argv[1]);
193 else
194 printf("%s has been idle for %d second%s\n", argv[1],
195 last, (last == 1 ? "" : "s"));
196 #endif
197 if (down == 1 && llast >= last)
198 down = 0;
199 llast = last;
200 if (last >= timeout) {
201 if (down == 0 &&
202 (ioctl(fd, SCSI_IOCTL_STOP_UNIT) != 0)) {
203 perror(argv[1]);
204 close(fd);
205 exit(1);
207 last = 0;
208 down = 1;
210 #if DEBUG
211 printf("sleeping for %d seconds\n", timeout - last);
212 #endif
213 sleep(timeout - last);
215 /* not reached */
217 default:
218 printf("%s: idle daemon started, timeout is %ld seconds\n",
219 argv[1], timeout);
220 /* Ok, done */
221 break;
226 close(fd);
227 exit(0);