[Ocfs2-tools-devel] [PATCH 08/32] libtools-internal: Add scandisk functionality to libtools

Sunil Mushran sunil.mushran at oracle.com
Tue Sep 14 15:54:38 PDT 2010


scandisk, written by Fabio M. Di Nitto <fdinitto at redhat.com> with the help
of Joel Becker <joel.becker at oracle.com>, is a handy way to scan devices.

This patch extracted scandisk.[ch] from the scan-new branch of the
oracleasm-support git repo and added it to libtools.

http://oss.oracle.com/git/?p=oracleasm-support.git;a=summary

Signed-off-by: Sunil Mushran <sunil.mushran at oracle.com>
Signed-off-by: Joel Becker <joel.becker at oracle.com>
---
 include/tools-internal/Makefile   |    2 +-
 include/tools-internal/scandisk.h |  105 +++++
 libtools-internal/Makefile        |    2 +-
 libtools-internal/scandisk.c      |  866 +++++++++++++++++++++++++++++++++++++
 4 files changed, 973 insertions(+), 2 deletions(-)
 create mode 100644 include/tools-internal/scandisk.h
 create mode 100644 libtools-internal/scandisk.c

diff --git a/include/tools-internal/Makefile b/include/tools-internal/Makefile
index ec12d90..ff76c6d 100644
--- a/include/tools-internal/Makefile
+++ b/include/tools-internal/Makefile
@@ -2,7 +2,7 @@ TOPDIR = ../..
 
 include $(TOPDIR)/Preamble.make
 
-HFILES = verbose.h progress.h utils.h
+HFILES = verbose.h progress.h utils.h scandisk.h
 
 DIST_FILES = $(HFILES)
 
diff --git a/include/tools-internal/scandisk.h b/include/tools-internal/scandisk.h
new file mode 100644
index 0000000..bb23ef8
--- /dev/null
+++ b/include/tools-internal/scandisk.h
@@ -0,0 +1,105 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) 2008 Red Hat, Inc.  All rights reserved.
+**
+**  Author: Fabio M. Di Nitto <fdinitto at redhat.com>
+**
+**  Original design by:
+**  Joel Becker <Joel.Becker at oracle.com>
+**  Fabio M. Di Nitto <fdinitto at redhat.com>
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#ifndef __SCANDISK_H__
+#define __SCANDISK_H__
+
+#ifndef DEVPATH
+#define	DEVPATH		"/dev"
+#endif
+
+#ifndef SYSFSPATH
+#define SYSFSPATH	"/sys"
+#endif
+
+#ifndef SYSBLOCKPATH
+#define SYSBLOCKPATH	SYSFSPATH "/block"
+#endif
+
+#ifdef DEBUG
+#define	DEVCACHETIMEOUT	5	/* expressed in seconds */
+#else
+#define	DEVCACHETIMEOUT	30
+#endif
+
+/* each entry can be (generally):
+ * > 0 on success or good hit
+ * 0 on success with no hit
+ * < 0 on error
+ */
+
+struct sysfsattrs {		/* usual 0 | 1 game */
+	int sysfs;		/* did we find an entry in sysfs at all? */
+	int slaves;		/* device has slaves */
+	int holders;		/* device has holders */
+	int removable;		/* device is removable */
+	int disk;		/* device is a disk */
+};
+
+/* this structure is required because we don't know upfront how many
+ * entries for a certain maj/min will be found in /dev, and so we need
+ * to alloc them dynamically.
+ */
+struct devpath {
+	struct devpath *next;
+	char path[MAXPATHLEN];
+};
+
+/* this structure holds all the data for each maj/min found in the system
+ * that is a block device
+ */
+struct devnode {
+	struct devnode *next;
+	struct devpath *devpath;	/* point to the first path entry */
+	int maj;		/* device major */
+	int min;		/* device minor */
+	struct sysfsattrs sysfsattrs;	/* like the others.. scanning /sys */
+	int procpart;		/* 0 if the device is not in proc/part or 1 on success. <0 on error */
+	char procname[MAXPATHLEN];	/* non-NULL if we find a maj/min match */
+	int md;			/* 0 nothing to do with raid, 1 is raid,
+				 * 2 is raid slave - data from /proc/mdstat */
+	int mapper;		/* 0 nothing, 1 we believe it's a devmap dev */
+	void *filter;		/* your filter output.. whatever it is */
+};
+
+/* this is what you get after a scan... if you are lucky */
+/* each entry can be 0 if we can't scan or < 0 if there are errors */
+
+struct devlisthead {
+	time_t cache_timestamp;	/* this cache timestamp */
+	int cache_timeout;	/* for how long this cache is valid */
+	int sysfs;		/* set to 1 if we were able to scan
+				 * /sys */
+	int procpart;		/* set to 1 if we were able to scan
+				 * /proc/partitions */
+	int lsdev;		/* set to 1 if we were able to ls /dev */
+	int mdstat;		/* set to 1 if we were able to scan
+				 * /proc/mdstat */
+	int mapper;		/* set to 1 if we were able to run
+				 * something against mapper */
+	struct devnode *devnode;	/* points to the first entry */
+};
+
+typedef void (*devfilter) (struct devnode * cur, void *arg);
+
+struct devlisthead *scan_for_dev(struct devlisthead *devlisthead,
+				 time_t timeout,
+				 devfilter filter, void *filter_args);
+void free_dev_list(struct devlisthead *devlisthead);
+
+#endif /* __SCANDISK_H__ */
diff --git a/libtools-internal/Makefile b/libtools-internal/Makefile
index 05e3216..27b9159 100644
--- a/libtools-internal/Makefile
+++ b/libtools-internal/Makefile
@@ -28,7 +28,7 @@ debug_%: debug_%.o
 
 endif
 
