Merged trunk... huh...
[collectd.git] / src / utils_mount.c
1 /**
2  * collectd - src/utils_mount.c
3  * Copyright (C) 2005  Niki W. Waibel
4  *
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.
9  *
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.
14  *
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,
18  * USA.
19  *
20  * Author:
21  *   Niki W. Waibel <niki.waibel@gmx.net>
22 **/
23
24
25
26 #include "common.h"
27 #if HAVE_XFS_XQM_H
28 # include <xfs/xqm.h>
29 #define xfs_mem_dqinfo  fs_quota_stat
30 #define Q_XFS_GETQSTAT  Q_XGETQSTAT
31 #define XFS_SUPER_MAGIC_STR "XFSB"
32 #define XFS_SUPER_MAGIC2_STR "BSFX"
33 #endif
34 #include "utils_debug.h"
35 #include "utils_mount.h"
36
37
38
39 /* *** *** ***   local functions   *** *** *** */
40
41
42
43 /* stolen from quota-3.13 (quota-tools) */
44
45 #define PROC_PARTITIONS "/proc/partitions"
46 #define DEVLABELDIR     "/dev"
47 #define UUID   1
48 #define VOL    2
49
50 #define AUTOFS_DIR_MAX 64       /* Maximum number of autofs directories */
51
52 static struct uuidCache_s {
53         struct uuidCache_s *next;
54         char uuid[16];
55         char *label;
56         char *device;
57 } *uuidCache = NULL;
58
59 #define EXT2_SUPER_MAGIC 0xEF53
60 struct ext2_super_block {
61         unsigned char s_dummy1[56];
62         unsigned char s_magic[2];
63         unsigned char s_dummy2[46];
64         unsigned char s_uuid[16];
65         char s_volume_name[16];
66 };
67 #define ext2magic(s) ((unsigned int)s.s_magic[0] \
68         + (((unsigned int)s.s_magic[1]) << 8))
69
70 #if HAVE_XFS_XQM_H
71 struct xfs_super_block {
72         unsigned char s_magic[4];
73         unsigned char s_dummy[28];
74         unsigned char s_uuid[16];
75         unsigned char s_dummy2[60];
76         char s_fsname[12];
77 };
78 #endif /* HAVE_XFS_XQM_H */
79
80 #define REISER_SUPER_MAGIC "ReIsEr2Fs"
81 struct reiserfs_super_block {
82         unsigned char s_dummy1[52];
83         unsigned char s_magic[10];
84         unsigned char s_dummy2[22];
85         unsigned char s_uuid[16];
86         char s_volume_name[16];
87 };
88
89 /* for now, only ext2 and xfs are supported */
90 static int
91 get_label_uuid(const char *device, char **label, char *uuid)
92 {
93         /* start with ext2 and xfs tests, taken from mount_guess_fstype */
94         /* should merge these later */
95         int fd, rv = 1;
96         size_t namesize;
97         struct ext2_super_block e2sb;
98 #if HAVE_XFS_XQM_H
99         struct xfs_super_block xfsb;
100 #endif
101         struct reiserfs_super_block reisersb;
102
103         fd = open(device, O_RDONLY);
104         if(fd == -1) {
105                 return rv;
106         }
107
108         if(lseek(fd, 1024, SEEK_SET) == 1024
109         && read(fd, (char *)&e2sb, sizeof(e2sb)) == sizeof(e2sb)
110         && ext2magic(e2sb) == EXT2_SUPER_MAGIC) {
111                 memcpy(uuid, e2sb.s_uuid, sizeof(e2sb.s_uuid));
112                 namesize = sizeof(e2sb.s_volume_name);
113                 *label = smalloc(namesize + 1);
114                 sstrncpy(*label, e2sb.s_volume_name, namesize);
115                 rv = 0;
116 #if HAVE_XFS_XQM_H
117         } else if(lseek(fd, 0, SEEK_SET) == 0
118         && read(fd, (char *)&xfsb, sizeof(xfsb)) == sizeof(xfsb)
119         && (strncmp((char *)&xfsb.s_magic, XFS_SUPER_MAGIC_STR, 4) == 0 ||
120         strncmp((char *)&xfsb.s_magic, XFS_SUPER_MAGIC2_STR, 4) == 0)) {
121                 memcpy(uuid, xfsb.s_uuid, sizeof(xfsb.s_uuid));
122                 namesize = sizeof(xfsb.s_fsname);
123                 *label = smalloc(namesize + 1);
124                 sstrncpy(*label, xfsb.s_fsname, namesize);
125                 rv = 0;
126 #endif /* HAVE_XFS_XQM_H */
127         } else if(lseek(fd, 65536, SEEK_SET) == 65536
128         && read(fd, (char *)&reisersb, sizeof(reisersb)) == sizeof(reisersb)
129         && !strncmp((char *)&reisersb.s_magic, REISER_SUPER_MAGIC, 9)) {
130                 memcpy(uuid, reisersb.s_uuid, sizeof(reisersb.s_uuid));
131                 namesize = sizeof(reisersb.s_volume_name);
132                 *label = smalloc(namesize + 1);
133                 sstrncpy(*label, reisersb.s_volume_name, namesize);
134                 rv = 0;
135         }
136         close(fd);
137         return rv;
138 }
139
140 static void
141 uuidcache_addentry(char *device, char *label, char *uuid)
142 {
143         struct uuidCache_s *last;
144
145         if(!uuidCache) {
146                 last = uuidCache = smalloc(sizeof(*uuidCache));
147         } else {
148                 for(last = uuidCache; last->next; last = last->next);
149                 last->next = smalloc(sizeof(*uuidCache));
150                 last = last->next;
151         }
152         last->next = NULL;
153         last->device = device;
154         last->label = label;
155         memcpy(last->uuid, uuid, sizeof(last->uuid));
156 }
157
158 static void
159 uuidcache_init(void)
160 {
161         char line[100];
162         char *s;
163         int ma, mi, sz;
164         static char ptname[100];
165         FILE *procpt;
166         char uuid[16], *label = NULL;
167         char device[110];
168         int firstPass;
169         int handleOnFirst;
170
171         if(uuidCache) {
172                 return;
173         }
174
175         procpt = fopen(PROC_PARTITIONS, "r");
176         if(procpt == NULL) {
177                 return;
178         }
179
180         for(firstPass = 1; firstPass >= 0; firstPass--) {
181                 fseek(procpt, 0, SEEK_SET);
182                 while(fgets(line, sizeof(line), procpt)) {
183                         if(sscanf(line, " %d %d %d %[^\n ]",
184                                 &ma, &mi, &sz, ptname) != 4)
185                         {
186                                 continue;
187                         }
188
189                         /* skip extended partitions (heuristic: size 1) */
190                         if(sz == 1) {
191                                 continue;
192                         }
193
194                         /* look only at md devices on first pass */
195                         handleOnFirst = !strncmp(ptname, "md", 2);
196                         if(firstPass != handleOnFirst) {
197                                 continue;
198                         }
199
200                         /* skip entire disk (minor 0, 64, ... on ide;
201                         0, 16, ... on sd) */
202                         /* heuristic: partition name ends in a digit */
203
204                         for(s = ptname; *s; s++);
205
206                         if(isdigit((int)s[-1])) {
207                         /*
208                         * Note: this is a heuristic only - there is no reason
209                         * why these devices should live in /dev.
210                         * Perhaps this directory should be specifiable by option.
211                         * One might for example have /devlabel with links to /dev
212                         * for the devices that may be accessed in this way.
213                         * (This is useful, if the cdrom on /dev/hdc must not
214                         * be accessed.)
215                         */
216                                 snprintf(device, sizeof(device), "%s/%s",
217                                         DEVLABELDIR, ptname);
218                                 if(!get_label_uuid(device, &label, uuid)) {
219                                         uuidcache_addentry(sstrdup(device),
220                                                 label, uuid);
221                                 }
222                         }
223                 }
224         }
225         fclose(procpt);
226 }
227
228 static unsigned char
229 fromhex(char c)
230 {
231         if(isdigit((int)c)) {
232                 return (c - '0');
233         } else if(islower((int)c)) {
234                 return (c - 'a' + 10);
235         } else {
236                 return (c - 'A' + 10);
237         }
238 }
239
240 static char *
241 get_spec_by_x(int n, const char *t)
242 {
243         struct uuidCache_s *uc;
244
245         uuidcache_init();
246         uc = uuidCache;
247
248         while(uc) {
249                 switch(n) {
250                 case UUID:
251                         if(!memcmp(t, uc->uuid, sizeof(uc->uuid))) {
252                                 return sstrdup(uc->device);
253                         }
254                         break;
255                 case VOL:
256                         if(!strcmp(t, uc->label)) {
257                                 return sstrdup(uc->device);
258                         }
259                         break;
260                 }
261                 uc = uc->next;
262         }
263         return NULL;
264 }
265
266 static char *
267 get_spec_by_uuid(const char *s)
268 {
269         char uuid[16];
270         int i;
271
272         if(strlen(s) != 36
273         || s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-') {
274                 goto bad_uuid;
275         }
276
277         for(i=0; i<16; i++) {
278                 if(*s == '-') {
279                         s++;
280                 }
281                 if(!isxdigit((int)s[0]) || !isxdigit((int)s[1])) {
282                         goto bad_uuid;
283                 }
284                 uuid[i] = ((fromhex(s[0]) << 4) | fromhex(s[1]));
285                 s += 2;
286         }
287         return get_spec_by_x(UUID, uuid);
288
289         bad_uuid:
290                 DBG("Found an invalid UUID: %s", s);
291         return NULL;
292 }
293
294 static char *
295 get_spec_by_volume_label(const char *s)
296 {
297         return get_spec_by_x(VOL, s);
298 }
299
300 static char *
301 get_device_name(const char *item)
302 {
303         char *rc;
304
305         if(!strncmp(item, "UUID=", 5)) {
306                 DBG("TODO: check UUID= code!");
307                 rc = get_spec_by_uuid(item + 5);
308         } else if(!strncmp(item, "LABEL=", 6)) {
309                 DBG("TODO: check LABEL= code!");
310                 rc = get_spec_by_volume_label(item + 6);
311         } else {
312                 rc = sstrdup(item);
313         }
314         if(!rc) {
315                 DBG("Error checking device name: %s", item);
316         }
317         return rc;
318 }
319
320 #if HAVE_LISTMNTENT
321 static void
322 cu_mount_listmntent(struct tabmntent *mntlist, cu_mount_t **list)
323 {
324         struct *p;
325         struct mntent *mnt;
326
327         for(p = mntlist; p; p = p->next) {
328                 mnt = p->ment;
329                 *list = smalloc(sizeof(cu_mount_t));
330                 list->device = strdup(mnt->mnt_fsname);
331                 list->name = strdup(mnt->mnt_dir);
332                 list->type = strdup(mnt->mnt_type);
333                 list->next = NULL;
334                 list = &(ist->next);
335         }
336         freemntlist(mntlist);
337 }
338 #endif /* HAVE_LISTMNTENT */
339
340
341
342 #if HAVE_GETVFSENT
343 static void
344 cu_mount_getvfsmnt(FILE *mntf, cu_mount_t **list)
345 {
346         DBG("TODO: getvfsmnt");
347         *list = NULL;
348 }
349 #endif /* HAVE_GETVFSENT */
350
351 char *
352 cu_mount_checkmountopt(char *line, char *keyword, int full)
353 {
354         char *line2, *l2;
355         int l = strlen(keyword);
356         char *p1, *p2;
357
358         if(line == NULL || keyword == NULL) {
359                 return NULL;
360         }
361         if(full != 0) {
362                 full = 1;
363         }
364
365         line2 = sstrdup(line);
366         l2 = line2;
367         while(*l2 != '\0') {
368                 if(*l2 == ',') {
369                         *l2 = '\0';
370                 }
371                 l2++;
372         }
373
374         p1 = line - 1;
375         p2 = strchr(line, ',');
376         do {
377                 if(strncmp(line2+(p1-line)+1, keyword, l+full) == 0) {
378                         free(line2);
379                         return p1+1;
380                 }
381                 p1 = p2;
382                 if(p1 != NULL) {
383                         p2 = strchr(p1+1, ',');
384                 }
385         } while(p1 != NULL);
386
387         free(line2);
388         return NULL;
389 } /* char *cu_mount_checkmountopt(char *line, char *keyword, int full) */
390
391 char *
392 cu_mount_getmountopt(char *line, char *keyword)
393 {
394         char *r;
395
396         r = cu_mount_checkmountopt(line, keyword, 0);
397         if(r != NULL) {
398                 char *p;
399                 r += strlen(keyword);
400                 p = strchr(r, ',');
401                 if(p == NULL) {
402                         if(strlen(r) == 0) {
403                                 return NULL;
404                         }
405                         return sstrdup(r);
406                 } else {
407                         char *m;
408                         if((p-r) == 1) {
409                                 return NULL;
410                         }
411                         m = (char *)smalloc(p-r+1);
412                         sstrncpy(m, r, p-r+1);
413                         return m;
414                 }
415         }
416         return r;
417 } /* char *cu_mount_getmountopt(char *line, char *keyword) */
418
419 #if HAVE_GETMNTENT
420 static cu_mount_t *
421 cu_mount_getmntent(FILE *mntf, cu_mount_t **list)
422 {
423         cu_mount_t *last = *list;
424         struct mntent *mnt;
425
426 #if HAVE_GETMNTENT1
427         while((mnt = getmntent(mntf)) != NULL) {
428 #endif /* HAVE_GETMNTENT1 */
429                 char *loop = NULL, *device = NULL;
430
431 #if 1
432                 DBG("------------------ BEGIN");
433                 DBG("mnt->mnt_fsname %s", mnt->mnt_fsname);
434                 DBG("mnt->mnt_dir    %s", mnt->mnt_dir);
435                 DBG("mnt->mnt_type   %s", mnt->mnt_type);
436                 DBG("mnt->mnt_opts   %s", mnt->mnt_opts);
437                 DBG("mnt->mnt_freq   %d", mnt->mnt_freq);
438                 DBG("mnt->mnt_passno %d", mnt->mnt_passno);
439 #endif
440
441                 loop = cu_mount_getmountopt(mnt->mnt_opts, "loop=");
442                 if(loop == NULL) {   /* no loop= mount */
443                         device = get_device_name(mnt->mnt_fsname);
444                         if(device == NULL) {
445                                 DBG("can't get devicename for fs (%s) %s (%s)"
446                                         ": ignored", mnt->mnt_type,
447                                         mnt->mnt_dir, mnt->mnt_fsname);
448                                 continue;
449                         }
450                 } else {
451                         device = loop;
452                 }
453
454 #if 1
455                 DBG("device          %s", device);
456                 DBG("------------------ END");
457 #endif
458                 if(*list == NULL) {
459                         *list = (cu_mount_t *)smalloc(sizeof(cu_mount_t));
460                         last = *list;
461                 } else {
462                         last->next = (cu_mount_t *)smalloc(sizeof(cu_mount_t));
463                         last = last->next;
464                 }
465                 last->dir = sstrdup(mnt->mnt_dir);
466                 last->spec_device = sstrdup(mnt->mnt_fsname);
467                 last->device = device;
468                 last->type = sstrdup(mnt->mnt_type);
469                 last->options = sstrdup(mnt->mnt_opts);
470                 last->next = NULL;
471         } /* while((mnt = getmntent(mntf)) != NULL) */
472
473         return last;
474 } /* static cu_mount_t *cu_mount_getmntent(FILE *mntf, cu_mount_t **list) */
475 #endif /* HAVE_GETMNTENT */
476
477
478
479 cu_mount_t *
480 cu_mount_getlist(cu_mount_t **list)
481 {
482         cu_mount_t *last = NULL;
483
484         /* yes, i know that the indentation is wrong.
485            but show me a better way to do this... */
486         /* see lib/mountlist.c of coreutils for all
487            gory details! */
488 #if HAVE_GETMNTENT && defined(_PATH_MOUNTED)
489         {
490         FILE *mntf = NULL;
491         if((mntf = setmntent(_PATH_MOUNTED, "r")) == NULL) {
492                 DBG("opening %s failed: %s", _PATH_MOUNTED, strerror(errno));
493 #endif
494 #if HAVE_GETMNTENT && defined(MNT_MNTTAB)
495         {
496         FILE *mntf = NULL;
497         if((mntf = setmntent(MNT_MNTTAB, "r")) == NULL) {
498                 DBG("opening %s failed: %s", MNT_MNTTAB, strerror(errno));
499 #endif
500 #if HAVE_GETMNTENT && defined(MNTTABNAME)
501         {
502         FILE *mntf = NULL;
503         if((mntf = setmntent(MNTTABNAME, "r")) == NULL) {
504                 DBG("opening %s failed: %s", MNTTABNAME, strerror(errno));
505 #endif
506 #if HAVE_GETMNTENT && defined(_PATH_MNTTAB)
507         {
508         FILE *mntf = NULL;
509         if((mntf = setmntent(_PATH_MNTTAB, "r")) == NULL) {
510                 DBG("opening %s failed: %s", _PATH_MNTTAB, strerror(errno));
511 #endif
512 #if HAVE_GETVFSENT && defined(VFSTAB)
513         {
514         FILE *mntf = NULL;
515         if((mntf = fopen(VFSTAB, "r")) == NULL) {
516                 DBG("opening %s failed: %s", VFSTAB, strerror(errno));
517 #endif
518 #if HAVE_LISTMNTENT
519         {
520         struct tabmntent *mntlist;
521
522         if(listmntent(&mntlist, KMTAB, NULL, NULL) < 0) {
523                 DBG("calling listmntent() failed: %s", strerror(errno));
524 #endif
525                 /* give up */
526                 DBG("failed get local mountpoints");
527                 return(NULL);
528
529 #if HAVE_LISTMNTENT
530         } else { last = cu_mount_listmntent(mntlist, list); }
531         freemntlist(mntlist);
532         }
533 #endif
534 #if HAVE_GETVFSENT && defined(VFSTAB)
535         } else { last = cu_mount_getvfsmnt(mntf, list); }
536         (void)fclose(mntf);
537         }
538 #endif
539 #if HAVE_GETMNTENT && defined(_PATH_MNTTAB)
540         } else { last = cu_mount_getmntent(mntf, list); }
541         (void)endmntent(mntf);
542         }
543 #endif
544 #if HAVE_GETMNTENT && defined(MNTTABNAME)
545         } else { last = cu_mount_getmntent(mntf, list); }
546         (void)endmntent(mntf);
547         }
548 #endif
549 #if HAVE_GETMNTENT && defined(MNT_MNTTAB)
550         } else { last = cu_mount_getmntent(mntf, list); }
551         (void)endmntent(mntf);
552         }
553 #endif
554 #if HAVE_GETMNTENT && defined(_PATH_MOUNTED)
555         } else { last = cu_mount_getmntent(mntf, list); }
556         (void)endmntent(mntf);
557         }
558 #endif
559         return(last);
560 } /* cu_mount_t *cu_mount_getlist(cu_mount_t **list) */
561
562 void
563 cu_mount_freelist(cu_mount_t *list)
564 {
565         cu_mount_t *l = list, *p = NULL;
566
567         while(l != NULL) {
568                 while(l->next != NULL) {
569                         p = l;
570                         l = l->next;
571                 }
572                 if(p != NULL) {
573                         p->next = NULL;
574                 }
575                 sfree(l->dir);
576                 sfree(l->spec_device);
577                 sfree(l->device);
578                 sfree(l->type);
579                 sfree(l->options);
580                 sfree(l);
581                 p = NULL;
582                 if(l != list) {
583                         l = list;
584                 } else {
585                         l = NULL;
586                 }
587         } /* while(l != NULL) */
588 } /* void cu_mount_freelist(cu_mount_t *list) */
589
590 int
591 cu_mount_type(const char *type)
592 {
593         if(strcmp(type, "ext3") == 0) return CUMT_EXT3;
594         if(strcmp(type, "ext2") == 0) return CUMT_EXT2;
595         if(strcmp(type, "ufs")  == 0) return CUMT_UFS;
596         if(strcmp(type, "vxfs") == 0) return CUMT_VXFS;
597         if(strcmp(type, "zfs")  == 0) return CUMT_ZFS;
598         return CUMT_UNKNOWN;
599 } /* int cu_mount_type(const char *type) */
600
601
602