]> git.ipfire.org Git - thirdparty/mdadm.git/blob - lib.c
mdmon: don't test both 'all' and 'container_name'.
[thirdparty/mdadm.git] / lib.c
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"
26 #include "dlink.h"
27 #include <ctype.h>
28 #include <limits.h>
29
30 bool 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
41 /* This fill contains various 'library' style function. They
42 * have no dependency on anything outside this file.
43 */
44
45 int get_mdp_major(void)
46 {
47 static int mdp_major = -1;
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;
56
57 fl = fopen("/proc/devices", "r");
58 if (!fl)
59 return -1;
60
61 while ((w = conf_word(fl, 1))) {
62 if (have_block && strcmp(w, "devices:") == 0)
63 have_devices = 1;
64 have_block = (strcmp(w, "Block") == 0);
65 if (isdigit(w[0]))
66 last_num = atoi(w);
67 if (have_devices && strcmp(w, "mdp") == 0)
68 mdp_major = last_num;
69 free(w);
70 }
71 fclose(fl);
72
73 return mdp_major;
74 }
75
76 char *devid2kname(dev_t devid)
77 {
78 char path[30];
79 char link[PATH_MAX];
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 */
88 sprintf(path, "/sys/dev/block/%d:%d", major(devid), minor(devid));
89 n = readlink(path, link, sizeof(link) - 1);
90 if (n > 0) {
91 link[n] = 0;
92 cp = strrchr(link, '/');
93 if (cp) {
94 strcpy(devnm, cp + 1);
95 return devnm;
96 }
97 }
98 return NULL;
99 }
100
101 char *stat2kname(struct stat *st)
102 {
103 if ((S_IFMT & st->st_mode) != S_IFBLK)
104 return NULL;
105
106 return devid2kname(st->st_rdev);
107 }
108
109 char *fd2kname(int fd)
110 {
111 struct stat stb;
112
113 if (fstat(fd, &stb) == 0)
114 return stat2kname(&stb);
115
116 return NULL;
117 }
118
119 char *devid2devnm(dev_t devid)
120 {
121 char path[30];
122 char link[200];
123 static char devnm[32];
124 char *cp, *ep;
125 int n;
126
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 */
134 sprintf(path, "/sys/dev/block/%d:%d", major(devid), minor(devid));
135 n = readlink(path, link, sizeof(link) - 1);
136 if (n > 0) {
137 link[n] = 0;
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 }
147 }
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;
155
156 return devnm;
157 }
158
159 char *stat2devnm(struct stat *st)
160 {
161 if ((S_IFMT & st->st_mode) != S_IFBLK)
162 return NULL;
163
164 return devid2devnm(st->st_rdev);
165 }
166
167 bool 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
179 char *fd2devnm(int fd)
180 {
181 struct stat stb;
182
183 if (fstat(fd, &stb) == 0)
184 return stat2devnm(&stb);
185
186 return NULL;
187 }
188
189 /* When we create a new array, we don't want the content to
190 * be immediately examined by udev - it is probably meaningless.
191 * So create /run/mdadm/creating-mdXXX and expect that a udev
192 * rule will noticed this and act accordingly.
193 */
194 static char block_path[] = "/run/mdadm/creating-%s";
195 static char *unblock_path = NULL;
196 void 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
210 void udev_unblock(void)
211 {
212 if (unblock_path)
213 unlink(unblock_path);
214 free(unblock_path);
215 unblock_path = NULL;
216 }
217
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 */
223 struct devmap {
224 int major, minor;
225 char *name;
226 struct devmap *next;
227 } *devlist = NULL;
228 int devlist_ready = 0;
229
230 int 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) {
241 char *n = xstrdup(name);
242 struct devmap *dm = xmalloc(sizeof(*dm));
243 if (strncmp(n, "/dev/./", 7) == 0)
244 strcpy(n + 4, name + 6);
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 }
253
254 return 0;
255 }
256
257 #ifndef HAVE_NFTW
258 #ifdef HAVE_FTW
259 int add_dev_1(const char *name, const struct stat *stb, int flag)
260 {
261 return add_dev(name, stb, flag, NULL);
262 }
263 int nftw(const char *path,
264 int (*han)(const char *name, const struct stat *stb,
265 int flag, struct FTW *s), int nopenfd, int flags)
266 {
267 return ftw(path, add_dev_1, nopenfd);
268 }
269 #else
270 int nftw(const char *path,
271 int (*han)(const char *name, const struct stat *stb,
272 int flag, struct FTW *s), int nopenfd, int flags)
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.
284 * If 'prefer' is set (normally to e.g. /by-path/)
285 * then we prefer a name which contains that string.
286 */
287 char *map_dev_preferred(int major, int minor, int create,
288 char *prefer)
289 {
290 struct devmap *p;
291 char *regular = NULL, *preferred=NULL;
292 int did_check = 0;
293
294 if (major == 0 && minor == 0)
295 return NULL;
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 }
307 if (lstat(dev, &stb) == 0 && S_ISLNK(stb.st_mode))
308 dev = "/dev/.";
309 nftw(dev, add_dev, 10, FTW_PHYS);
310 devlist_ready=1;
311 did_check = 1;
312 }
313
314 for (p = devlist; p; p = p->next)
315 if (p->major == major && p->minor == minor) {
316 if (strncmp(p->name, "/dev/md/",8) == 0 ||
317 (prefer && strstr(p->name, prefer))) {
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
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
347 char *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;
354 char *word = xmalloc(wsize);
355
356 while (wordfound == 0) {
357 /* at the end of a word.. */
358 c = getc(file);
359 if (c == '#')
360 while (c != EOF && c != '\n')
361 c = getc(file);
362 if (c == EOF)
363 break;
364 if (c == '\n')
365 continue;
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 */
378 while (c != EOF && c != '\n' &&
379 (quote || (c != ' ' && c != '\t'))) {
380 wordfound = 1;
381 if (quote && c == quote)
382 quote = 0;
383 else if (quote == 0 && (c == '\'' || c == '"'))
384 quote = c;
385 else {
386 if (len == wsize-1) {
387 wsize += 100;
388 word = xrealloc(word, wsize);
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 */
398 if (c == '(' && len >= 6 &&
399 strncmp(word + len - 6, "active", 6) == 0)
400 c = ' ';
401 }
402 }
403 if (c != EOF)
404 ungetc(c, file);
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 }
419
420 void 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
468 void print_escape(char *str)
469 {
470 /* print str, but change space and tab to '_'
471 * as is suitable for device names
472 */
473 for (; *str; str++) {
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 }
487
488 int 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
498 int use_udev(void)
499 {
500 static int use = -1;
501 struct stat stb;
502
503 if (use < 0) {
504 use = ((stat("/dev/.udev", &stb) == 0 ||
505 stat("/run/udev", &stb) == 0) &&
506 check_env("MDADM_NO_UDEV") == 0);
507 }
508 return use;
509 }
510
511 unsigned 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 }
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
530 char *conf_line(FILE *file)
531 {
532 char *w;
533 char *list;
534
535 w = conf_word(file, 1);
536 if (w == NULL)
537 return NULL;
538
539 list = dl_strdup(w);
540 free(w);
541 dl_init(list);
542
543 while ((w = conf_word(file, 0))){
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
552 void free_line(char *line)
553 {
554 char *w;
555 for (w = dl_next(line); w != line; w = dl_next(line)) {
556 dl_del(w);
557 dl_free(w);
558 }
559 dl_free(line);
560 }
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 */
573 int parse_num(int *dest, char *num)
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 }