-CFILES = verbose.c progress.c utils.c
+CFILES = verbose.c progress.c utils.c scandisk.c
 HFILES = libtools-internal.h
 
 OBJS = $(subst .c,.o,$(CFILES))
diff --git a/libtools-internal/scandisk.c b/libtools-internal/scandisk.c
new file mode 100644
index 0000000..d4b32fa
--- /dev/null
+++ b/libtools-internal/scandisk.c
@@ -0,0 +1,866 @@
+/******************************************************************************
+*******************************************************************************
+**
+**  Copyright (C) 2008 Red Hat, Inc.  All rights reserved.
+**  All rights reserved.
+**
+**  Author: Fabio M. Di Nitto <fdinitto at redhat.com>
+**
+**  Original design by:
+**  Joel Becker <Joel.Becker at oracle.com>
+**  Fabio M. Di Nitto <fdinitto at redhat.com>
+**
+**  This copyrighted material is made available to anyone wishing to use,
+**  modify, copy, or redistribute it subject to the terms and conditions
+**  of the GNU General Public License v.2.
+**
+*******************************************************************************
+******************************************************************************/
+
+#include <stdio.h>
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <sys/sysmacros.h>
+#include <sys/stat.h>
+
+#include "tools-internal/scandisk.h"
+
+/** search in cache helpers **/
+
+/*
+ * match is 0 for exact match
+ *          1 to see if the string is contained and return the first match
+ */
+
+static struct devnode *find_dev_by_path(struct devnode *startnode, char *path,
+					int match)
+{
+	struct devnode *nextnode;
+	struct devpath *nextpath;
+
+	while (startnode) {
+		nextnode = startnode->next;
+		nextpath = startnode->devpath;
+		while (nextpath) {
+			if (match) {
+				if (strstr(nextpath->path, path))
+					return startnode;
+			} else {
+				if (!strcmp(nextpath->path, path))
+					return startnode;
+			}
+			nextpath = nextpath->next;
+		}
+		startnode = nextnode;
+	}
+
+	return 0;
+}
+
+static struct devnode *find_dev_by_majmin(struct devnode *startnode, int maj,
+					  int min)
+{
+	struct devnode *nextnode;
+
+	while (startnode) {
+		nextnode = startnode->next;
+		if ((startnode->maj == maj) && (startnode->min == min))
+			return startnode;
+		startnode = nextnode;
+	}
+
+	return 0;
+}
+
+/** free the cache.. this one is easy ;) **/
+
+/* free all the path associated to one node */
+static void flush_dev_list(struct devpath *startpath)
+{
+	struct devpath *nextpath;
+
+	while (startpath) {
+		nextpath = startpath->next;
+		free(startpath);
+		startpath = nextpath;
+	}
+
+	return;
+}
+
+/* free all nodes associated with one devlist */
+static void flush_dev_cache(struct devlisthead *devlisthead)
+{
+	struct devnode *nextnode, *startnode = devlisthead->devnode;
+
+	while (startnode) {
+		nextnode = startnode->next;
+		flush_dev_list(startnode->devpath);
+		free(startnode);
+		startnode = nextnode;
+	}
+
+	return;
+}
+
+/** list object allocation helpers **/
+
+/* our only certain keys in the list are maj and min
+ * this function append a devnode obj to devlisthead
+ * and set maj and min
+ */
+
+static struct devnode *alloc_list_obj(struct devlisthead *devlisthead, int maj,
+				      int min)
+{
+	struct devnode *nextnode, *startnode;
+
+	nextnode = malloc(sizeof(struct devnode));
+	if (!nextnode)
+		return 0;
+
+	memset(nextnode, 0, sizeof(struct devnode));
+
+	if (!devlisthead->devnode) {
+		devlisthead->devnode = startnode = nextnode;
+	} else {
+		startnode = devlisthead->devnode;
+		while (startnode->next)
+			startnode = startnode->next;
+
+		/* always append what we find */
+		startnode->next = nextnode;
+		startnode = nextnode;
+	}
+
+	startnode->maj = maj;
+	startnode->min = min;
+
+	return startnode;
+}
+
+/* really annoying but we have no way to know upfront how
+ * many paths are linked to a certain maj/min combo.
+ * Once we find a device, we know maj/min and this new path.
+ * add_path_obj will add the given path to the devnode
+ */
+static int add_path_obj(struct devnode *startnode, char *path)
+{
+	struct devpath *nextpath, *startpath;
+
+	nextpath = malloc(sizeof(struct devpath));
+	if (!nextpath)
+		return 0;
+
+	memset(nextpath, 0, sizeof(struct devpath));
+
+	if (!startnode->devpath) {
+		startnode->devpath = startpath = nextpath;
+	} else {
+		startpath = startnode->devpath;
+		while (startpath->next)
+			startpath = startpath->next;
+
+		/* always append what we find */
+		startpath->next = nextpath;
+		startpath = nextpath;
+	}
+
+	strncpy(startpath->path, path, MAXPATHLEN - 1);
+
+	return 1;
+}
+
+/* lsdev needs to add blocks in 2 conditions: if we have a real block device
+ * or if have a symlink to a block device.
+ * this function simply avoid duplicate code around.
+ */
+static int add_lsdev_block(struct devlisthead *devlisthead, struct stat *sb,
+			   char *path)
+{
+	int maj, min;
+	struct devnode *startnode;
+
+	maj = major(sb->st_rdev);
+	min = minor(sb->st_rdev);
+
+	startnode = find_dev_by_majmin(devlisthead->devnode, maj, min);
+	if (!startnode) {
+		startnode = alloc_list_obj(devlisthead, maj, min);
+		if (!startnode)
+			return 0;
+	}
+
+	if (!add_path_obj(startnode, path))
+		return 0;
+
+	return 1;
+}
+
+/* check if it is a device or a symlink to a device */
+static int dev_is_block(struct stat *sb, char *path)
+{
+	if (S_ISBLK(sb->st_mode))
+		return 1;
+
+	if (S_ISLNK(sb->st_mode))
+		if (!stat(path, sb))
+			if (S_ISBLK(sb->st_mode))
+				return 1;
+
+	return 0;
+}
+
+/* lsdev does nothing more than ls -lR /dev
+ * dives into dirs (skips hidden directories)
+ * add block devices
+ * parse symlinks
+ *
+ * ret:
+ * 1 on success
+ * -1 for generic errors
+ * -2 -ENOMEM
+ */
+static int lsdev(struct devlisthead *devlisthead, char *path)
+{
+	int i, n, err = 0;
+	struct dirent **namelist;
+	struct stat sb;
+	char newpath[MAXPATHLEN];
+
+	i = scandir(path, &namelist, 0, alphasort);
+	if (i < 0)
+		return -1;
+
+	for (n = 0; n < i; n++) {
+		if (namelist[n]->d_name[0] != '.') {
+			snprintf(newpath, sizeof(newpath), "%s/%s", path,
+				 namelist[n]->d_name);
+
+			if (!lstat(newpath, &sb)) {
+				if (S_ISDIR(sb.st_mode))
+					err = lsdev(devlisthead, newpath);
+				if (err < 0)
+					return err;
+
+				if (dev_is_block(&sb, newpath))
+					if (!add_lsdev_block
+					    (devlisthead, &sb, newpath) < 0)
+						return -2;
+			}
+		}
+		free(namelist[n]);
+	}
+	free(namelist);
+	return 1;
+}
+
+/*
+ * scan /proc/partitions and adds info into the list.
+ * It's able to add nodes if those are not found in sysfs.
+ *
+ * ret:
+ *  0 if we can't scan
+ *  -2 -ENOMEM
+ *  1 if everything is ok
+ */
+
+static int scanprocpart(struct devlisthead *devlisthead)
+{
+	char line[4096];
+	FILE *fp;
+	int minor, major;
+	unsigned long long blkcnt;
+	char device[128];
+	struct devnode *startnode;
+	fp = fopen("/proc/partitions", "r");
+	if (!fp)
+		return 0;
+	while (fgets(line, sizeof(line), fp)
+	       != NULL) {
+
+		if (strlen(line) > 128 + (22))
+			continue;
+		sscanf(line, "%4d %4d %10llu %s",
+		       &major, &minor, &blkcnt, device);
+
+		/* careful here.. if there is no device, we are scanning the
+		 * first two lines that are not useful to us
+		 */
+		if (!strlen(device))
+			continue;
+		startnode =
+		    find_dev_by_majmin(devlisthead->devnode, major, minor);
+		if (!startnode) {
+			startnode = alloc_list_obj(devlisthead, major, minor);
+			if (!startnode)
+				return -2;
+		}
+
+		startnode->procpart = 1;
+		strcpy(startnode->procname, device);
+	}
+
+	fclose(fp);
+	return 1;
+}
+
+/* scan /proc/mdstat and adds info to the list. At this point
+ * all the devices _must_ be already in the list. We don't add anymore
+ * since raids can only be assembled out of existing devices
+ *
+ * ret:
+ * 1 if we could scan
+ * 0 otherwise
+ */
+static int scanmdstat(struct devlisthead *devlisthead)
+{
+	char line[4096];
+	FILE *fp;
+	char device[16];
+	char separator[4];
+	char status[16];
+	char personality[16];
+	char firstdevice[16];
+	char devices[4096];
+	char *tmp, *next;
+	struct devnode *startnode = NULL;
+
+	fp = fopen("/proc/mdstat", "r");
+	if (!fp)
+		return 0;
+
+	while (fgets(line, sizeof(line), fp) != NULL) {
+
+		/* i like things to be absolutely clean */
+		memset(device, 0, 16);
+		memset(separator, 0, 4);
+		memset(status, 0, 16);
+		memset(personality, 0, 16);
+		memset(firstdevice, 0, 16);
+		memset(devices, 0, 4096);
+
+		if (strlen(line) > 4096)
+			continue;
+
+		/* we only parse stuff that starts with ^md
+		 * that's supposed to point to raid */
+		if (!(line[0] == 'm' && line[1] == 'd'))
+			continue;
+
+		sscanf(line, "%s %s %s %s %s",
+		       device, separator, status, personality, firstdevice);
+
+		/* scan only raids that are active */
+		if (strcmp(status, "active"))
+			continue;
+
+		/* try to find *mdX and set the device as real raid.
+		 * if we don't find the device we don't try to set the slaves */
+		startnode = find_dev_by_path(devlisthead->devnode, device, 1);
+		if (!startnode)
+			continue;
+
+		startnode->md = 1;
+
+		/* trunkate the string from sdaX[Y] to sdaX and
+		 * copy the whole device string over */
+		memset(strstr(firstdevice, "["), 0, 1);
+		strcpy(devices, strstr(line, firstdevice));
+
+		/* if we don't find any slave (for whatever reason)
+		 * keep going */
+		if (!strlen(devices))
+			continue;
+
+		tmp = devices;
+		while ((tmp) && ((next = strstr(tmp, " ")) || strlen(tmp))) {
+
+			memset(strstr(tmp, "["), 0, 1);
+
+			startnode =
+			    find_dev_by_path(devlisthead->devnode, tmp, 1);
+			if (startnode)
+				startnode->md = 2;
+
+			tmp = next;
+
+			if (tmp)
+				tmp++;
+
+		}
+	}
+
+	fclose(fp);
+	return 1;
+}
+
+/* scanmapper parses /proc/devices to identify what maj are associated
+ * with device-mapper
+ *
+ * ret:
+ * can't fail for now
+ */
+static int scanmapper(struct devlisthead *devlisthead)
+{
+	struct devnode *startnode;
+	FILE *fp;
+	char line[4096];
+	char major[4];
+	char device[64];
+	int maj, start = 0;
+
+	fp = fopen("/proc/devices", "r");
+	if (!fp)
+		return 0;
+
+	while (fgets(line, sizeof(line), fp) != NULL) {
+		memset(major, 0, 4);
+		memset(device, 0, 64);
+
+		if (strlen(line) > 4096)
+			continue;
+
+		if (!strncmp(line, "Block devices:", 13)) {
+			start = 1;
+			continue;
+		}
+
+		if (!start)
+			continue;
+
+		sscanf(line, "%s %s", major, device);
+
+		if (!strncmp(device, "device-mapper", 13)) {
+			maj = atoi(major);
+			startnode = devlisthead->devnode;
+
+			while (startnode) {
+				if (startnode->maj == maj)
+					startnode->mapper = 1;
+
+				startnode = startnode->next;
+			}
+
+		}
+
+	}
+
+	fclose(fp);
+	return 1;
+}
+
+/* scan through the list and execute the custom filter for each entry */
+static void run_filter(struct devlisthead *devlisthead,
+		       devfilter filter, void *filter_args)
+{
+	struct devnode *startnode = devlisthead->devnode;
+
+	while (startnode) {
+		filter(startnode, filter_args);
+		startnode = startnode->next;
+	}
+	return;
+}
+
+/** sysfs helper functions **/
+
+/* /sys/block/sda/dev or /sys/block/sda1/dev exists
+ * the device is real and dev contains maj/min info.
+ *
+ * ret:
+ * 1 on success and set maj/min
+ * 0 if no file is found
+ * -1 if we could not open the file
+ */
+static int sysfs_is_dev(char *path, int *maj, int *min)
+{
+	char newpath[MAXPATHLEN];
+	struct stat sb;
+	FILE *f;
+	snprintf(newpath, sizeof(newpath), "%s/dev", path);
+	if (!lstat(newpath, &sb)) {
+		f = fopen(newpath, "r");
+		if (f) {
+			fscanf(f, "%d:%d", maj, min);
+			fclose(f);
+			return 1;
+		} else
+			return -1;
+	}
+	return 0;
+}
+
+/* /sys/block/sda/removable tells us if a device can be ejected
+ * from the system or not. This is useful for USB pendrive that are
+ * both removable and disks.
+ *
+ * ret:
+ * 1 if is removable
+ * 0 if not
+ * -1 if we couldn't find the file.
+ */
+static int sysfs_is_removable(char *path)
+{
+	char newpath[MAXPATHLEN];
+	struct stat sb;
+	int i = -1;
+	FILE *f;
+	snprintf(newpath, sizeof(newpath), "%s/removable", path);
+	if (!lstat(newpath, &sb)) {
+		f = fopen(newpath, "r");
+		if (f) {
+			fscanf(f, "%d\n", &i);
+			fclose(f);
+		}
+	}
+	return i;
+}
+
+/* we use this function to scan /sys/block/sda{,1}/{holders,slaves}
+ * to know in what position of the foodchain this device is.
+ * NOTE: a device can have both holders and slaves at the same time!
+ * (for example an lvm volume on top of a raid device made of N real disks
+ *
+ * ret:
+ * always return the amount of entries in the dir if successful
+ * or any return value from scandir.
+ */
+static int sysfs_has_subdirs_entries(char *path, char *subdir)
+{
+	char newpath[MAXPATHLEN];
+	struct dirent **namelist;
+	struct stat sb;
+	int n, i, count = 0;
+
+	snprintf(newpath, sizeof(newpath), "%s/%s", path, subdir);
+	if (!lstat(newpath, &sb)) {
+		if (S_ISDIR(sb.st_mode)) {
+			i = scandir(newpath, &namelist, 0, alphasort);
+			if (i < 0)
+				return i;
+			for (n = 0; n < i; n++) {
+				if (namelist[n]->d_name[0] != '.')
+					count++;
+				free(namelist[n]);
+			}
+			free(namelist);
+		}
+	}
+	return count;
+}
+
+/* this is the best approach so far to make sure a block device
+ * is a disk and distinguish it from a cdrom or tape or etc.
+ * What we know for sure is that a type 0 is a disk.
+ * From an old piece code 0xe is an IDE disk and comes from media.
+ * NOTE: we scan also for ../ that while it seems stupid, it will
+ * allow to easily mark partitions as real disks.
+ * (see for example /sys/block/sda/device/type and
+ * /sys/block/sda1/../device/type)
+ * TODO: there might be more cases to evaluate.
+ *
+ * ret:
+ * -2 we were not able to open the file
+ * -1 no path found
+ *  0 we found the path but we have 0 clue on what it is
+ *  1 is a disk
+ */
+static int sysfs_is_disk(char *path)
+{
+	char newpath[MAXPATHLEN];
+	struct stat sb;
+	int i = -1;
+	FILE *f;
+
+	snprintf(newpath, sizeof(newpath), "%s/device/type", path);
+	if (!lstat(newpath, &sb))
+		goto found;
+
+	snprintf(newpath, sizeof(newpath), "%s/../device/type", path);
+	if (!lstat(newpath, &sb))
+		goto found;
+
+	snprintf(newpath, sizeof(newpath), "%s/device/media", path);
+	if (!lstat(newpath, &sb))
+		goto found;
+
+	snprintf(newpath, sizeof(newpath), "%s/../device/media", path);
+	if (lstat(newpath, &sb))
+		return -1;
+
+      found:
+	f = fopen(newpath, "r");
+	if (f) {
+		fscanf(f, "%d\n", &i);
+		fclose(f);
+
+		switch (i) {
+		case 0x0:	/* scsi type_disk */
+		case 0xe:	/* found on ide disks from old kernels.. */
+			i = 1;
+			break;
+		default:
+			i = 0;	/* by default we have no clue */
+			break;
+		}
+	} else
+		i = -2;
+
+	return i;
+}
+
+/* recursive function that will scan and dive into /sys/block
+ * looking for devices and scanning for attributes.
+ *
+ * ret:
+ * 1 on success
+ * -1 on generic error
+ * -2 -ENOMEM
+ */
+static int scansysfs(struct devlisthead *devlisthead, char *path)
+{
+	struct devnode *startnode;
+	int i, n, maj, min;
+	struct dirent **namelist;
+	struct stat sb;
+	char newpath[MAXPATHLEN];
+
+	i = scandir(path, &namelist, 0, alphasort);
+	if (i < 0)
+		return -1;
+
+	for (n = 0; n < i; n++) {
+		if (namelist[n]->d_name[0] != '.') {
+			snprintf(newpath, sizeof(newpath),
+				 "%s/%s", path, namelist[n]->d_name);
+			if (!lstat(newpath, &sb)) {
+
+				if (S_ISDIR(sb.st_mode))
+					if (scansysfs(devlisthead, newpath) < 0)
+						return -1;
+
+				if (S_ISLNK(sb.st_mode))
+					continue;
+
+				if (sysfs_is_dev(newpath, &maj, &min) > 0) {
+					startnode =
+					    alloc_list_obj(devlisthead, maj,
+							   min);
+					if (!startnode)
+						return -2;
+
+					startnode->sysfsattrs.sysfs = 1;
+					startnode->sysfsattrs.removable =
+					    sysfs_is_removable(newpath);
+					startnode->sysfsattrs.holders =
+					    sysfs_has_subdirs_entries(newpath,
+								      "holders");
+					startnode->sysfsattrs.slaves =
+					    sysfs_has_subdirs_entries(newpath,
+								      "slaves");
+					startnode->sysfsattrs.disk =
+					    sysfs_is_disk(newpath);
+				}
+			}
+		}
+		free(namelist[n]);
+	}
+
+	free(namelist);
+	return 1;
+}
+
+/*
+ * devlisthead can be null if you are at init time. pass the old one if you are
+ * updating or scanning..
+ *
+ * timeout is used only at init time to set the cache timeout value if default
+ * value is not good enough. We might extend its meaning at somepoint.
+ * Anything <= 0 means that the cache does not expire.
+ */
+
+struct devlisthead *scan_for_dev(struct devlisthead *devlisthead,
+				 time_t timeout,
+				 devfilter filter, void *filter_args)
+{
+	int res;
+	time_t current;
+
+	time(&current);
+
+	if (devlisthead) {
+		if ((current - devlisthead->cache_timestamp) <
+		    devlisthead->cache_timeout) {
+			return devlisthead;
+		}
+	} else {
+		devlisthead = malloc(sizeof(struct devlisthead));
+		if (!devlisthead)
+			return NULL;
+		memset(devlisthead, 0, sizeof(struct devlisthead));
+		if (timeout)
+			devlisthead->cache_timeout = timeout;
+		else
+			devlisthead->cache_timeout = DEVCACHETIMEOUT;
+	}
+
+	flush_dev_cache(devlisthead);
+	devlisthead->cache_timestamp = current;
+
+	/* it's important we check those 3 errors and abort in case
+	 * as it means that we are running out of mem,
+	 */
+	devlisthead->sysfs = res = scansysfs(devlisthead, SYSBLOCKPATH);
+	if (res < -1)
+		goto emergencyout;
+
+	devlisthead->procpart = res = scanprocpart(devlisthead);
+	if (res < -1)
+		goto emergencyout;
+
+	devlisthead->lsdev = res = lsdev(devlisthead, DEVPATH);
+	if (res < -1)
+		goto emergencyout;
+
+	/* from now on we don't alloc mem ourselves but only add info */
+	devlisthead->mdstat = scanmdstat(devlisthead);
+	devlisthead->mapper = scanmapper(devlisthead);
+	if (filter)
+		run_filter(devlisthead, filter, filter_args);
+
+	return devlisthead;
+
+      emergencyout:
+	free_dev_list(devlisthead);
+	return 0;
+}
+
+/* free everything we used so far */
+
+void free_dev_list(struct devlisthead *devlisthead)
+{
+	if (devlisthead) {
+		flush_dev_cache(devlisthead);
+		free(devlisthead);
+	}
+	return;
+}
+
+#ifdef DEBUG_EXE
+#include "ocfs2-kernel/kernel-list.h"
+#include <unistd.h>
+
+struct sd_devices {
+	struct list_head sd_list;
+	int sd_maj;
+	int sd_min;
+	char *sd_path;
+};
+
+struct scan_context {
+	struct list_head devlist;
+	int rescan;
+};
+
+static void add_to_list(struct list_head *device_list, struct devnode *node)
+{
+	struct devpath *path;
+	struct sd_devices *sd;
+	int add = 0;
+
+	path = node->devpath;
+	while (path) {
+		if (node->mapper)
+			add = !strncmp(path->path, "/dev/mapper/", 12);
+		else
+			add = !strncmp(path->path, "/dev/sd", 7);
+		if (add) {
+			sd = malloc(sizeof(struct sd_devices));
+			if (sd) {
+				sd->sd_maj = node->maj;
+				sd->sd_min = node->min;
+				sd->sd_path = strdup(path->path);
+				list_add_tail(&sd->sd_list, device_list);
+				break;
+			}
+		}
+		path = path->next;
+	}
+}
+
+static void filter_devices(struct devnode *node, void *user_data)
+{
+	struct scan_context *ctxt = user_data;
+
+	/* No information in sysfs?  Ignore it! */
+	if (!node->sysfsattrs.sysfs)
+		return;
+
+	/* Not a disk?  Ignore it! */
+	if (!node->sysfsattrs.disk)
+		return;
+
+	/* It's part of some other device?  Ignore it! */
+	if (node->sysfsattrs.holders)
+		return;
+
+	/*
+	 * No path in /dev?  Well, udev probably hasn't gotten there. Trigger
+	 * a rescan
+	 */
+	if (!node->devpath) {
+		ctxt->rescan = 1;
+		return;
+	}
+
+	add_to_list(&ctxt->devlist, node);
+}
+
+int main(int argc, char **argv)
+{
+	struct devlisthead *dev = NULL;
+	int delay = 1;
+	struct scan_context scan_ctxt, *ctxt = &scan_ctxt;
+	struct list_head *pos, *pos1;
+	struct sd_devices *sd;
+
+	INIT_LIST_HEAD(&ctxt->devlist);
+
+	do {
+		ctxt->rescan = 0;
+		if (delay > 5)
+			break;
+
+		if (dev) {
+			list_for_each_safe(pos, pos1, &ctxt->devlist) {
+				sd = list_entry(pos, struct sd_devices, sd_list);
+				list_del(pos);
+				free(sd);
+			}
+			free_dev_list(dev);
+			sleep(delay);
+			delay += 2;
+		}
+
+		dev = scan_for_dev(NULL, 5, filter_devices, ctxt);
+		if (!dev) {
+			printf("error\n");
+			return -1;
+		}
+	} while (ctxt->rescan);
+
+
+	list_for_each(pos, &ctxt->devlist) {
+		sd = list_entry(pos, struct sd_devices, sd_list);
+		printf("%d %d %s\n", sd->sd_maj, sd->sd_min, sd->sd_path);
+	}
+
+	free_dev_list(dev);
+
+	return 0;
+}
+#endif
-- 
1.7.0.4




More information about the Ocfs2-tools-devel mailing list