static int get_partition_start(int fd, int partno, uint64_t *start) { struct stat st; struct sysfs_cxt disk = UL_SYSFSCXT_EMPTY, part = UL_SYSFSCXT_EMPTY; dev_t devno = 0; int rc = -1; /* * wholedisk */ if (fstat(fd, &st) || !S_ISBLK(st.st_mode)) goto done; devno = st.st_rdev; if (sysfs_init(&disk, devno, NULL)) goto done; /* * partition */ devno = sysfs_partno_to_devno(&disk, partno); if (!devno) goto done; if (sysfs_init(&part, devno, &disk)) goto done; if (sysfs_read_u64(&part, "start", start)) goto done; rc = 0; done: sysfs_deinit(&part); sysfs_deinit(&disk); return rc; }
static int has_discard(const char *devname, struct sysfs_cxt *wholedisk) { struct sysfs_cxt cxt, *parent = NULL; uint64_t dg = 0; dev_t disk = 0, dev; int rc; dev = sysfs_devname_to_devno(devname, NULL); if (!dev) return 1; /* * This is tricky to read the info from sys/, because the queue * atrributes are provided for whole devices (disk) only. We're trying * to reuse the whole-disk sysfs context to optimize this stuff (as * system usually have just one disk only). */ if (sysfs_devno_to_wholedisk(dev, NULL, 0, &disk) || !disk) return 1; if (dev != disk) { if (wholedisk->devno != disk) { sysfs_deinit(wholedisk); if (sysfs_init(wholedisk, disk, NULL)) return 1; } parent = wholedisk; } rc = sysfs_init(&cxt, dev, parent); if (!rc) rc = sysfs_read_u64(&cxt, "queue/discard_granularity", &dg); sysfs_deinit(&cxt); return rc == 0 && dg > 0; }
/* * Returns 1 if the device is private LVM device. */ int sysfs_devno_is_lvm_private(dev_t devno) { struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY; char *uuid = NULL; int rc = 0; if (sysfs_init(&cxt, devno, NULL) != 0) return 0; uuid = sysfs_strdup(&cxt, "dm/uuid"); /* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important * is the "LVM" prefix and "-<name>" postfix). */ if (uuid && strncmp(uuid, "LVM-", 4) == 0) { char *p = strrchr(uuid + 4, '-'); if (p && *(p + 1)) rc = 1; } sysfs_deinit(&cxt); free(uuid); return rc; }
/* * Returns devname (e.g. "/dev/sda1") for the given devno. * * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min> * symlinks. * * Please, use more robust blkid_devno_to_devname() in your applications. */ char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz) { struct sysfs_cxt cxt; char *name; size_t sz; struct stat st; if (sysfs_init(&cxt, devno, NULL)) return NULL; name = sysfs_get_devname(&cxt, buf, bufsiz); sysfs_deinit(&cxt); if (!name) return NULL; sz = strlen(name); if (sz + sizeof("/dev/") > bufsiz) return NULL; /* create the final "/dev/<name>" string */ memmove(buf + 5, name, sz + 1); memcpy(buf, "/dev/", 5); if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == devno) return buf; return NULL; }
/* Not really Monitor but ... */ int Wait(char *dev) { struct stat stb; char devnm[32]; int rv = 1; int frozen_remaining = 3; if (stat(dev, &stb) != 0) { pr_err("Cannot find %s: %s\n", dev, strerror(errno)); return 2; } strcpy(devnm, stat2devnm(&stb)); while(1) { struct mdstat_ent *ms = mdstat_read(1, 0); struct mdstat_ent *e; for (e=ms ; e; e=e->next) if (strcmp(e->devnm, devnm) == 0) break; if (e && e->percent == RESYNC_NONE) { /* We could be in the brief pause before something * starts. /proc/mdstat doesn't show that, but * sync_action does. */ struct mdinfo mdi; char buf[21]; sysfs_init(&mdi, -1, devnm); if (sysfs_get_str(&mdi, NULL, "sync_action", buf, 20) > 0 && strcmp(buf,"idle\n") != 0) { e->percent = RESYNC_UNKNOWN; if (strcmp(buf, "frozen\n") == 0) { if (frozen_remaining == 0) e->percent = RESYNC_NONE; else frozen_remaining -= 1; } } } if (!e || e->percent == RESYNC_NONE) { if (e && e->metadata_version && strncmp(e->metadata_version, "external:", 9) == 0) { if (is_subarray(&e->metadata_version[9])) ping_monitor(&e->metadata_version[9]); else ping_monitor(devnm); } free_mdstat(ms); return rv; } free_mdstat(ms); rv = 0; mdstat_wait(5); } }
int main(int argc, char *argv[]) { struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY; char *devname; dev_t devno; char path[PATH_MAX]; int i; uint64_t u64; ssize_t len; if (argc != 2) errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]); devname = argv[1]; devno = sysfs_devname_to_devno(devname, NULL); if (!devno) err(EXIT_FAILURE, "failed to read devno"); printf("NAME: %s\n", devname); printf("DEVNO: %u\n", (unsigned int) devno); printf("DEVNOPATH: %s\n", sysfs_devno_path(devno, path, sizeof(path))); printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path))); printf("PARTITION: %s\n", sysfs_devno_has_attribute(devno, "partition") ? "YES" : "NOT"); if (sysfs_init(&cxt, devno, NULL)) return EXIT_FAILURE; len = sysfs_readlink(&cxt, NULL, path, sizeof(path) - 1); if (len > 0) { path[len] = '\0'; printf("DEVNOLINK: %s\n", path); } printf("SLAVES: %d\n", sysfs_count_dirents(&cxt, "slaves")); if (sysfs_read_u64(&cxt, "size", &u64)) printf("read SIZE failed\n"); else printf("SIZE: %jd\n", u64); if (sysfs_read_int(&cxt, "queue/hw_sector_size", &i)) printf("read SECTOR failed\n"); else printf("SECTOR: %d\n", i); printf("DEVNAME: %s\n", sysfs_get_devname(&cxt, path, sizeof(path))); sysfs_deinit(&cxt); return EXIT_SUCCESS; }
static int is_hotpluggable(const struct eject_control *ctl) { struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY; dev_t devno; int rc = 0; devno = sysfs_devname_to_devno(ctl->device, NULL); if (sysfs_init(&cxt, devno, NULL) != 0) return 0; rc = sysfs_is_hotpluggable(&cxt); sysfs_deinit(&cxt); return rc; }
static int is_hotpluggable(const char* device) { struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY; char devchain[PATH_MAX]; char subbuf[PATH_MAX]; dev_t devno; int rc = 0; ssize_t sz; char *sub; devno = sysfs_devname_to_devno(device, NULL); if (sysfs_init(&cxt, devno, NULL) != 0) return 0; /* check /sys/dev/block/<maj>:<min>/removable attribute */ if (sysfs_read_int(&cxt, "removable", &rc) == 0 && rc == 1) { verbose(_("%s: is removable device"), device); goto done; } /* read /sys/dev/block/<maj>:<min> symlink */ sz = sysfs_readlink(&cxt, NULL, devchain, sizeof(devchain)); if (sz <= 0 || sz + sizeof(_PATH_SYS_DEVBLOCK "/") > sizeof(devchain)) goto done; devchain[sz++] = '\0'; /* create absolute patch from the link */ memmove(devchain + sizeof(_PATH_SYS_DEVBLOCK "/") - 1, devchain, sz); memcpy(devchain, _PATH_SYS_DEVBLOCK "/", sizeof(_PATH_SYS_DEVBLOCK "/") - 1); while ((sub = get_subsystem(devchain, subbuf, sizeof(subbuf)))) { rc = is_hotpluggable_subsystem(sub); if (rc) { verbose(_("%s: connected by hotplug subsystem: %s"), device, sub); break; } } done: sysfs_deinit(&cxt); return rc; }
static int umount_partitions(const char *disk, int checkonly) { struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY; dev_t devno; DIR *dir = NULL; struct dirent *d; int count = 0; devno = sysfs_devname_to_devno(disk, NULL); if (sysfs_init(&cxt, devno, NULL) != 0) return 0; /* open /sys/block/<wholedisk> */ if (!(dir = sysfs_opendir(&cxt, NULL))) goto done; /* scan for partition subdirs */ while ((d = readdir(dir))) { if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) continue; if (sysfs_is_partition_dirent(dir, d, disk)) { char *mnt = NULL; char *dev = find_device(d->d_name); if (dev && device_get_mountpoint(&dev, &mnt) == 0) { verbose(_("%s: mounted on %s"), dev, mnt); if (!checkonly) umount_one(mnt); count++; } free(dev); free(mnt); } } done: if (dir) closedir(dir); sysfs_deinit(&cxt); return count; }
/* * @lc: context * * Returns pointer to the sysfs context (see lib/sysfs.c) */ struct sysfs_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc) { if (!lc || !*lc->device || (lc->flags & LOOPDEV_FL_NOSYSFS)) return NULL; if (!lc->sysfs.devno) { dev_t devno = sysfs_devname_to_devno(lc->device, NULL); if (!devno) { DBG(lc, loopdev_debug("sysfs: failed devname to devno")); return NULL; } if (sysfs_init(&lc->sysfs, devno, NULL)) { DBG(lc, loopdev_debug("sysfs: init failed")); return NULL; } } return &lc->sysfs; }
/* * Returns by @diskdevno whole disk device devno and (optionaly) by * @diskname the whole disk device name. */ int sysfs_devno_to_wholedisk(dev_t dev, char *diskname, size_t len, dev_t *diskdevno) { struct sysfs_cxt cxt; int is_part = 0; if (!dev || sysfs_init(&cxt, dev, NULL) != 0) return -1; is_part = sysfs_has_attribute(&cxt, "partition"); if (!is_part) { /* * Extra case for partitions mapped by device-mapper. * * All regualar partitions (added by BLKPG ioctl or kernel PT * parser) have the /sys/.../partition file. The partitions * mapped by DM don't have such file, but they have "part" * prefix in DM UUID. */ char *uuid = sysfs_strdup(&cxt, "dm/uuid"); char *tmp = uuid; char *prefix = uuid ? strsep(&tmp, "-") : NULL; if (prefix && strncasecmp(prefix, "part", 4) == 0) is_part = 1; free(uuid); if (is_part && get_dm_wholedisk(&cxt, diskname, len, diskdevno) == 0) /* * partitioned device, mapped by DM */ goto done; is_part = 0; } if (!is_part) { /* * unpartitioned device */ if (diskname && len) { if (!sysfs_get_devname(&cxt, diskname, len)) goto err; } if (diskdevno) *diskdevno = dev; } else { /* * partitioned device * - readlink /sys/dev/block/8:1 = ../../block/sda/sda1 * - dirname ../../block/sda/sda1 = ../../block/sda * - basename ../../block/sda = sda */ char linkpath[PATH_MAX]; char *name; int linklen; linklen = sysfs_readlink(&cxt, NULL, linkpath, sizeof(linkpath) - 1); if (linklen < 0) goto err; linkpath[linklen] = '\0'; stripoff_last_component(linkpath); /* dirname */ name = stripoff_last_component(linkpath); /* basename */ if (!name) goto err; if (diskname && len) { strncpy(diskname, name, len); diskname[len - 1] = '\0'; } if (diskdevno) { *diskdevno = sysfs_devname_to_devno(name, NULL); if (!*diskdevno) goto err; } } done: sysfs_deinit(&cxt); return 0; err: sysfs_deinit(&cxt); return -1; }
int main(int argc, char *argv[], char *envp[]) { char queuename[PATH_SIZE]; char filename[PATH_SIZE]; unsigned long long seq_kernel; unsigned long long seq_udev; char seqnum[32]; int fd; ssize_t len; int timeout = DEFAULT_TIMEOUT; int loop; static const struct option options[] = { { "timeout", 1, NULL, 't' }, { "help", 0, NULL, 'h' }, {} }; int option; int rc = 1; int seconds; logging_init("udevsettle"); udev_config_init(); dbg("version %s", UDEV_VERSION); sysfs_init(); while (1) { option = getopt_long(argc, argv, "t:h", options, NULL); if (option == -1) break; switch (option) { case 't': seconds = atoi(optarg); if (seconds > 0) timeout = seconds; else fprintf(stderr, "invalid timeout value\n"); dbg("timeout=%i", timeout); break; case 'h': printf("Usage: udevsettle [--help] [--timeout=<seconds>]\n\n"); goto exit; } } strlcpy(queuename, udev_root, sizeof(queuename)); strlcat(queuename, "/" EVENT_QUEUE_DIR, sizeof(queuename)); loop = timeout * LOOP_PER_SECOND; while (loop--) { /* wait for events in queue to finish */ while (loop--) { struct stat statbuf; if (stat(queuename, &statbuf) < 0) { info("queue is empty"); break; } usleep(1000 * 1000 / LOOP_PER_SECOND); } if (loop <= 0) { info("timeout waiting for queue"); goto exit; } /* read current udev seqnum */ strlcpy(filename, udev_root, sizeof(filename)); strlcat(filename, "/" EVENT_SEQNUM, sizeof(filename)); fd = open(filename, O_RDONLY); if (fd < 0) goto exit; len = read(fd, seqnum, sizeof(seqnum)-1); close(fd); if (len <= 0) goto exit; seqnum[len] = '\0'; seq_udev = strtoull(seqnum, NULL, 10); info("udev seqnum = %llu", seq_udev); /* read current kernel seqnum */ strlcpy(filename, sysfs_path, sizeof(filename)); strlcat(filename, "/kernel/uevent_seqnum", sizeof(filename)); fd = open(filename, O_RDONLY); if (fd < 0) goto exit; len = read(fd, seqnum, sizeof(seqnum)-1); close(fd); if (len <= 0) goto exit; seqnum[len] = '\0'; seq_kernel = strtoull(seqnum, NULL, 10); info("kernel seqnum = %llu", seq_kernel); /* make sure all kernel events have arrived in the queue */ if (seq_udev >= seq_kernel) { info("queue is empty and no pending events left"); rc = 0; goto exit; } usleep(1000 * 1000 / LOOP_PER_SECOND); info("queue is empty, but events still pending"); } exit: sysfs_cleanup(); logging_close(); return rc; }
struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options) { char fname[PATH_MAX]; char buf[PATH_MAX]; char *base; char *dbase; struct mdinfo *sra; struct mdinfo *dev, **devp; DIR *dir = NULL; struct dirent *de; sra = xcalloc(1, sizeof(*sra)); sysfs_init(sra, fd, devnm); if (sra->sys_name[0] == 0) { free(sra); return NULL; } sprintf(fname, "/sys/block/%s/md/", sra->sys_name); base = fname + strlen(fname); sra->devs = NULL; if (options & GET_VERSION) { strcpy(base, "metadata_version"); if (load_sys(fname, buf, sizeof(buf))) goto abort; if (strncmp(buf, "none", 4) == 0) { sra->array.major_version = sra->array.minor_version = -1; strcpy(sra->text_version, ""); } else if (strncmp(buf, "external:", 9) == 0) { sra->array.major_version = -1; sra->array.minor_version = -2; strcpy(sra->text_version, buf+9); } else { sscanf(buf, "%d.%d", &sra->array.major_version, &sra->array.minor_version); strcpy(sra->text_version, buf); } } if (options & GET_LEVEL) { strcpy(base, "level"); if (load_sys(fname, buf, sizeof(buf))) goto abort; sra->array.level = map_name(pers, buf); } if (options & GET_LAYOUT) { strcpy(base, "layout"); if (load_sys(fname, buf, sizeof(buf))) goto abort; sra->array.layout = strtoul(buf, NULL, 0); } if (options & GET_DISKS) { strcpy(base, "raid_disks"); if (load_sys(fname, buf, sizeof(buf))) goto abort; sra->array.raid_disks = strtoul(buf, NULL, 0); } if (options & GET_DEGRADED) { strcpy(base, "degraded"); if (load_sys(fname, buf, sizeof(buf))) goto abort; sra->array.failed_disks = strtoul(buf, NULL, 0); } if (options & GET_COMPONENT) { strcpy(base, "component_size"); if (load_sys(fname, buf, sizeof(buf))) goto abort; sra->component_size = strtoull(buf, NULL, 0); /* sysfs reports "K", but we want sectors */ sra->component_size *= 2; } if (options & GET_CHUNK) { strcpy(base, "chunk_size"); if (load_sys(fname, buf, sizeof(buf))) goto abort; sra->array.chunk_size = strtoul(buf, NULL, 0); } if (options & GET_CACHE) { strcpy(base, "stripe_cache_size"); if (load_sys(fname, buf, sizeof(buf))) /* Probably level doesn't support it */ sra->cache_size = 0; else sra->cache_size = strtoul(buf, NULL, 0); } if (options & GET_MISMATCH) { strcpy(base, "mismatch_cnt"); if (load_sys(fname, buf, sizeof(buf))) goto abort; sra->mismatch_cnt = strtoul(buf, NULL, 0); } if (options & GET_SAFEMODE) { int scale = 1; int dot = 0; unsigned i; unsigned long msec; size_t len; strcpy(base, "safe_mode_delay"); if (load_sys(fname, buf, sizeof(buf))) goto abort; /* remove a period, and count digits after it */ len = strlen(buf); for (i = 0; i < len; i++) { if (dot) { if (isdigit(buf[i])) { buf[i-1] = buf[i]; scale *= 10; } buf[i] = 0; } else if (buf[i] == '.') { dot=1; buf[i] = 0; } } msec = strtoul(buf, NULL, 10); msec = (msec * 1000) / scale; sra->safe_mode_delay = msec; } if (options & GET_BITMAP_LOCATION) { strcpy(base, "bitmap/location"); if (load_sys(fname, buf, sizeof(buf))) goto abort; if (strncmp(buf, "file", 4) == 0) sra->bitmap_offset = 1; else if (strncmp(buf, "none", 4) == 0) sra->bitmap_offset = 0; else if (buf[0] == '+') sra->bitmap_offset = strtol(buf+1, NULL, 10); else goto abort; } if (options & GET_ARRAY_STATE) { strcpy(base, "array_state"); if (load_sys(fname, sra->sysfs_array_state, sizeof(sra->sysfs_array_state))) goto abort; } else sra->sysfs_array_state[0] = 0; if (! (options & GET_DEVS)) return sra; /* Get all the devices as well */ *base = 0; dir = opendir(fname); if (!dir) goto abort; sra->array.spare_disks = 0; devp = &sra->devs; sra->devs = NULL; while ((de = readdir(dir)) != NULL) { char *ep; if (de->d_ino == 0 || strncmp(de->d_name, "dev-", 4) != 0) continue; strcpy(base, de->d_name); dbase = base + strlen(base); *dbase++ = '/'; dev = xcalloc(1, sizeof(*dev)); /* Always get slot, major, minor */ strcpy(dbase, "slot"); if (load_sys(fname, buf, sizeof(buf))) { /* hmm... unable to read 'slot' maybe the device * is going away? */ strcpy(dbase, "block"); if (readlink(fname, buf, sizeof(buf)) < 0 && errno != ENAMETOOLONG) { /* ...yup device is gone */ free(dev); continue; } else { /* slot is unreadable but 'block' link * still intact... something bad is happening * so abort */ free(dev); goto abort; } } strcpy(dev->sys_name, de->d_name); dev->disk.raid_disk = strtoul(buf, &ep, 10); if (*ep) dev->disk.raid_disk = -1; strcpy(dbase, "block/dev"); if (load_sys(fname, buf, sizeof(buf))) { /* assume this is a stale reference to a hot * removed device */ free(dev); continue; } sra->array.nr_disks++; sscanf(buf, "%d:%d", &dev->disk.major, &dev->disk.minor); /* special case check for block devices that can go 'offline' */ strcpy(dbase, "block/device/state"); if (load_sys(fname, buf, sizeof(buf)) == 0 && strncmp(buf, "offline", 7) == 0) { free(dev); continue; } /* finally add this disk to the array */ *devp = dev; devp = & dev->next; dev->next = NULL; if (options & GET_OFFSET) { strcpy(dbase, "offset"); if (load_sys(fname, buf, sizeof(buf))) goto abort; dev->data_offset = strtoull(buf, NULL, 0); strcpy(dbase, "new_offset"); if (load_sys(fname, buf, sizeof(buf)) == 0) dev->new_data_offset = strtoull(buf, NULL, 0); else dev->new_data_offset = dev->data_offset; } if (options & GET_SIZE) { strcpy(dbase, "size"); if (load_sys(fname, buf, sizeof(buf))) goto abort; dev->component_size = strtoull(buf, NULL, 0) * 2; } if (options & GET_STATE) { dev->disk.state = 0; strcpy(dbase, "state"); if (load_sys(fname, buf, sizeof(buf))) goto abort; if (strstr(buf, "in_sync")) dev->disk.state |= (1<<MD_DISK_SYNC); if (strstr(buf, "faulty")) dev->disk.state |= (1<<MD_DISK_FAULTY); if (dev->disk.state == 0) sra->array.spare_disks++; } if (options & GET_ERROR) { strcpy(buf, "errors"); if (load_sys(fname, buf, sizeof(buf))) goto abort; dev->errors = strtoul(buf, NULL, 0); } } closedir(dir); return sra; abort: if (dir) closedir(dir); sysfs_free(sra); return NULL; }