Move calls to SET_ARRAY_INFO to common helper.
[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 #include        <ctype.h>
29
30 int load_sys(char *path, char *buf)
31 {
32         int fd = open(path, O_RDONLY);
33         int n;
34         if (fd < 0)
35                 return -1;
36         n = read(fd, buf, 1024);
37         close(fd);
38         if (n <0 || n >= 1024)
39                 return -1;
40         buf[n] = 0;
41         if (n && buf[n-1] == '\n')
42                 buf[n-1] = 0;
43         return 0;
44 }
45
46 void sysfs_free(struct mdinfo *sra)
47 {
48         while (sra) {
49                 struct mdinfo *sra2 = sra->next;
50                 while (sra->devs) {
51                         struct mdinfo *d = sra->devs;
52                         sra->devs = d->next;
53                         free(d);
54                 }
55                 free(sra);
56                 sra = sra2;
57         }
58 }
59
60 int sysfs_open(int devnum, char *devname, char *attr)
61 {
62         char fname[50];
63         int fd;
64
65         sprintf(fname, "/sys/block/%s/md/", devnum2devname(devnum));
66         if (devname) {
67                 strcat(fname, devname);
68                 strcat(fname, "/");
69         }
70         strcat(fname, attr);
71         fd = open(fname, O_RDWR);
72         if (fd < 0 && errno == EACCES)
73                 fd = open(fname, O_RDONLY);
74         return fd;
75 }
76
77 void sysfs_init(struct mdinfo *mdi, int fd, int devnum)
78 {
79         if (fd >= 0) {
80                 struct stat stb;
81                 mdu_version_t vers;
82                 if (fstat(fd, &stb))
83                         return;
84                 if (ioctl(fd, RAID_VERSION, &vers) != 0)
85                         return;
86                 if (major(stb.st_rdev)==9)
87                         sprintf(mdi->sys_name, "md%d", (int)minor(stb.st_rdev));
88                 else
89                         sprintf(mdi->sys_name, "md_d%d",
90                                 (int)minor(stb.st_rdev)>>MdpMinorShift);
91         } else {
92                 if (devnum >= 0)
93                         sprintf(mdi->sys_name, "md%d", devnum);
94                 else
95                         sprintf(mdi->sys_name, "md_d%d",
96                                 -1-devnum);
97         }
98 }
99
100 struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
101 {
102         /* Longest possible name in sysfs, mounted at /sys, is
103          *  /sys/block/md_dXXX/md/dev-XXXXX/block/dev
104          *  /sys/block/md_dXXX/md/metadata_version
105          * which is about 41 characters.  50 should do for now
106          */
107         char fname[50];
108         char buf[1024];
109         char *base;
110         char *dbase;
111         struct mdinfo *sra;
112         struct mdinfo *dev;
113         DIR *dir = NULL;
114         struct dirent *de;
115
116         sra = malloc(sizeof(*sra));
117         if (sra == NULL)
118                 return sra;
119         memset(sra, 0, sizeof(*sra));
120         sysfs_init(sra, fd, devnum);
121
122         sprintf(fname, "/sys/block/%s/md/", sra->sys_name);
123         base = fname + strlen(fname);
124
125         sra->devs = NULL;
126         if (options & GET_VERSION) {
127                 strcpy(base, "metadata_version");
128                 if (load_sys(fname, buf))
129                         goto abort;
130                 if (strncmp(buf, "none", 4) == 0) {
131                         sra->array.major_version =
132                                 sra->array.minor_version = -1;
133                         strcpy(sra->text_version, "");
134                 } else if (strncmp(buf, "external:", 9) == 0) {
135                         sra->array.major_version = -1;
136                         sra->array.minor_version = -2;
137                         strcpy(sra->text_version, buf+9);
138                 } else {
139                         sscanf(buf, "%d.%d",
140                                &sra->array.major_version,
141                                &sra->array.minor_version);
142                         strcpy(sra->text_version, buf);
143                 }
144         }
145         if (options & GET_LEVEL) {
146                 strcpy(base, "level");
147                 if (load_sys(fname, buf))
148                         goto abort;
149                 sra->array.level = map_name(pers, buf);
150         }
151         if (options & GET_LAYOUT) {
152                 strcpy(base, "layout");
153                 if (load_sys(fname, buf))
154                         goto abort;
155                 sra->array.layout = strtoul(buf, NULL, 0);
156         }
157         if (options & GET_DISKS) {
158                 strcpy(base, "raid_disks");
159                 if (load_sys(fname, buf))
160                         goto abort;
161                 sra->array.raid_disks = strtoul(buf, NULL, 0);
162         }
163         if (options & GET_DEGRADED) {
164                 strcpy(base, "degraded");
165                 if (load_sys(fname, buf))
166                         goto abort;
167                 sra->array.failed_disks = strtoul(buf, NULL, 0);
168         }
169         if (options & GET_COMPONENT) {
170                 strcpy(base, "component_size");
171                 if (load_sys(fname, buf))
172                         goto abort;
173                 sra->component_size = strtoull(buf, NULL, 0);
174                 /* sysfs reports "K", but we want sectors */
175                 sra->component_size *= 2;
176         }
177         if (options & GET_CHUNK) {
178                 strcpy(base, "chunk_size");
179                 if (load_sys(fname, buf))
180                         goto abort;
181                 sra->array.chunk_size = strtoul(buf, NULL, 0);
182         }
183         if (options & GET_CACHE) {
184                 strcpy(base, "stripe_cache_size");
185                 if (load_sys(fname, buf))
186                         goto abort;
187                 sra->cache_size = strtoul(buf, NULL, 0);
188         }
189         if (options & GET_MISMATCH) {
190                 strcpy(base, "mismatch_cnt");
191                 if (load_sys(fname, buf))
192                         goto abort;
193                 sra->mismatch_cnt = strtoul(buf, NULL, 0);
194         }
195         if (options & GET_SAFEMODE) {
196                 int scale = 1;
197                 int dot = 0;
198                 int i;
199                 unsigned long msec;
200                 size_t len;
201
202                 strcpy(base, "safe_mode_delay");
203                 if (load_sys(fname, buf))
204                         goto abort;
205
206                 /* remove a period, and count digits after it */
207                 len = strlen(buf);
208                 for (i = 0; i < len; i++) {
209                         if (dot) {
210                                 if (isdigit(buf[i])) {
211                                         buf[i-1] = buf[i];
212                                         scale *= 10;
213                                 }
214                                 buf[i] = 0;
215                         } else if (buf[i] == '.') {
216                                 dot=1;
217                                 buf[i] = 0;
218                         }
219                 }
220                 msec = strtoul(buf, NULL, 10);
221                 msec = (msec * 1000) / scale;
222                 sra->safe_mode_delay = msec;
223         }
224
225         if (! (options & GET_DEVS))
226                 return sra;
227
228         /* Get all the devices as well */
229         *base = 0;
230         dir = opendir(fname);
231         if (!dir)
232                 goto abort;
233         sra->array.spare_disks = 0;
234
235         while ((de = readdir(dir)) != NULL) {
236                 char *ep;
237                 if (de->d_ino == 0 ||
238                     strncmp(de->d_name, "dev-", 4) != 0)
239                         continue;
240                 strcpy(base, de->d_name);
241                 dbase = base + strlen(base);
242                 *dbase++ = '/';
243
244                 dev = malloc(sizeof(*dev));
245                 if (!dev)
246                         goto abort;
247
248                 /* Always get slot, major, minor */
249                 strcpy(dbase, "slot");
250                 if (load_sys(fname, buf)) {
251                         /* hmm... unable to read 'slot' maybe the device
252                          * is going away?
253                          */
254                         strcpy(dbase, "block");
255                         if (readlink(fname, buf, sizeof(buf)) < 0 &&
256                             errno != ENAMETOOLONG) {
257                                 /* ...yup device is gone */
258                                 free(dev);
259                                 continue;
260                         } else {
261                                 /* slot is unreadable but 'block' link
262                                  * still intact... something bad is happening
263                                  * so abort
264                                  */
265                                 free(dev);
266                                 goto abort;
267                         }
268                         
269                 }
270                 dev->next = sra->devs;
271                 sra->devs = dev;
272
273                 strcpy(dev->sys_name, de->d_name);
274                 dev->disk.raid_disk = strtoul(buf, &ep, 10);
275                 if (*ep) dev->disk.raid_disk = -1;
276
277                 strcpy(dbase, "block/dev");
278                 if (load_sys(fname, buf))
279                         goto abort;
280                 sscanf(buf, "%d:%d", &dev->disk.major, &dev->disk.minor);
281
282                 if (options & GET_OFFSET) {
283                         strcpy(dbase, "offset");
284                         if (load_sys(fname, buf))
285                                 goto abort;
286                         dev->data_offset = strtoull(buf, NULL, 0);
287                 }
288                 if (options & GET_SIZE) {
289                         strcpy(dbase, "size");
290                         if (load_sys(fname, buf))
291                                 goto abort;
292                         dev->component_size = strtoull(buf, NULL, 0) * 2;
293                 }
294                 if (options & GET_STATE) {
295                         dev->disk.state = 0;
296                         strcpy(dbase, "state");
297                         if (load_sys(fname, buf))
298                                 goto abort;
299                         if (strstr(buf, "in_sync"))
300                                 dev->disk.state |= (1<<MD_DISK_SYNC);
301                         if (strstr(buf, "faulty"))
302                                 dev->disk.state |= (1<<MD_DISK_FAULTY);
303                         if (dev->disk.state == 0)
304                                 sra->array.spare_disks++;
305                 }
306                 if (options & GET_ERROR) {
307                         strcpy(buf, "errors");
308                         if (load_sys(fname, buf))
309                                 goto abort;
310                         dev->errors = strtoul(buf, NULL, 0);
311                 }
312         }
313         closedir(dir);
314         return sra;
315
316  abort:
317         if (dir)
318                 closedir(dir);
319         sysfs_free(sra);
320         return NULL;
321 }
322
323 int sysfs_attr_match(const char *attr, const char *str)
324 {
325         /* See if attr, read from a sysfs file, matches
326          * str.  They must either be the same, or attr can
327          * have a trailing newline or comma
328          */
329         while (*attr && *str && *attr == *str) {
330                 attr++;
331                 str++;
332         }
333
334         if (*str || (*attr && *attr != ',' && *attr != '\n'))
335                 return 0;
336         return 1;
337 }
338
339 int sysfs_match_word(const char *word, char **list)
340 {
341         int n;
342         for (n=0; list[n]; n++)
343                 if (sysfs_attr_match(word, list[n]))
344                         break;
345         return n;
346 }
347
348 unsigned long long get_component_size(int fd)
349 {
350         /* Find out the component size of the array.
351          * We cannot trust GET_ARRAY_INFO ioctl as it's
352          * size field is only 32bits.
353          * So look in /sys/block/mdXXX/md/component_size
354          *
355          * This returns in units of sectors.
356          */
357         struct stat stb;
358         char fname[50];
359         int n;
360         if (fstat(fd, &stb)) return 0;
361         if (major(stb.st_rdev) == 9)
362                 sprintf(fname, "/sys/block/md%d/md/component_size",
363                         (int)minor(stb.st_rdev));
364         else
365                 sprintf(fname, "/sys/block/md_d%d/md/component_size",
366                         (int)minor(stb.st_rdev)>>MdpMinorShift);
367         fd = open(fname, O_RDONLY);
368         if (fd < 0)
369                 return 0;
370         n = read(fd, fname, sizeof(fname));
371         close(fd);
372         if (n == sizeof(fname))
373                 return 0;
374         fname[n] = 0;
375         return strtoull(fname, NULL, 10) * 2;
376 }
377
378 int sysfs_set_str(struct mdinfo *sra, struct mdinfo *dev,
379                   char *name, char *val)
380 {
381         char fname[50];
382         int n;
383         int fd;
384
385         sprintf(fname, "/sys/block/%s/md/%s/%s",
386                 sra->sys_name, dev?dev->sys_name:"", name);
387         fd = open(fname, O_WRONLY);
388         if (fd < 0)
389                 return -1;
390         n = write(fd, val, strlen(val));
391         close(fd);
392         if (n != strlen(val))
393                 return -1;
394         return 0;
395 }
396
397 int sysfs_set_num(struct mdinfo *sra, struct mdinfo *dev,
398                   char *name, unsigned long long val)
399 {
400         char valstr[50];
401         sprintf(valstr, "%llu", val);
402         return sysfs_set_str(sra, dev, name, valstr);
403 }
404
405 int sysfs_get_ll(struct mdinfo *sra, struct mdinfo *dev,
406                        char *name, unsigned long long *val)
407 {
408         char fname[50];
409         char buf[50];
410         int n;
411         int fd;
412         char *ep;
413         sprintf(fname, "/sys/block/%s/md/%s/%s",
414                 sra->sys_name, dev?dev->sys_name:"", name);
415         fd = open(fname, O_RDONLY);
416         if (fd < 0)
417                 return -1;
418         n = read(fd, buf, sizeof(buf));
419         close(fd);
420         if (n <= 0)
421                 return -1;
422         buf[n] = 0;
423         *val = strtoull(buf, &ep, 0);
424         if (ep == buf || (*ep != 0 && *ep != '\n' && *ep != ' '))
425                 return -1;
426         return 0;
427 }
428
429 int sysfs_set_safemode(struct mdinfo *sra, unsigned long ms)
430 {
431         unsigned long sec;
432         unsigned long msec;
433         char delay[30];
434
435         sec = ms / 1000;
436         msec = ms - (sec * 1000);
437
438         sprintf(delay, "%ld.%ld", sec, msec);
439         return sysfs_set_str(sra, NULL, "safe_mode_delay", delay);
440 }
441
442 int sysfs_set_array(struct mdinfo *info, int vers)
443 {
444         int rv = 0;
445         char ver[100];
446
447         ver[0] = 0;
448         if (info->array.major_version == -1 &&
449             info->array.minor_version == -2) {
450                 strcat(strcpy(ver, "external:"), info->text_version);
451
452                 if ((vers % 100) < 2 ||
453                     sysfs_set_str(info, NULL, "metadata_version",
454                                   ver) < 0) {
455                         fprintf(stderr, Name ": This kernel does not "
456                                 "support external metadata.\n");
457                         return 1;
458                 }
459         }
460         if (info->array.level < 0)
461                 return 0; /* FIXME */
462         rv |= sysfs_set_str(info, NULL, "level",
463                             map_num(pers, info->array.level));
464         rv |= sysfs_set_num(info, NULL, "raid_disks", info->array.raid_disks);
465         rv |= sysfs_set_num(info, NULL, "chunk_size", info->array.chunk_size);
466         rv |= sysfs_set_num(info, NULL, "layout", info->array.layout);
467         rv |= sysfs_set_num(info, NULL, "component_size", info->component_size/2);
468         if (info->array.level > 0)
469                 rv |= sysfs_set_num(info, NULL, "resync_start", info->resync_start);
470         return rv;
471 }
472
473 int sysfs_add_disk(struct mdinfo *sra, struct mdinfo *sd)
474 {
475         char dv[100];
476         char nm[100];
477         char *dname;
478         int rv;
479
480         sprintf(dv, "%d:%d", sd->disk.major, sd->disk.minor);
481         rv = sysfs_set_str(sra, NULL, "new_dev", dv);
482         if (rv)
483                 return rv;
484
485         memset(nm, 0, sizeof(nm));
486         sprintf(dv, "/sys/dev/block/%d:%d", sd->disk.major, sd->disk.minor);
487         rv = readlink(dv, nm, sizeof(nm));
488         if (rv <= 0)
489                 return -1;
490         nm[rv] = '\0';
491         dname = strrchr(nm, '/');
492         if (dname) dname++;
493         strcpy(sd->sys_name, "dev-");
494         strcpy(sd->sys_name+4, dname);
495
496         rv = sysfs_set_num(sra, sd, "offset", sd->data_offset);
497         rv |= sysfs_set_num(sra, sd, "size", (sd->component_size+1) / 2);
498         if (sra->array.level != LEVEL_CONTAINER) {
499                 rv |= sysfs_set_num(sra, sd, "slot", sd->disk.raid_disk);
500 //              rv |= sysfs_set_str(sra, sd, "state", "in_sync");
501         }
502         return rv;
503 }
504
505 #if 0
506 int sysfs_disk_to_sg(int fd)
507 {
508         /* from an open block device, try find and open its corresponding
509          * scsi_generic interface
510          */
511         struct stat st;
512         char path[256];
513         char sg_path[256];
514         char sg_major_minor[8];
515         char *c;
516         DIR *dir;
517         struct dirent *de;
518         int major, minor, rv;
519
520         if (fstat(fd, &st))
521                 return -1;
522
523         snprintf(path, sizeof(path), "/sys/dev/block/%d:%d/device",
524                  major(st.st_rdev), minor(st.st_rdev));
525
526         dir = opendir(path);
527         if (!dir)
528                 return -1;
529
530         de = readdir(dir);
531         while (de) {
532                 if (strncmp("scsi_generic:", de->d_name,
533                             strlen("scsi_generic:")) == 0)
534                         break;
535                 de = readdir(dir);
536         }
537         closedir(dir);
538
539         if (!de)
540                 return -1;
541
542         snprintf(sg_path, sizeof(sg_path), "%s/%s/dev", path, de->d_name);
543         fd = open(sg_path, O_RDONLY);
544         if (fd < 0)
545                 return fd;
546
547         rv = read(fd, sg_major_minor, sizeof(sg_major_minor));
548         close(fd);
549         if (rv < 0)
550                 return -1;
551         else
552                 sg_major_minor[rv - 1] = '\0';
553
554         c = strchr(sg_major_minor, ':');
555         *c = '\0';
556         c++;
557         major = strtol(sg_major_minor, NULL, 10);
558         minor = strtol(c, NULL, 10);
559         snprintf(path, sizeof(path), "/dev/.tmp.md.%d:%d:%d",
560                  (int) getpid(), major, minor);
561         if (mknod(path, S_IFCHR|0600, makedev(major, minor))==0) {
562                         fd = open(path, O_RDONLY);
563                         unlink(path);
564                         return fd;
565         }
566
567         return -1;
568 }
569 #endif
570
571 int sysfs_disk_to_scsi_id(int fd, __u32 *id)
572 {
573         /* from an open block device, try to retrieve it scsi_id */
574         struct stat st;
575         char path[256];
576         char *c1, *c2;
577         DIR *dir;
578         struct dirent *de;
579
580         if (fstat(fd, &st))
581                 return 1;
582
583         snprintf(path, sizeof(path), "/sys/dev/block/%d:%d/device",
584                  major(st.st_rdev), minor(st.st_rdev));
585
586         dir = opendir(path);
587         if (!dir)
588                 return 1;
589
590         de = readdir(dir);
591         while (de) {
592                 if (strncmp("scsi_disk:", de->d_name,
593                             strlen("scsi_disk:")) == 0)
594                         break;
595                 de = readdir(dir);
596         }
597         closedir(dir);
598
599         if (!de)
600                 return 1;
601
602         c1 = strchr(de->d_name, ':');
603         c1++;
604         c2 = strchr(c1, ':');
605         *c2 = '\0';
606         *id = strtol(c1, NULL, 10) << 24; /* host */
607         c1 = c2 + 1;
608         c2 = strchr(c1, ':');
609         *c2 = '\0';
610         *id |= strtol(c1, NULL, 10) << 16; /* channel */
611         c1 = c2 + 1;
612         c2 = strchr(c1, ':');
613         *c2 = '\0';
614         *id |= strtol(c1, NULL, 10) << 8; /* lun */
615         c1 = c2 + 1;
616         *id |= strtol(c1, NULL, 10); /* id */
617
618         return 0;
619 }
620
621
622 int sysfs_unique_holder(int devnum, long rdev)
623 {
624         /* Check that devnum is a holder of rdev,
625          * and is the only holder.
626          * we should be locked against races by
627          * an O_EXCL on devnum
628          */
629         DIR *dir;
630         struct dirent *de;
631         char dirname[100];
632         char l;
633         int found = 0;
634         sprintf(dirname, "/sys/dev/block/%d:%d/holders",
635                 major(rdev), minor(rdev));
636         dir = opendir(dirname);
637         errno = ENOENT;
638         if (!dir)
639                 return 0;
640         l = strlen(dirname);
641         while ((de = readdir(dir)) != NULL) {
642                 char buf[10];
643                 int n;
644                 int mj, mn;
645                 char c;
646                 int fd;
647
648                 if (de->d_ino == 0)
649                         continue;
650                 if (de->d_name[0] == '.')
651                         continue;
652                 strcpy(dirname+l, "/");
653                 strcat(dirname+l, de->d_name);
654                 strcat(dirname+l, "/dev");
655                 fd = open(dirname, O_RDONLY);
656                 if (fd < 0) {
657                         errno = ENOENT;
658                         break;
659                 }
660                 n = read(fd, buf, sizeof(buf)-1);
661                 close(fd);
662                 buf[n] = 0;
663                 if (sscanf(buf, "%d:%d%c", &mj, &mn, &c) != 3 ||
664                     c != '\n') {
665                         errno = ENOENT;
666                         break;
667                 }
668                 if (mj != MD_MAJOR)
669                         mn = -1-(mn>>6);
670
671                 if (devnum != mn) {
672                         errno = EEXIST;
673                         break;
674                 }
675                 found = 1;
676         }
677         closedir(dir);
678         if (de)
679                 return 0;
680         else
681                 return found;
682 }