2 * collectd - src/utils_mount.c
3 * Copyright (C) 2005 Niki W. Waibel
5 * This program is free software; you can redistribute it and/
6 * or modify it under the terms of the GNU General Public Li-
7 * cence as published by the Free Software Foundation; either
8 * version 2 of the Licence, or any later version.
10 * This program is distributed in the hope that it will be use-
11 * ful, but WITHOUT ANY WARRANTY; without even the implied war-
12 * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public Licence for more details.
15 * You should have received a copy of the GNU General Public
16 * Licence along with this program; if not, write to the Free
17 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
21 * Niki W. Waibel <niki.waibel@gmx.net>
29 #define XFS_SUPER_MAGIC_STR "XFSB"
30 #define XFS_SUPER_MAGIC2_STR "BSFX"
32 #include "utils_debug.h"
33 #include "utils_mount.h"
37 /* *** *** *** ********************************************* *** *** *** */
38 /* *** *** *** *** *** *** private functions *** *** *** *** *** *** */
39 /* *** *** *** ********************************************* *** *** *** */
43 /* stolen from quota-3.13 (quota-tools) */
45 #define PROC_PARTITIONS "/proc/partitions"
46 #define DEVLABELDIR "/dev"
50 static struct uuidCache_s {
51 struct uuidCache_s *next;
57 #define EXT2_SUPER_MAGIC 0xEF53
58 struct ext2_super_block {
59 unsigned char s_dummy1[56];
60 unsigned char s_magic[2];
61 unsigned char s_dummy2[46];
62 unsigned char s_uuid[16];
63 char s_volume_name[16];
65 #define ext2magic(s) ((unsigned int)s.s_magic[0] \
66 + (((unsigned int)s.s_magic[1]) << 8))
69 struct xfs_super_block {
70 unsigned char s_magic[4];
71 unsigned char s_dummy[28];
72 unsigned char s_uuid[16];
73 unsigned char s_dummy2[60];
76 #endif /* HAVE_XFS_XQM_H */
78 #define REISER_SUPER_MAGIC "ReIsEr2Fs"
79 struct reiserfs_super_block {
80 unsigned char s_dummy1[52];
81 unsigned char s_magic[10];
82 unsigned char s_dummy2[22];
83 unsigned char s_uuid[16];
84 char s_volume_name[16];
87 /* for now, only ext2 and xfs are supported */
89 get_label_uuid(const char *device, char **label, char *uuid)
91 /* start with ext2 and xfs tests, taken from mount_guess_fstype */
92 /* should merge these later */
95 struct ext2_super_block e2sb;
97 struct xfs_super_block xfsb;
99 struct reiserfs_super_block reisersb;
101 fd = open(device, O_RDONLY);
106 if(lseek(fd, 1024, SEEK_SET) == 1024
107 && read(fd, (char *)&e2sb, sizeof(e2sb)) == sizeof(e2sb)
108 && ext2magic(e2sb) == EXT2_SUPER_MAGIC) {
109 memcpy(uuid, e2sb.s_uuid, sizeof(e2sb.s_uuid));
110 namesize = sizeof(e2sb.s_volume_name);
111 *label = smalloc(namesize + 1);
112 sstrncpy(*label, e2sb.s_volume_name, namesize);
115 } else if(lseek(fd, 0, SEEK_SET) == 0
116 && read(fd, (char *)&xfsb, sizeof(xfsb)) == sizeof(xfsb)
117 && (strncmp((char *)&xfsb.s_magic, XFS_SUPER_MAGIC_STR, 4) == 0 ||
118 strncmp((char *)&xfsb.s_magic, XFS_SUPER_MAGIC2_STR, 4) == 0)) {
119 memcpy(uuid, xfsb.s_uuid, sizeof(xfsb.s_uuid));
120 namesize = sizeof(xfsb.s_fsname);
121 *label = smalloc(namesize + 1);
122 sstrncpy(*label, xfsb.s_fsname, namesize);
124 #endif /* HAVE_XFS_XQM_H */
125 } else if(lseek(fd, 65536, SEEK_SET) == 65536
126 && read(fd, (char *)&reisersb, sizeof(reisersb)) == sizeof(reisersb)
127 && !strncmp((char *)&reisersb.s_magic, REISER_SUPER_MAGIC, 9)) {
128 memcpy(uuid, reisersb.s_uuid, sizeof(reisersb.s_uuid));
129 namesize = sizeof(reisersb.s_volume_name);
130 *label = smalloc(namesize + 1);
131 sstrncpy(*label, reisersb.s_volume_name, namesize);
139 uuidcache_addentry(char *device, char *label, char *uuid)
141 struct uuidCache_s *last;
144 last = uuidCache = smalloc(sizeof(*uuidCache));
146 for(last = uuidCache; last->next; last = last->next);
147 last->next = smalloc(sizeof(*uuidCache));
151 last->device = device;
153 memcpy(last->uuid, uuid, sizeof(last->uuid));
162 static char ptname[100];
164 char uuid[16], *label = NULL;
173 procpt = fopen(PROC_PARTITIONS, "r");
178 for(firstPass = 1; firstPass >= 0; firstPass--) {
179 fseek(procpt, 0, SEEK_SET);
180 while(fgets(line, sizeof(line), procpt)) {
181 if(sscanf(line, " %d %d %d %[^\n ]",
182 &ma, &mi, &sz, ptname) != 4)
187 /* skip extended partitions (heuristic: size 1) */
192 /* look only at md devices on first pass */
193 handleOnFirst = !strncmp(ptname, "md", 2);
194 if(firstPass != handleOnFirst) {
198 /* skip entire disk (minor 0, 64, ... on ide;
200 /* heuristic: partition name ends in a digit */
202 for(s = ptname; *s; s++);
204 if(isdigit((int)s[-1])) {
206 * Note: this is a heuristic only - there is no reason
207 * why these devices should live in /dev.
208 * Perhaps this directory should be specifiable by option.
209 * One might for example have /devlabel with links to /dev
210 * for the devices that may be accessed in this way.
211 * (This is useful, if the cdrom on /dev/hdc must not
214 snprintf(device, sizeof(device), "%s/%s",
215 DEVLABELDIR, ptname);
216 if(!get_label_uuid(device, &label, uuid)) {
217 uuidcache_addentry(sstrdup(device),
229 if(isdigit((int)c)) {
231 } else if(islower((int)c)) {
232 return (c - 'a' + 10);
234 return (c - 'A' + 10);
239 get_spec_by_x(int n, const char *t)
241 struct uuidCache_s *uc;
249 if(!memcmp(t, uc->uuid, sizeof(uc->uuid))) {
250 return sstrdup(uc->device);
254 if(!strcmp(t, uc->label)) {
255 return sstrdup(uc->device);
265 get_spec_by_uuid(const char *s)
271 || s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-') {
275 for(i=0; i<16; i++) {
279 if(!isxdigit((int)s[0]) || !isxdigit((int)s[1])) {
282 uuid[i] = ((fromhex(s[0]) << 4) | fromhex(s[1]));
285 return get_spec_by_x(UUID, uuid);
288 DBG("Found an invalid UUID: %s", s);
293 get_spec_by_volume_label(const char *s)
295 return get_spec_by_x(VOL, s);
299 get_device_name(const char *item)
303 if(!strncmp(item, "UUID=", 5)) {
304 DBG("TODO: check UUID= code!");
305 rc = get_spec_by_uuid(item + 5);
306 } else if(!strncmp(item, "LABEL=", 6)) {
307 DBG("TODO: check LABEL= code!");
308 rc = get_spec_by_volume_label(item + 6);
313 DBG("Error checking device name: %s", item);
322 cu_mount_getvfsmnt(FILE *mntf, cu_mount_t **list)
324 DBG("TODO: getvfsmnt");
327 #endif /* HAVE_GETVFSENT */
333 cu_mount_listmntent(struct tabmntent *mntlist, cu_mount_t **list)
335 cu_mount_t *last = *list;
339 for(p = mntlist; p; p = p->next) {
340 char *loop = NULL, *device = NULL;
343 loop = cu_mount_getoptionvalue(mnt->mnt_opts, "loop=");
344 if(loop == NULL) { /* no loop= mount */
345 device = get_device_name(mnt->mnt_fsname);
347 DBG("can't get devicename for fs (%s) %s (%s)"
348 ": ignored", mnt->mnt_type,
349 mnt->mnt_dir, mnt->mnt_fsname);
356 *list = (cu_mount_t *)smalloc(sizeof(cu_mount_t));
359 while(last->next != NULL) { /* is last really last? */
362 last->next = (cu_mount_t *)smalloc(sizeof(cu_mount_t));
365 last->dir = sstrdup(mnt->mnt_dir);
366 last->spec_device = sstrdup(mnt->mnt_fsname);
367 last->device = device;
368 last->type = sstrdup(mnt->mnt_type);
369 last->options = sstrdup(mnt->mnt_opts);
371 } /* for(p = mntlist; p; p = p->next) */
374 } /* static cu_mount_t *cu_mount_listmntent(struct tabmntent *mntlist,
375 cu_mount_t **list) */
376 #endif /* HAVE_LISTMNTENT */
382 cu_mount_getmntent(FILE *mntf, cu_mount_t **list)
384 cu_mount_t *last = *list;
386 struct mntent *mnt = NULL;
389 struct mntent real_mnt;
390 struct mntent *mnt = &real_mnt;
394 while((mnt = getmntent(mntf)) != NULL) {
397 while(getmntent(mntf, &real_mnt) == 0) {
399 char *loop = NULL, *device = NULL;
402 DBG("------------------ BEGIN");
403 DBG("mnt->mnt_fsname %s", mnt->mnt_fsname);
404 DBG("mnt->mnt_dir %s", mnt->mnt_dir);
405 DBG("mnt->mnt_type %s", mnt->mnt_type);
406 DBG("mnt->mnt_opts %s", mnt->mnt_opts);
407 DBG("mnt->mnt_freq %d", mnt->mnt_freq);
408 DBG("mnt->mnt_passno %d", mnt->mnt_passno);
411 loop = cu_mount_getoptionvalue(mnt->mnt_opts, "loop=");
412 if(loop == NULL) { /* no loop= mount */
413 device = get_device_name(mnt->mnt_fsname);
415 DBG("can't get devicename for fs (%s) %s (%s)"
416 ": ignored", mnt->mnt_type,
417 mnt->mnt_dir, mnt->mnt_fsname);
425 DBG("device: %s", device);
426 DBG("------------------ END");
429 *list = (cu_mount_t *)smalloc(sizeof(cu_mount_t));
432 while(last->next != NULL) { /* is last really last? */
435 last->next = (cu_mount_t *)smalloc(sizeof(cu_mount_t));
438 last->dir = sstrdup(mnt->mnt_dir);
439 last->spec_device = sstrdup(mnt->mnt_fsname);
440 last->device = device;
441 last->type = sstrdup(mnt->mnt_type);
442 last->options = sstrdup(mnt->mnt_opts);
445 } /* while(getmntent(mntf, &real_mnt) == 0) */
448 } /* while((mnt = getmntent(mntf)) != NULL) */
452 } /* static cu_mount_t *cu_mount_getmntent(FILE *mntf, cu_mount_t **list) */
453 #endif /* HAVE_GETMNTENT */
457 /* *** *** *** ******************************************** *** *** *** */
458 /* *** *** *** *** *** *** public functions *** *** *** *** *** *** */
459 /* *** *** *** ******************************************** *** *** *** */
464 cu_mount_getlist(cu_mount_t **list)
466 cu_mount_t *last = NULL;
468 /* see lib/mountlist.c of coreutils for all (ugly) details! */
471 there are two implementations of getmntent():
472 * one argument getmntent:
473 FILE *setmntent(const char *filename, const char *type);
474 struct mntent *getmntent(FILE *fp);
475 int endmntent(FILE *fp);
476 * two argument getmntent:
477 FILE *fopen(const char *path, const char *mode);
478 int getmntent(FILE *fp, struct mnttab *mnt);
479 int fclose(FILE *fp);
480 and a third (linux/gnu style) version called getmntent_r, which is not used
481 here (enough trouble with the two versions above).
485 # define setmntent setmntent
486 # define endmntent endmntent
489 # define setmntent fopen
490 # define endmntent fclose
492 # error HAVE_GETMNTENT defined, but neither HAVE_GETMNTENT1 nor HAVE_GETMNTENT2
493 # endif /* HAVE_GETMNTENT2 */
494 # endif /* HAVE_GETMNTENT1 */
495 #endif /* HAVE_GETMNTENT */
497 /* the indentation is wrong. is there a better way to do this? */
499 #if HAVE_GETMNTENT && defined(_PATH_MOUNTED)
502 if((mntf = setmntent(_PATH_MOUNTED, "r")) == NULL) {
503 DBG("opening %s failed: %s", _PATH_MOUNTED, strerror(errno));
505 #if HAVE_GETMNTENT && defined(MNT_MNTTAB)
508 if((mntf = setmntent(MNT_MNTTAB, "r")) == NULL) {
509 DBG("opening %s failed: %s", MNT_MNTTAB, strerror(errno));
511 #if HAVE_GETMNTENT && defined(MNTTABNAME)
514 if((mntf = setmntent(MNTTABNAME, "r")) == NULL) {
515 DBG("opening %s failed: %s", MNTTABNAME, strerror(errno));
519 struct tabmntent *mntlist;
520 if(listmntent(&mntlist, KMTAB, NULL, NULL) < 0) {
521 DBG("calling listmntent() failed: %s", strerror(errno));
523 #if HAVE_GETVFSENT && defined(VFSTAB)
524 /* this is as bad as the next one, read next comment */
527 if((mntf = fopen(VFSTAB, "r")) == NULL) {
528 DBG("opening %s failed: %s", VFSTAB, strerror(errno));
530 #if HAVE_GETMNTENT && defined(_PATH_MNTTAB)
531 /* _PATH_MNTTAB is usually /etc/fstab and so this should be really
532 the very last thing to try, because it does not provide a list
533 of currently mounted filesystems... */
536 if((mntf = setmntent(_PATH_MNTTAB, "r")) == NULL) {
537 DBG("opening %s failed: %s", _PATH_MNTTAB, strerror(errno));
541 DBG("failed get local mountpoints");
544 #if HAVE_GETMNTENT && defined(_PATH_MNTTAB)
545 } else { last = cu_mount_getmntent(mntf, list); }
546 (void)endmntent(mntf);
549 #if HAVE_GETVFSENT && defined(VFSTAB)
550 } else { last = cu_mount_getvfsmnt(mntf, list); }
555 } else { last = cu_mount_listmntent(mntlist, list); }
556 freemntlist(mntlist);
559 #if HAVE_GETMNTENT && defined(MNTTABNAME)
560 } else { last = cu_mount_getmntent(mntf, list); }
561 (void)endmntent(mntf);
564 #if HAVE_GETMNTENT && defined(MNT_MNTTAB)
565 } else { last = cu_mount_getmntent(mntf, list); }
566 (void)endmntent(mntf);
569 #if HAVE_GETMNTENT && defined(_PATH_MOUNTED)
570 } else { last = cu_mount_getmntent(mntf, list); }
571 (void)endmntent(mntf);
575 } /* cu_mount_t *cu_mount_getlist(cu_mount_t **list) */
580 cu_mount_freelist(cu_mount_t *list)
582 cu_mount_t *l = list, *p = NULL;
585 while(l->next != NULL) {
593 sfree(l->spec_device);
603 l = NULL; /* done by sfree already */
605 } /* while(l != NULL) */
606 } /* void cu_mount_freelist(cu_mount_t *list) */
611 cu_mount_checkoption(char *line, char *keyword, int full)
614 int l = strlen(keyword);
617 if(line == NULL || keyword == NULL) {
624 line2 = sstrdup(line);
634 p2 = strchr(line, ',');
636 if(strncmp(line2+(p1-line)+1, keyword, l+full) == 0) {
642 p2 = strchr(p1+1, ',');
648 } /* char *cu_mount_checkoption(char *line, char *keyword, int full) */
653 cu_mount_getoptionvalue(char *line, char *keyword)
657 r = cu_mount_checkoption(line, keyword, 0);
660 r += strlen(keyword);
672 m = (char *)smalloc(p-r+1);
673 sstrncpy(m, r, p-r+1);
678 } /* char *cu_mount_getoptionvalue(char *line, char *keyword) */
683 cu_mount_type(const char *type)
685 if(strcmp(type, "ext3") == 0) return CUMT_EXT3;
686 if(strcmp(type, "ext2") == 0) return CUMT_EXT2;
687 if(strcmp(type, "ufs") == 0) return CUMT_UFS;
688 if(strcmp(type, "vxfs") == 0) return CUMT_VXFS;
689 if(strcmp(type, "zfs") == 0) return CUMT_ZFS;
691 } /* int cu_mount_type(const char *type) */