busybox: update config for new options in 1.20
[opendreambox.git] / meta-opendreambox / recipes-core / sysvinit / sysvinit / 92_sata-hddown.dpatch
1 #! /bin/sh /usr/share/dpatch/dpatch-run
2 ## 92_sata-hddown.dpatch by Werner Fink and SuSe
3 ##
4 ## All lines beginning with `## DP:' are a description of the patch.
5 ## DP: Make sure to shut down SATA disks properly, and handle each
6 ## DP: disk individually.  See also http://linux-ata.org/shutdown.html
7
8 @DPATCH@
9 diff -urNad trunk~/src/hddown.c trunk/src/hddown.c
10 --- trunk~/src/hddown.c 2008-03-26 09:32:51.000000000 +0100
11 +++ trunk/src/hddown.c  2008-03-26 09:32:51.000000000 +0100
12 @@ -5,6 +5,9 @@
13   */
14  char *v_hddown = "@(#)hddown.c  1.02  22-Apr-2003  miquels@cistron.nl";
15  
16 +#ifndef _GNU_SOURCE
17 +#define _GNU_SOURCE
18 +#endif
19  #include <stdio.h>
20  #include <stdlib.h>
21  #include <unistd.h>
22 @@ -18,6 +21,326 @@
23  #include <sys/ioctl.h>
24  #include <linux/hdreg.h>
25  
26 +#define USE_SYSFS
27 +#ifdef USE_SYSFS
28 +/*
29 + * sysfs part  Find all disks on the system, list out IDE and unmanaged
30 + *             SATA disks, flush the cache of those and shut them down.
31 + * Author:     Werner Fink <werner@suse.de>, 2007/06/12
32 + *
33 + */
34 +#include <limits.h>
35 +#include <errno.h>
36 +#include <sys/stat.h>
37 +#include <sys/types.h>
38 +#ifdef WORDS_BIGENDIAN
39 +#include <byteswap.h>
40 +#endif
41 +
42 +#define SYS_BLK                "/sys/block"
43 +#define SYS_CLASS      "/sys/class/scsi_disk"
44 +#define DEV_BASE       "/dev"
45 +#define ISSPACE(c)     (((c)==' ')||((c)=='\n')||((c)=='\t')||((c)=='\v')||((c)=='\r')||((c)=='\f'))
46 +
47 +/* Used in flush_cache_ext(), compare with <linux/hdreg.h> */
48 +#define IDBYTES                512
49 +#define MASK_EXT       0xE000          /* Bit 15 shall be zero, bit 14 shall be one, bit 13 flush cache ext */
50 +#define TEST_EXT       0x6000
51 +
52 +/* Maybe set in list_disks() and used in do_standby_idedisk() */
53 +#define DISK_IS_IDE    0x00000001
54 +#define DISK_IS_SATA   0x00000002
55 +#define DISK_EXTFLUSH  0x00000004
56 +
57 +static char *strstrip(char *str);
58 +static FILE *hdopen(const char* const format, const char* const name);
59 +static int flush_cache_ext(const char *device);
60 +
61 +/*
62 + *     Find all disks through /sys/block.
63 + */
64 +static char *list_disks(DIR* blk, unsigned int* flags)
65 +{
66 +       struct dirent *d;
67 +
68 +       while ((d = readdir(blk))) {
69 +               *flags = 0;
70 +               if (d->d_name[1] == 'd' && (d->d_name[0] == 'h' || d->d_name[0] == 's')) {
71 +                       char buf[NAME_MAX+1], lnk[NAME_MAX+1], *ptr;
72 +                       struct stat st;
73 +                       FILE *fp;
74 +                       int ret;
75 +
76 +                       fp = hdopen(SYS_BLK "/%s/removable", d->d_name);
77 +                       if ((long)fp <= 0) {
78 +                               if ((long)fp < 0)
79 +                                       goto empty;     /* error */
80 +                               continue;               /* no entry `removable' */
81 +                       }
82 +
83 +                       ret = getc(fp);
84 +                       fclose(fp);
85 +
86 +                       if (ret != '0')
87 +                               continue;               /* not a hard disk */
88 +
89 +                       if (d->d_name[0] == 'h') {
90 +                               (*flags) |= DISK_IS_IDE;
91 +                               if ((ret = flush_cache_ext(d->d_name))) {
92 +                                       if (ret < 0)
93 +                                               goto empty;
94 +                                       (*flags) |= DISK_EXTFLUSH;
95 +                               }
96 +                               break;                  /* old IDE disk not managed by kernel, out here */
97 +                       }
98 +
99 +                       ret = snprintf(buf, sizeof(buf), SYS_BLK "/%s/device", d->d_name);
100 +                       if ((ret >= sizeof(buf)) || (ret < 0))
101 +                               goto empty;             /* error */
102 +
103 +                       ret = readlink(buf, lnk, sizeof(lnk));
104 +                       if (ret >= sizeof(lnk))
105 +                               goto empty;             /* error */
106 +                       if (ret < 0) {
107 +                               if (errno != ENOENT)
108 +                                       goto empty;     /* error */
109 +                               continue;               /* no entry `device' */
110 +                       }
111 +                       lnk[ret] = '\0';
112 +
113 +                       ptr = basename(lnk);
114 +                       if (!ptr || !*ptr)
115 +                               continue;               /* should not happen */
116 +
117 +                       ret = snprintf(buf, sizeof(buf), SYS_CLASS "/%s/manage_start_stop", ptr);
118 +                       if ((ret >= sizeof(buf)) || (ret < 0))
119 +                               goto empty;             /* error */
120 +
121 +                       ret = stat(buf, &st);
122 +                       if (ret == 0)
123 +                               continue;               /* disk found but managed by kernel */
124 +
125 +                       if (errno != ENOENT)
126 +                               goto empty;             /* error */
127 +
128 +                       fp = hdopen(SYS_BLK "/%s/device/vendor", d->d_name);
129 +                       if ((long)fp <= 0) {
130 +                               if ((long)fp < 0)
131 +                                       goto empty;     /* error */
132 +                               continue;               /* no entry `device/vendor' */
133 +                       }
134 +
135 +                       ptr = fgets(buf, sizeof(buf), fp);
136 +                       fclose(fp);
137 +                       if (ptr == (char*)0)
138 +                               continue;               /* should not happen */
139 +
140 +                       ptr = strstrip(buf);
141 +                       if (*ptr == '\0')
142 +                               continue;               /* should not happen */
143 +
144 +                       if (strncmp(buf, "ATA", sizeof(buf)))
145 +                               continue;               /* no SATA but a real SCSI disk */
146 +
147 +                       (*flags) |= (DISK_IS_IDE|DISK_IS_SATA);
148 +                       if ((ret = flush_cache_ext(d->d_name))) {
149 +                               if (ret < 0)
150 +                                       goto empty;
151 +                               (*flags) |= DISK_EXTFLUSH;
152 +                       }
153 +                       break;                          /* new SATA disk to shutdown, out here */
154 +               }
155 +       }
156 +       if (d == (struct dirent*)0)
157 +               goto empty;
158 +       return d->d_name;
159 +empty:
160 +       return (char*)0;
161 +}
162 +
163 +/*
164 + *     Put an disk in standby mode.
165 + *     Code stolen from hdparm.c
166 + */
167 +static int do_standby_idedisk(char *device, unsigned int flags)
168 +{
169 +#ifndef WIN_STANDBYNOW1
170 +#define WIN_STANDBYNOW1                0xE0
171 +#endif
172 +#ifndef WIN_STANDBYNOW2
173 +#define WIN_STANDBYNOW2                0x94
174 +#endif
175 +#ifndef WIN_FLUSH_CACHE_EXT
176 +#define WIN_FLUSH_CACHE_EXT    0xEA
177 +#endif
178 +#ifndef WIN_FLUSH_CACHE
179 +#define WIN_FLUSH_CACHE                0xE7
180 +#endif
181 +       unsigned char flush1[4] = {WIN_FLUSH_CACHE_EXT,0,0,0};
182 +       unsigned char flush2[4] = {WIN_FLUSH_CACHE,0,0,0};
183 +       unsigned char stdby1[4] = {WIN_STANDBYNOW1,0,0,0};
184 +       unsigned char stdby2[4] = {WIN_STANDBYNOW2,0,0,0};
185 +       char buf[NAME_MAX+1];
186 +       int fd, ret;
187 +
188 +       ret = snprintf(buf, sizeof(buf), DEV_BASE "/%s", device);
189 +       if ((ret >= sizeof(buf)) || (ret < 0))
190 +               return -1;
191 +
192 +       if ((fd = open(buf, O_RDWR)) < 0)
193 +               return -1;
194 +
195 +       switch (flags & DISK_EXTFLUSH) {
196 +       case DISK_EXTFLUSH:
197 +               if (ioctl(fd, HDIO_DRIVE_CMD, &flush1) == 0)
198 +                       break;
199 +               /* Extend flush rejected, try standard flush */
200 +       default:
201 +               ioctl(fd, HDIO_DRIVE_CMD, &flush2);
202 +               break;
203 +       }
204 +
205 +       ret = ioctl(fd, HDIO_DRIVE_CMD, &stdby1) &&
206 +             ioctl(fd, HDIO_DRIVE_CMD, &stdby2);
207 +       close(fd);
208 +
209 +       if (ret)
210 +               return -1;
211 +       return 0;
212 +}
213 +
214 +/*
215 + *     List all disks and put them in standby mode.
216 + *     This has the side-effect of flushing the writecache,
217 + *     which is exactly what we want on poweroff.
218 + */
219 +int hddown(void)
220 +{
221 +       unsigned int flags;
222 +       char *disk;
223 +       DIR *blk;
224 +
225 +       if ((blk = opendir(SYS_BLK)) == (DIR*)0)
226 +               return -1;
227 +
228 +       while ((disk = list_disks(blk, &flags)))
229 +               do_standby_idedisk(disk, flags);
230 +
231 +       return closedir(blk);
232 +}
233 +
234 +/*
235 + * Strip off trailing white spaces
236 + */
237 +static char *strstrip(char *str)
238 +{
239 +       const size_t len = strlen(str);
240 +       if (len) {
241 +               char* end = str + len - 1;
242 +               while ((end != str) && ISSPACE(*end))
243 +                       end--;
244 +               *(end + 1) = '\0';                      /* remove trailing white spaces */
245 +       }
246 +       return str;
247 +}
248 +
249 +/*
250 + * Open a sysfs file without getting a controlling tty
251 + * and return FILE* pointer.
252 + */
253 +static FILE *hdopen(const char* const format, const char* const name)
254 +{
255 +       char buf[NAME_MAX+1];
256 +       FILE *fp = (FILE*)-1;
257 +       int fd, ret;
258 +       
259 +       ret = snprintf(buf, sizeof(buf), format, name);
260 +       if ((ret >= sizeof(buf)) || (ret < 0))
261 +               goto error;             /* error */
262 +
263 +       fd = open(buf, O_RDONLY|O_NOCTTY);
264 +       if (fd < 0) {
265 +               if (errno != ENOENT)
266 +                       goto error;     /* error */
267 +               fp = (FILE*)0;
268 +               goto error;             /* no entry `removable' */
269 +       }
270 +
271 +       fp = fdopen(fd, "r");
272 +       if (fp == (FILE*)0)
273 +               close(fd);              /* should not happen */
274 +error:
275 +       return fp;
276 +}
277 +
278 +/*
279 + * Check IDE/(S)ATA hard disk identity for
280 + * the FLUSH CACHE EXT bit set.
281 + */
282 +static int flush_cache_ext(const char *device)
283 +{
284 +#ifndef WIN_IDENTIFY
285 +#define WIN_IDENTIFY           0xEC
286 +#endif
287 +       unsigned char args[4+IDBYTES];
288 +       unsigned short *id = (unsigned short*)(&args[4]);
289 +       char buf[NAME_MAX+1], *ptr;
290 +       int fd = -1, ret = 0;
291 +       FILE *fp;
292 +
293 +       fp = hdopen(SYS_BLK "/%s/size", device);
294 +       if ((long)fp <= 0) {
295 +               if ((long)fp < 0)
296 +                       return -1;      /* error */
297 +               goto out;               /* no entry `size' */
298 +       }
299 +
300 +       ptr = fgets(buf, sizeof(buf), fp);
301 +       fclose(fp);
302 +       if (ptr == (char*)0)
303 +               goto out;               /* should not happen */
304 +
305 +       ptr = strstrip(buf);
306 +       if (*ptr == '\0')
307 +               goto out;               /* should not happen */
308 +
309 +       if ((size_t)atoll(buf) < (1<<28))
310 +               goto out;               /* small disk */
311 +               
312 +       ret = snprintf(buf, sizeof(buf), DEV_BASE "/%s", device);
313 +       if ((ret >= sizeof(buf)) || (ret < 0))
314 +               return -1;              /* error */
315 +
316 +       if ((fd = open(buf, O_RDONLY|O_NONBLOCK)) < 0)
317 +               goto out;
318 +
319 +       memset(&args[0], 0, sizeof(args));
320 +       args[0] = WIN_IDENTIFY;
321 +       args[3] = 1;
322 +       if (ioctl(fd, HDIO_DRIVE_CMD, &args))
323 +               goto out;
324 +#ifdef WORDS_BIGENDIAN
325 +# if 0
326 +       {
327 +               const unsigned short *end = id + IDBYTES/2;
328 +               const unsigned short *from = id;
329 +               unsigned short *to = id;
330 +
331 +               while (from < end)
332 +                       *to++ = bswap_16(*from++);
333 +       }
334 +# else
335 +       id[83] = bswap_16(id[83]);
336 +# endif
337 +#endif
338 +       if ((id[83] & MASK_EXT) == TEST_EXT)
339 +               ret = 1;
340 +out:
341 +       if (fd >= 0)
342 +               close(fd);
343 +       return ret;
344 +}
345 +#else /* ! USE_SYSFS */
346  #define MAX_DISKS      64
347  #define PROC_IDE       "/proc/ide"
348  #define DEV_BASE       "/dev"
349 @@ -154,7 +477,7 @@
350  
351         return (result1 ? result1 : result2);
352  }
353 -
354 +#endif /* ! USE_SYSFS */
355  #else /* __linux__ */
356  
357  int hddown(void)