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