Remember to close directories when we are finished with them.
[thirdparty/mdadm.git] / sysfs.c
1 /*
2  * sysfs - extract md related information from sysfs.  Part of:
3  * mdadm - manage Linux "md" devices aka RAID arrays.
4  *
5  * Copyright (C) 2006 Neil Brown <neilb@suse.de>
6  *
7  *
8  *    This program is free software; you can redistribute it and/or modify
9  *    it under the terms of the GNU General Public License as published by
10  *    the Free Software Foundation; either version 2 of the License, or
11  *    (at your option) any later version.
12  *
13  *    This program is distributed in the hope that it will be useful,
14  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *    GNU General Public License for more details.
17  *
18  *    You should have received a copy of the GNU General Public License
19  *    along with this program; if not, write to the Free Software
20  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  *    Author: Neil Brown
23  *    Email: <neilb@suse.de>
24  */
25
26 #include        "mdadm.h"
27 #include        <dirent.h>
28
29 int load_sys(char *path, char *buf)
30 {
31         int fd = open(path, O_RDONLY);
32         int n;
33         if (fd < 0)
34                 return -1;
35         n = read(fd, buf, 1024);
36         close(fd);
37         if (n <0 || n >= 1024)
38                 return -1;
39         buf[n] = 0;
40         if (buf[n-1] == '\n')
41                 buf[n-1] = 0;
42         return 0;
43 }
44
45 void sysfs_free(struct mdinfo *sra)
46 {
47         while (sra) {
48                 struct mdinfo *sra2 = sra->next;
49                 while (sra->devs) {
50                         struct mdinfo *d = sra->devs;
51                         sra->devs = d->next;
52                         free(d);
53                 }
54                 free(sra);
55                 sra = sra2;
56         }
57 }
58
59 int sysfs_open(int devnum, char *devname, char *attr)
60 {
61         char fname[50];
62         char sys_name[16];
63         int fd;
64         if (devnum >= 0)
65                 sprintf(sys_name, "md%d", devnum);
66         else
67                 sprintf(sys_name, "md_d%d",
68                         -1-devnum);
69
70         sprintf(fname, "/sys/block/%s/md/", sys_name);
71         if (devname) {
72                 strcat(fname, devname);
73                 strcat(fname, "/");
74         }
75         strcat(fname, attr);
76         fd = open(fname, O_RDWR);
77         if (fd < 0 && errno == -EACCES)
78                 fd = open(fname, O_RDONLY);
79         return fd;
80 }
81
82 struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
83 {
84         /* Longest possible name in sysfs, mounted at /sys, is
85          *  /sys/block/md_dXXX/md/dev-XXXXX/block/dev
86          *  /sys/block/md_dXXX/md/metadata_version
87          * which is about 41 characters.  50 should do for now
88          */
89         char fname[50];
90         char buf[1024];
91         char *base;
92         char *dbase;
93         struct mdinfo *sra;
94         struct mdinfo *dev;
95         DIR *dir = NULL;
96         struct dirent *de;
97
98         sra = malloc(sizeof(*sra));
99         if (sra == NULL)
100                 return sra;
101         sra->next = NULL;
102
103         if (fd >= 0) {
104                 struct stat stb;
105                 mdu_version_t vers;
106                 if (fstat(fd, &stb)) return NULL;
107                 if (ioctl(fd, RAID_VERSION, &vers) != 0)
108                         return NULL;
109                 if (major(stb.st_rdev)==9)
110                         sprintf(sra->sys_name, "md%d", (int)minor(stb.st_rdev));
111                 else
112                         sprintf(sra->sys_name, "md_d%d",
113                                 (int)minor(stb.st_rdev)>>MdpMinorShift);
114         } else {
115                 if (devnum >= 0)
116                         sprintf(sra->sys_name, "md%d", devnum);
117                 else
118                         sprintf(sra->sys_name, "md_d%d",
119                                 -1-devnum);
120         }
121         sprintf(fname, "/sys/block/%s/md/", sra->sys_name);
122         base = fname + strlen(fname);
123
124         sra->devs = NULL;
125         if (options & GET_VERSION) {
126                 strcpy(base, "metadata_version");
127                 if (load_sys(fname, buf))
128                         goto abort;
129                 if (strncmp(buf, "none", 4) == 0) {
130                         sra->array.major_version =
131                                 sra->array.minor_version = -1;
132                         strcpy(sra->text_version, "");
133                 } else if (strncmp(buf, "external:", 9) == 0) {
134                         sra->array.major_version = -1;
135                         sra->array.minor_version = -2;
136                         strcpy(sra->text_version, buf+9);
137                 } else
138                         sscanf(buf, "%d.%d",
139                                &sra->array.major_version,
140                                &sra->array.minor_version);
141         }
142         if (options & GET_LEVEL) {
143                 strcpy(base, "level");
144                 if (load_sys(fname, buf))
145                         goto abort;
146                 sra->array.level = map_name(pers, buf);
147         }
148         if (options & GET_LAYOUT) {
149                 strcpy(base, "layout");
150                 if (load_sys(fname, buf))
151                         goto abort;
152                 sra->array.layout = strtoul(buf, NULL, 0);
153         }
154         if (options & GET_DISKS) {
155                 strcpy(base, "raid_disks");
156                 if (load_sys(fname, buf))
157                         goto abort;
158                 sra->array.raid_disks = strtoul(buf, NULL, 0);
159         }
160         if (options & GET_COMPONENT) {
161                 strcpy(base, "component_size");
162                 if (load_sys(fname, buf))
163                         goto abort;
164                 sra->component_size = strtoull(buf, NULL, 0);
165                 /* sysfs reports "K", but we want sectors */
166                 sra->component_size *= 2;
167         }
168         if (options & GET_CHUNK) {
169                 strcpy(base, "chunk_size");
170                 if (load_sys(fname, buf))
171                         goto abort;
172                 sra->array.chunk_size = strtoul(buf, NULL, 0);
173         }
174         if (options & GET_CACHE) {
175                 strcpy(base, "stripe_cache_size");
176                 if (load_sys(fname, buf))
177                         goto abort;
178                 sra->cache_size = strtoul(buf, NULL, 0);
179         }
180         if (options & GET_MISMATCH) {
181                 strcpy(base, "mismatch_cnt");
182                 if (load_sys(fname, buf))
183                         goto abort;
184                 sra->mismatch_cnt = strtoul(buf, NULL, 0);
185         }
186
187         if (! (options & GET_DEVS))
188                 return sra;
189
190         /* Get all the devices as well */
191         *base = 0;
192         dir = opendir(fname);
193         if (!dir)
194                 goto abort;
195         sra->array.spare_disks = 0;
196
197         while ((de = readdir(dir)) != NULL) {
198                 char *ep;
199                 if (de->d_ino == 0 ||
200                     strncmp(de->d_name, "dev-", 4) != 0)
201                         continue;
202                 strcpy(base, de->d_name);
203                 dbase = base + strlen(base);
204                 *dbase++ = '/';
205
206                 dev = malloc(sizeof(*dev));
207                 if (!dev)
208                         goto abort;
209                 dev->next = sra->devs;
210                 sra->devs = dev;
211                 strcpy(dev->sys_name, de->d_name);
212
213                 /* Always get slot, major, minor */
214                 strcpy(dbase, "slot");
215                 if (load_sys(fname, buf))
216                         goto abort;
217                 dev->disk.raid_disk = strtoul(buf, &ep, 10);
218                 if (*ep) dev->disk.raid_disk = -1;
219
220                 strcpy(dbase, "block/dev");
221                 if (load_sys(fname, buf))
222                         goto abort;
223                 sscanf(buf, "%d:%d", &dev->disk.major, &dev->disk.minor);
224
225                 if (options & GET_OFFSET) {
226                         strcpy(dbase, "offset");
227                         if (load_sys(fname, buf))
228                                 goto abort;
229                         dev->data_offset = strtoull(buf, NULL, 0);
230                 }
231                 if (options & GET_SIZE) {
232                         strcpy(dbase, "size");
233                         if (load_sys(fname, buf))
234                                 goto abort;
235                         dev->component_size = strtoull(buf, NULL, 0);
236                 }
237                 if (options & GET_STATE) {
238                         dev->disk.state = 0;
239                         strcpy(dbase, "state");
240                         if (load_sys(fname, buf))
241                                 goto abort;
242                         if (strstr(buf, "in_sync"))
243                                 dev->disk.state |= (1<<MD_DISK_SYNC);
244                         if (strstr(buf, "faulty"))
245                                 dev->disk.state |= (1<<MD_DISK_FAULTY);
246                         if (dev->disk.state == 0)
247                                 sra->array.spare_disks++;
248                 }
249                 if (options & GET_ERROR) {
250                         strcpy(buf, "errors");
251                         if (load_sys(fname, buf))
252                                 goto abort;
253                         dev->errors = strtoul(buf, NULL, 0);
254                 }
255         }
256         closedir(dir);
257         return sra;
258
259  abort:
260         if (dir)
261                 closedir(dir);
262         sysfs_free(sra);
263         return NULL;
264 }
265
266 unsigned long long get_component_size(int fd)
267 {
268         /* Find out the component size of the array.
269          * We cannot trust GET_ARRAY_INFO ioctl as it's
270          * size field is only 32bits.
271          * So look in /sys/block/mdXXX/md/component_size
272          *
273          * This returns in units of sectors.
274          */
275         struct stat stb;
276         char fname[50];
277         int n;
278         if (fstat(fd, &stb)) return 0;
279         if (major(stb.st_rdev) == 9)
280                 sprintf(fname, "/sys/block/md%d/md/component_size",
281                         (int)minor(stb.st_rdev));
282         else
283                 sprintf(fname, "/sys/block/md_d%d/md/component_size",
284                         (int)minor(stb.st_rdev)>>MdpMinorShift);
285         fd = open(fname, O_RDONLY);
286         if (fd < 0)
287                 return 0;
288         n = read(fd, fname, sizeof(fname));
289         close(fd);
290         if (n == sizeof(fname))
291                 return 0;
292         fname[n] = 0;
293         return strtoull(fname, NULL, 10) * 2;
294 }
295
296 int sysfs_set_str(struct mdinfo *sra, struct mdinfo *dev,
297                   char *name, char *val)
298 {
299         char fname[50];
300         int n;
301         int fd;
302         sprintf(fname, "/sys/block/%s/md/%s/%s",
303                 sra->sys_name, dev?dev->sys_name:"", name);
304         fd = open(fname, O_WRONLY);
305         if (fd < 0)
306                 return -1;
307         n = write(fd, val, strlen(val));
308         close(fd);
309         if (n != strlen(val))
310                 return -1;
311         return 0;
312 }
313
314 int sysfs_set_num(struct mdinfo *sra, struct mdinfo *dev,
315                   char *name, unsigned long long val)
316 {
317         char valstr[50];
318         sprintf(valstr, "%llu", val);
319         return sysfs_set_str(sra, dev, name, valstr);
320 }
321
322 int sysfs_get_ll(struct mdinfo *sra, struct mdinfo *dev,
323                        char *name, unsigned long long *val)
324 {
325         char fname[50];
326         char buf[50];
327         int n;
328         int fd;
329         char *ep;
330         sprintf(fname, "/sys/block/%s/md/%s/%s",
331                 sra->sys_name, dev?dev->sys_name:"", name);
332         fd = open(fname, O_RDONLY);
333         if (fd < 0)
334                 return -1;
335         n = read(fd, buf, sizeof(buf));
336         close(fd);
337         if (n <= 0)
338                 return -1;
339         buf[n] = 0;
340         *val = strtoull(buf, &ep, 0);
341         if (ep == buf || (*ep != 0 && *ep != '\n' && *ep != ' '))
342                 return -1;
343         return 0;
344 }
345
346 int sysfs_set_array(struct mdinfo *sra,
347                     struct mdinfo *info)
348 {
349         int rv = 0;
350         sra->array = info->array;
351
352         if (info->array.level < 0)
353                 return 0; /* FIXME */
354         rv |= sysfs_set_str(sra, NULL, "level",
355                             map_num(pers, info->array.level));
356         rv |= sysfs_set_num(sra, NULL, "raid_disks", info->array.raid_disks);
357         rv |= sysfs_set_num(sra, NULL, "chunk_size", info->array.chunk_size);
358         rv |= sysfs_set_num(sra, NULL, "layout", info->array.layout);
359         rv |= sysfs_set_num(sra, NULL, "component_size", info->component_size);
360         rv |= sysfs_set_num(sra, NULL, "resync_start", info->resync_start);
361         sra->array = info->array;
362         return rv;
363 }
364
365 int sysfs_add_disk(struct mdinfo *sra, struct mdinfo *sd)
366 {
367         char dv[100];
368         char nm[100];
369         struct mdinfo *sd2;
370         char *dname;
371         int rv;
372
373         sprintf(dv, "%d:%d", sd->disk.major, sd->disk.minor);
374         rv = sysfs_set_str(sra, NULL, "new_dev", dv);
375         if (rv)
376                 return rv;
377
378         memset(nm, 0, sizeof(nm));
379         sprintf(dv, "/sys/dev/block/%d:%d", sd->disk.major, sd->disk.minor);
380         if (readlink(dv, nm, sizeof(nm)) < 0)
381                 return -1;
382         dname = strrchr(nm, '/');
383         if (dname) dname++;
384         strcpy(sd->sys_name, "dev-");
385         strcpy(sd->sys_name+4, dname);
386
387         rv |= sysfs_set_num(sra, sd, "offset", sd->data_offset);
388         rv |= sysfs_set_num(sra, sd, "size", (sd->component_size+1) / 2);
389         if (sra->array.level != LEVEL_CONTAINER) {
390                 rv |= sysfs_set_num(sra, sd, "slot", sd->disk.raid_disk);
391 //              rv |= sysfs_set_str(sra, sd, "state", "in_sync");
392         }
393         sd2 = malloc(sizeof(*sd2));
394         *sd2 = *sd;
395         sd2->next = sra->devs;
396         sra->devs = sd2;
397
398         return rv;
399 }