]> git.ipfire.org Git - thirdparty/mdadm.git/blame - lib.c
mdadm: set ident.devname if applicable
[thirdparty/mdadm.git] / lib.c
CommitLineData
78c0a3b1
N
1/*
2 * mdadm - manage Linux "md" devices aka RAID arrays.
3 *
4 * Copyright (C) 2011 Neil Brown <neilb@suse.de>
5 *
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 * Author: Neil Brown
22 * Email: <neilb@suse.de>
23 */
24
25#include "mdadm.h"
d0c017a6 26#include "dlink.h"
78c0a3b1 27#include <ctype.h>
60815698 28#include <limits.h>
78c0a3b1 29
a35aa68f
XN
30bool is_dev_alive(char *path)
31{
32 if (!path)
33 return false;
34
35 if (access(path, R_OK) == 0)
36 return true;
37
38 return false;
39}
40
78c0a3b1
N
41/* This fill contains various 'library' style function. They
42 * have no dependency on anything outside this file.
43 */
44
45int get_mdp_major(void)
46{
ad7ac9ac 47 static int mdp_major = -1;
78c0a3b1
N
48 FILE *fl;
49 char *w;
50 int have_block = 0;
51 int have_devices = 0;
52 int last_num = -1;
53
54 if (mdp_major != -1)
55 return mdp_major;
ad7ac9ac 56
78c0a3b1
N
57 fl = fopen("/proc/devices", "r");
58 if (!fl)
59 return -1;
ad7ac9ac 60
78c0a3b1 61 while ((w = conf_word(fl, 1))) {
ad7ac9ac 62 if (have_block && strcmp(w, "devices:") == 0)
78c0a3b1 63 have_devices = 1;
ad7ac9ac 64 have_block = (strcmp(w, "Block") == 0);
78c0a3b1
N
65 if (isdigit(w[0]))
66 last_num = atoi(w);
ad7ac9ac 67 if (have_devices && strcmp(w, "mdp") == 0)
78c0a3b1
N
68 mdp_major = last_num;
69 free(w);
70 }
71 fclose(fl);
ad7ac9ac 72
78c0a3b1
N
73 return mdp_major;
74}
75
d3c40fab 76char *devid2kname(dev_t devid)
bc17158d
N
77{
78 char path[30];
52a94085 79 char link[PATH_MAX];
bc17158d
N
80 static char devnm[32];
81 char *cp;
82 int n;
83
84 /* Look at the
85 * /sys/dev/block/%d:%d link which must look like
86 * and take the last component.
87 */
d3c40fab 88 sprintf(path, "/sys/dev/block/%d:%d", major(devid), minor(devid));
ad7ac9ac 89 n = readlink(path, link, sizeof(link) - 1);
bc17158d
N
90 if (n > 0) {
91 link[n] = 0;
92 cp = strrchr(link, '/');
93 if (cp) {
ad7ac9ac 94 strcpy(devnm, cp + 1);
bc17158d
N
95 return devnm;
96 }
97 }
98 return NULL;
99}
100
f96b1302
AP
101char *stat2kname(struct stat *st)
102{
103 if ((S_IFMT & st->st_mode) != S_IFBLK)
104 return NULL;
ad7ac9ac 105
f96b1302
AP
106 return devid2kname(st->st_rdev);
107}
108
109char *fd2kname(int fd)
110{
111 struct stat stb;
ad7ac9ac 112
f96b1302
AP
113 if (fstat(fd, &stb) == 0)
114 return stat2kname(&stb);
ad7ac9ac 115
f96b1302
AP
116 return NULL;
117}
118
13db17bd 119char *devid2devnm(dev_t devid)
78c0a3b1
N
120{
121 char path[30];
122 char link[200];
4dd2df09
N
123 static char devnm[32];
124 char *cp, *ep;
78c0a3b1
N
125 int n;
126
4dd2df09
N
127 /* Might be an extended-minor partition or a
128 * named md device. Look at the
129 * /sys/dev/block/%d:%d link which must look like
130 * ../../block/mdXXX/mdXXXpYY
131 * or
132 * ...../block/md_FOO
133 */
ad7ac9ac
JS
134 sprintf(path, "/sys/dev/block/%d:%d", major(devid), minor(devid));
135 n = readlink(path, link, sizeof(link) - 1);
4dd2df09 136 if (n > 0) {
78c0a3b1 137 link[n] = 0;
4dd2df09
N
138 cp = strstr(link, "/block/");
139 if (cp) {
140 cp += 7;
141 ep = strchr(cp, '/');
142 if (ep)
143 *ep = 0;
144 strcpy(devnm, cp);
145 return devnm;
146 }
78c0a3b1 147 }
4dd2df09
N
148 if (major(devid) == MD_MAJOR)
149 sprintf(devnm,"md%d", minor(devid));
150 else if (major(devid) == (unsigned)get_mdp_major())
151 sprintf(devnm,"md_d%d",
152 (minor(devid)>>MdpMinorShift));
153 else
154 return NULL;
ad7ac9ac 155
4dd2df09
N
156 return devnm;
157}
78c0a3b1 158
4dd2df09
N
159char *stat2devnm(struct stat *st)
160{
161 if ((S_IFMT & st->st_mode) != S_IFBLK)
162 return NULL;
ad7ac9ac 163
4dd2df09 164 return devid2devnm(st->st_rdev);
78c0a3b1
N
165}
166
27ad4900
LG
167bool stat_is_md_dev(struct stat *st)
168{
169 if ((S_IFMT & st->st_mode) != S_IFBLK)
170 return false;
171 if (major(st->st_rdev) == MD_MAJOR)
172 return true;
173 if (major(st->st_rdev) == (unsigned)get_mdp_major())
174 return true;
175
176 return false;
177}
178
4dd2df09 179char *fd2devnm(int fd)
78c0a3b1
N
180{
181 struct stat stb;
ad7ac9ac 182
78c0a3b1 183 if (fstat(fd, &stb) == 0)
4dd2df09 184 return stat2devnm(&stb);
ad7ac9ac 185
4dd2df09 186 return NULL;
78c0a3b1
N
187}
188
cd6cbb08
N
189/* When we create a new array, we don't want the content to
190 * be immediately examined by udev - it is probably meaningless.
dd180cb1 191 * So create /run/mdadm/creating-mdXXX and expect that a udev
cd6cbb08
N
192 * rule will noticed this and act accordingly.
193 */
194static char block_path[] = "/run/mdadm/creating-%s";
195static char *unblock_path = NULL;
196void udev_block(char *devnm)
197{
198 int fd;
199 char *path = NULL;
200
201 xasprintf(&path, block_path, devnm);
202 fd = open(path, O_CREAT|O_RDWR, 0600);
203 if (fd >= 0) {
204 close(fd);
205 unblock_path = path;
206 } else
207 free(path);
208}
209
210void udev_unblock(void)
211{
212 if (unblock_path)
213 unlink(unblock_path);
214 free(unblock_path);
215 unblock_path = NULL;
216}
217
78c0a3b1
N
218/*
219 * convert a major/minor pair for a block device into a name in /dev, if possible.
220 * On the first call, walk /dev collecting name.
221 * Put them in a simple linked listfor now.
222 */
223struct devmap {
5d500228
N
224 int major, minor;
225 char *name;
226 struct devmap *next;
78c0a3b1
N
227} *devlist = NULL;
228int devlist_ready = 0;
229
230int add_dev(const char *name, const struct stat *stb, int flag, struct FTW *s)
231{
232 struct stat st;
233
234 if (S_ISLNK(stb->st_mode)) {
235 if (stat(name, &st) != 0)
236 return 0;
237 stb = &st;
238 }
239
240 if ((stb->st_mode&S_IFMT)== S_IFBLK) {
503975b9
N
241 char *n = xstrdup(name);
242 struct devmap *dm = xmalloc(sizeof(*dm));
ad7ac9ac
JS
243 if (strncmp(n, "/dev/./", 7) == 0)
244 strcpy(n + 4, name + 6);
78c0a3b1
N
245 if (dm) {
246 dm->major = major(stb->st_rdev);
247 dm->minor = minor(stb->st_rdev);
248 dm->name = n;
249 dm->next = devlist;
250 devlist = dm;
251 }
252 }
ad7ac9ac 253
78c0a3b1
N
254 return 0;
255}
256
257#ifndef HAVE_NFTW
258#ifdef HAVE_FTW
259int add_dev_1(const char *name, const struct stat *stb, int flag)
260{
261 return add_dev(name, stb, flag, NULL);
262}
ad7ac9ac
JS
263int nftw(const char *path,
264 int (*han)(const char *name, const struct stat *stb,
265 int flag, struct FTW *s), int nopenfd, int flags)
78c0a3b1
N
266{
267 return ftw(path, add_dev_1, nopenfd);
268}
269#else
ad7ac9ac
JS
270int nftw(const char *path,
271 int (*han)(const char *name, const struct stat *stb,
272 int flag, struct FTW *s), int nopenfd, int flags)
78c0a3b1
N
273{
274 return 0;
275}
276#endif /* HAVE_FTW */
277#endif /* HAVE_NFTW */
278
279/*
280 * Find a block device with the right major/minor number.
281 * If we find multiple names, choose the shortest.
282 * If we find a name in /dev/md/, we prefer that.
283 * This applies only to names for MD devices.
c2ecf5f6
N
284 * If 'prefer' is set (normally to e.g. /by-path/)
285 * then we prefer a name which contains that string.
78c0a3b1 286 */
c2ecf5f6
N
287char *map_dev_preferred(int major, int minor, int create,
288 char *prefer)
78c0a3b1
N
289{
290 struct devmap *p;
291 char *regular = NULL, *preferred=NULL;
292 int did_check = 0;
293
294 if (major == 0 && minor == 0)
781f7efb 295 return NULL;
78c0a3b1
N
296
297 retry:
298 if (!devlist_ready) {
299 char *dev = "/dev";
300 struct stat stb;
301 while(devlist) {
302 struct devmap *d = devlist;
303 devlist = d->next;
304 free(d->name);
305 free(d);
306 }
ad7ac9ac 307 if (lstat(dev, &stb) == 0 && S_ISLNK(stb.st_mode))
78c0a3b1
N
308 dev = "/dev/.";
309 nftw(dev, add_dev, 10, FTW_PHYS);
310 devlist_ready=1;
311 did_check = 1;
312 }
313
ad7ac9ac
JS
314 for (p = devlist; p; p = p->next)
315 if (p->major == major && p->minor == minor) {
b9ce7ab0 316 if (strncmp(p->name, DEV_MD_DIR, DEV_MD_DIR_LEN) == 0 ||
ad7ac9ac 317 (prefer && strstr(p->name, prefer))) {
78c0a3b1
N
318 if (preferred == NULL ||
319 strlen(p->name) < strlen(preferred))
320 preferred = p->name;
321 } else {
322 if (regular == NULL ||
323 strlen(p->name) < strlen(regular))
324 regular = p->name;
325 }
326 }
327 if (!regular && !preferred && !did_check) {
328 devlist_ready = 0;
329 goto retry;
330 }
331 if (create && !regular && !preferred) {
332 static char buf[30];
333 snprintf(buf, sizeof(buf), "%d:%d", major, minor);
334 regular = buf;
335 }
336
337 return preferred ? preferred : regular;
338}
339
78c0a3b1
N
340/* conf_word gets one word from the conf file.
341 * if "allow_key", then accept words at the start of a line,
342 * otherwise stop when such a word is found.
343 * We assume that the file pointer is at the end of a word, so the
344 * next character is a space, or a newline. If not, it is the start of a line.
345 */
346
347char *conf_word(FILE *file, int allow_key)
348{
349 int wsize = 100;
350 int len = 0;
351 int c;
352 int quote;
353 int wordfound = 0;
503975b9 354 char *word = xmalloc(wsize);
78c0a3b1 355
ad7ac9ac 356 while (wordfound == 0) {
78c0a3b1
N
357 /* at the end of a word.. */
358 c = getc(file);
359 if (c == '#')
360 while (c != EOF && c != '\n')
361 c = getc(file);
ad7ac9ac
JS
362 if (c == EOF)
363 break;
364 if (c == '\n')
365 continue;
78c0a3b1
N
366
367 if (c != ' ' && c != '\t' && ! allow_key) {
368 ungetc(c, file);
369 break;
370 }
371 /* looks like it is safe to get a word here, if there is one */
372 quote = 0;
373 /* first, skip any spaces */
374 while (c == ' ' || c == '\t')
375 c = getc(file);
376 if (c != EOF && c != '\n' && c != '#') {
377 /* we really have a character of a word, so start saving it */
ad7ac9ac
JS
378 while (c != EOF && c != '\n' &&
379 (quote || (c != ' ' && c != '\t'))) {
78c0a3b1 380 wordfound = 1;
ad7ac9ac
JS
381 if (quote && c == quote)
382 quote = 0;
78c0a3b1
N
383 else if (quote == 0 && (c == '\'' || c == '"'))
384 quote = c;
385 else {
386 if (len == wsize-1) {
387 wsize += 100;
503975b9 388 word = xrealloc(word, wsize);
78c0a3b1
N
389 }
390 word[len++] = c;
391 }
392 c = getc(file);
393 /* Hack for broken kernels (2.6.14-.24) that put
394 * "active(auto-read-only)"
395 * in /proc/mdstat instead of
396 * "active (auto-read-only)"
397 */
ad7ac9ac
JS
398 if (c == '(' && len >= 6 &&
399 strncmp(word + len - 6, "active", 6) == 0)
78c0a3b1
N
400 c = ' ';
401 }
402 }
ad7ac9ac
JS
403 if (c != EOF)
404 ungetc(c, file);
78c0a3b1
N
405 }
406 word[len] = 0;
407
408 /* Further HACK for broken kernels.. 2.6.14-2.6.24 */
409 if (strcmp(word, "auto-read-only)") == 0)
410 strcpy(word, "(auto-read-only)");
411
412/* printf("word is <%s>\n", word); */
413 if (!wordfound) {
414 free(word);
415 word = NULL;
416 }
417 return word;
418}
7103b9b8
N
419
420void print_quoted(char *str)
421{
422 /* Printf the string with surrounding quotes
423 * iff needed.
424 * If no space, tab, or quote - leave unchanged.
425 * Else print surrounded by " or ', swapping quotes
426 * when we find one that will cause confusion.
427 */
428
429 char first_quote = 0, q;
430 char *c;
431
432 for (c = str; *c; c++) {
433 switch(*c) {
434 case '\'':
435 case '"':
436 first_quote = *c;
437 break;
438 case ' ':
439 case '\t':
440 first_quote = *c;
441 continue;
442 default:
443 continue;
444 }
445 break;
446 }
447 if (!first_quote) {
448 printf("%s", str);
449 return;
450 }
451
452 if (first_quote == '"')
453 q = '\'';
454 else
455 q = '"';
456 putchar(q);
457 for (c = str; *c; c++) {
458 if (*c == q) {
459 putchar(q);
460 q ^= '"' ^ '\'';
461 putchar(q);
462 }
463 putchar(*c);
464 }
465 putchar(q);
466}
467
468void print_escape(char *str)
469{
470 /* print str, but change space and tab to '_'
471 * as is suitable for device names
472 */
ad7ac9ac 473 for (; *str; str++) {
7103b9b8
N
474 switch (*str) {
475 case ' ':
476 case '\t':
477 putchar('_');
478 break;
479 case '/':
480 putchar('-');
481 break;
482 default:
483 putchar(*str);
484 }
485 }
486}
06d2ffc3 487
f8fcf7a1
BS
488int check_env(char *name)
489{
490 char *val = getenv(name);
491
492 if (val && atoi(val) == 1)
493 return 1;
494
495 return 0;
496}
497
06d2ffc3
N
498int use_udev(void)
499{
500 static int use = -1;
501 struct stat stb;
502
503 if (use < 0) {
ad7ac9ac
JS
504 use = ((stat("/dev/.udev", &stb) == 0 ||
505 stat("/run/udev", &stb) == 0) &&
506 check_env("MDADM_NO_UDEV") == 0);
06d2ffc3
N
507 }
508 return use;
509}
2eba8496
N
510
511unsigned long GCD(unsigned long a, unsigned long b)
512{
513 while (a != b) {
514 if (a < b)
515 b -= a;
516 if (b < a)
517 a -= b;
518 }
519 return a;
520}
d0c017a6
N
521
522/*
523 * conf_line reads one logical line from the conffile or mdstat.
524 * It skips comments and continues until it finds a line that starts
525 * with a non blank/comment. This character is pushed back for the next call
526 * A doubly linked list of words is returned.
527 * the first word will be a keyword. Other words will have had quotes removed.
528 */
529
530char *conf_line(FILE *file)
531{
532 char *w;
533 char *list;
534
535 w = conf_word(file, 1);
781f7efb
JS
536 if (w == NULL)
537 return NULL;
d0c017a6
N
538
539 list = dl_strdup(w);
540 free(w);
541 dl_init(list);
542
ad7ac9ac 543 while ((w = conf_word(file, 0))){
d0c017a6
N
544 char *w2 = dl_strdup(w);
545 free(w);
546 dl_add(list, w2);
547 }
548/* printf("got a line\n");*/
549 return list;
550}
551
552void free_line(char *line)
553{
554 char *w;
ad7ac9ac 555 for (w = dl_next(line); w != line; w = dl_next(line)) {
d0c017a6
N
556 dl_del(w);
557 dl_free(w);
558 }
559 dl_free(line);
560}
60815698
MG
561
562/**
563 * parse_num() - Parse int from string.
564 * @dest: Pointer to destination.
565 * @num: Pointer to string that is going to be parsed.
566 *
567 * If string contains anything after a number, error code is returned.
568 * The same happens when number is bigger than INT_MAX or smaller than 0.
569 * Writes to destination only if successfully read the number.
570 *
571 * Return: 0 on success, 1 otherwise.
572 */
25aa7329 573int parse_num(int *dest, const char *num)
60815698
MG
574{
575 char *c = NULL;
576 long temp;
577
578 if (!num)
579 return 1;
580
581 errno = 0;
582 temp = strtol(num, &c, 10);
583 if (temp < 0 || temp > INT_MAX || *c || errno != 0 || num == c)
584 return 1;
585 *dest = temp;
586 return 0;
587}
21e622f2
BK
588
589/**
590 * s_gethostname() - secure get hostname. Assure null-terminated string.
591 *
592 * @buf: buffer for hostname.
593 * @buf_len: buffer length.
594 *
595 * Return: gethostname() result.
596 */
597int s_gethostname(char *buf, int buf_len)
598{
599 assert(buf);
600
601 int ret = gethostname(buf, buf_len);
602
603 buf[buf_len - 1] = 0;
604
605 return ret;
606}