Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/cman/qdisk/scandisk.c b/cman/qdisk/scandisk.c
index 0f4efa525..4456e53eb 100644
--- a/cman/qdisk/scandisk.c
+++ b/cman/qdisk/scandisk.c
@@ -1,758 +1,761 @@
#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 "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) {
int err;
err = fscanf(f, "%d:%d", maj, min);
fclose(f);
if ((err == EOF) || (err != 2))
return -1;
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) {
int err;
err = fscanf(f, "%d\n", &i);
fclose(f);
if ((err == EOF) || (err != 1))
i = -1;
}
}
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))
goto found;
snprintf(newpath, sizeof(newpath), "%s/device/devtype", path);
if (lstat(newpath, &sb))
return 1;
snprintf(newpath, sizeof(newpath), "%s/../device/devtype", path);
if (lstat(newpath, &sb))
return 1;
return -1;
found:
f = fopen(newpath, "r");
if (f) {
int err;
err = fscanf(f, "%d\n", &i);
fclose(f);
if ((err == EOF) || (err != 1))
return 0;
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, int level)
{
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 (!stat(newpath, &sb) && !level)
if (S_ISDIR(sb.st_mode))
if (scansysfs(devlisthead, newpath, 1) < 0)
return -1;
if (!lstat(newpath, &sb)) {
+ if (S_ISDIR(sb.st_mode))
+ if (scansysfs(devlisthead, newpath, 1) < 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, 0);
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;
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 11:04 AM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1018592
Default Alt Text
(17 KB)

Event Timeline