Example #1
0
/* reverse mapping from the device file name to the devpath */
static int name_index(struct udev *udev, const char *devpath, const char *name, int add, int test)
{
	char device[UTIL_PATH_SIZE];
	char filename[UTIL_PATH_SIZE * 2];
	size_t devlen = strlen(udev_get_dev_path(udev))+1;
	size_t start;
	int fd;

	/* directory with device name */
	util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
	start = util_strlcat(filename, "/.udev/names/", sizeof(filename));
	util_strlcat(filename, &name[devlen], sizeof(filename));
	util_path_encode(&filename[start], sizeof(filename) - start);
	/* entry with the devpath */
	util_strlcpy(device, devpath, sizeof(device));
	util_path_encode(device, sizeof(device));
	util_strlcat(filename, "/", sizeof(filename));
	util_strlcat(filename, device, sizeof(filename));

	if (add) {
		dbg(udev, "creating index: '%s'\n", filename);
		util_create_path(udev, filename);
		fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644);
		if (fd > 0)
			close(fd);
	} else {
		dbg(udev, "removing index: '%s'\n", filename);
		unlink(filename);
		util_delete_path(udev, filename);
	}
	return 0;
}
Example #2
0
/* move any old watches directory out of the way, and then restore
 * the watches
 */
void udev_watch_restore(struct udev *udev)
{
    char filename[UTIL_PATH_SIZE], oldname[UTIL_PATH_SIZE];

    if (inotify_fd < 0)
        return;

    util_strscpyl(oldname, sizeof(oldname), udev_get_dev_path(udev), "/.udev/watch.old", NULL);
    util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/watch", NULL);
    if (rename(filename, oldname) == 0) {
        DIR *dir;
        struct dirent *ent;

        dir = opendir(oldname);
        if (dir == NULL) {
            err(udev, "unable to open old watches dir '%s', old watches will not be restored: %m", oldname);
            return;
        }

        for (ent = readdir(dir); ent != NULL; ent = readdir(dir)) {
            char device[UTIL_PATH_SIZE];
            char *s;
            size_t l;
            ssize_t len;
            struct udev_device *dev;
            int maj, min;
            char type;

            if (ent->d_name[0] == '.')
                continue;

            s = device;
            l = util_strpcpy(&s, sizeof(device), udev_get_sys_path(udev));
            len = readlinkat(dirfd(dir), ent->d_name, s, l);
            if (len <= 0 || len == (ssize_t)l)
                goto unlink;
            s[len] = '\0';

            if (sscanf(s, "%c%i:%i", &type, &maj, &min) != 3)
                goto unlink;
            dev = udev_device_new_from_devnum(udev, type, makedev(maj, min));
            if (dev == NULL)
                goto unlink;

            info(udev, "restoring old watch on '%s'\n", udev_device_get_devnode(dev));
            udev_watch_begin(udev, dev);
            udev_device_unref(dev);
unlink:
            unlinkat(dirfd(dir), ent->d_name, 0);
        }

        closedir(dir);
        rmdir(oldname);

    } else if (errno != ENOENT) {
        err(udev, "unable to move watches dir '%s', old watches will not be restored: %m", filename);
    }
}
Example #3
0
/* manage "stack of names" with possibly specified device priorities */
static void link_update(struct udev_device *dev, const char *slink, bool add)
{
	struct udev *udev = udev_device_get_udev(dev);
	char name_enc[UTIL_PATH_SIZE];
	char filename[UTIL_PATH_SIZE * 2];
	char dirname[UTIL_PATH_SIZE];
	const char *target;
	char buf[UTIL_PATH_SIZE];

	dbg(udev, "update symlink '%s' of '%s'\n", slink, udev_device_get_syspath(dev));

	util_path_encode(&slink[strlen(udev_get_dev_path(udev))+1], name_enc, sizeof(name_enc));
	util_strscpyl(dirname, sizeof(dirname), udev_get_dev_path(udev), "/.udev/links/", name_enc, NULL);
	util_strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL);

	if (!add) {
		dbg(udev, "removing index: '%s'\n", filename);
		if (unlink(filename) == 0)
			rmdir(dirname);
	}

	target = link_find_prioritized(dev, add, dirname, buf, sizeof(buf));
	if (target == NULL) {
		info(udev, "no reference left, remove '%s'\n", slink);
		if (unlink(slink) == 0)
			util_delete_path(udev, slink);
	} else {
		info(udev, "creating link '%s' to '%s'\n", slink, target);
		node_symlink(udev, target, slink);
	}

	if (add) {
		int err;

		dbg(udev, "creating index: '%s'\n", filename);
		do {
			int fd;

			err = util_create_path(udev, filename);
			if (err != 0 && err != -ENOENT)
				break;
			fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
			if (fd >= 0)
				close(fd);
			else
				err = -errno;
		} while (err == -ENOENT);
	}
}
Example #4
0
void udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid)
{
        struct udev *udev = udev_device_get_udev(dev);
        char filename[UTIL_PATH_SIZE];
        struct udev_list_entry *list_entry;
        int err = 0;

        info(udev, "handling device node '%s', devnum=%s, mode=%#o, uid=%d, gid=%d\n",
             udev_device_get_devnode(dev), udev_device_get_id_filename(dev), mode, uid, gid);

        if (node_fixup(dev, mode, uid, gid) < 0)
                return;

        /* always add /dev/{block,char}/$major:$minor */
        snprintf(filename, sizeof(filename), "%s/%s/%u:%u",
                 udev_get_dev_path(udev),
                 strcmp(udev_device_get_subsystem(dev), "block") == 0 ? "block" : "char",
                 major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)));
        node_symlink(udev, udev_device_get_devnode(dev), filename);

        /* create/update symlinks, add symlinks to name index */
        udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) {
                if (udev_list_entry_get_num(list_entry))
                        /* simple unmanaged link name */
                        node_symlink(udev, udev_device_get_devnode(dev), udev_list_entry_get_name(list_entry));
                else
                        link_update(dev, udev_list_entry_get_name(list_entry), 1);
        }
}
Example #5
0
struct udev_device *udev_watch_lookup(struct udev *udev, int wd)
{
    char filename[UTIL_PATH_SIZE];
    char majmin[UTIL_PATH_SIZE];
    char *s;
    size_t l;
    ssize_t len;
    int maj, min;
    char type;
    dev_t devnum;

    if (inotify_fd < 0 || wd < 0)
        return NULL;

    snprintf(filename, sizeof(filename), "%s/.udev/watch/%d", udev_get_dev_path(udev), wd);
    s = majmin;
    l = util_strpcpy(&s, sizeof(majmin), udev_get_sys_path(udev));
    len = readlink(filename, s, l);
    if (len <= 0 || (size_t)len == l)
        return NULL;
    s[len] = '\0';

    if (sscanf(s, "%c%i:%i", &type, &maj, &min) != 3)
        return NULL;
    devnum = makedev(maj, min);
    return udev_device_new_from_devnum(udev, type, devnum);
}
Example #6
0
void udev_watch_begin(struct udev *udev, struct udev_device *dev)
{
    char filename[UTIL_PATH_SIZE];
    char majmin[UTIL_PATH_SIZE];
    int wd;

    if (inotify_fd < 0)
        return;

    info(udev, "adding watch on '%s'\n", udev_device_get_devnode(dev));
    wd = inotify_add_watch(inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE);
    if (wd < 0) {
        err(udev, "inotify_add_watch(%d, %s, %o) failed: %m\n",
            inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE);
        return;
    }

    snprintf(majmin, sizeof(majmin), "%c%i:%i",
             strcmp(udev_device_get_subsystem(dev), "block") == 0 ? 'b' : 'c',
             major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)));
    snprintf(filename, sizeof(filename), "%s/.udev/watch/%d", udev_get_dev_path(udev), wd);
    util_create_path(udev, filename);
    unlink(filename);
    symlink(majmin, filename);

    udev_device_set_watch_handle(dev, wd);
}
/* manage "stack of names" with possibly specified device priorities */
static void link_update(struct udev_device *dev, const char *slink, bool add)
{
	struct udev *udev = udev_device_get_udev(dev);
	char name_enc[UTIL_PATH_SIZE];
	char filename[UTIL_PATH_SIZE * 2];
	char dirname[UTIL_PATH_SIZE];
	const char *target;
	char buf[UTIL_PATH_SIZE];

	dbg(udev, "update symlink '%s' of '%s'\n", slink, udev_device_get_syspath(dev));

	util_path_encode(&slink[strlen(udev_get_dev_path(udev))+1], name_enc, sizeof(name_enc));
	snprintf(dirname, sizeof(dirname), "%s/.udev/links/%s", udev_get_dev_path(udev), name_enc);
	snprintf(filename, sizeof(filename), "%s/%c%u:%u", dirname,
		 strcmp(udev_device_get_subsystem(dev), "block") == 0 ? 'b' : 'c',
		 major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)));

	if (!add) {
		dbg(udev, "removing index: '%s'\n", filename);
		unlink(filename);
		util_delete_path(udev, filename);
	}

	target = link_find_prioritized(dev, add, dirname, buf, sizeof(buf));
	if (target == NULL) {
		info(udev, "no reference left, remove '%s'\n", slink);
		unlink(slink);
		util_delete_path(udev, slink);
	} else {
		info(udev, "creating link '%s' to '%s'\n", slink, target);
		node_symlink(udev, target, slink);
	}

	if (add) {
		int err;

		dbg(udev, "creating index: '%s'\n", filename);
		do {
			err = util_create_path(udev, filename);
			if (err != 0 && err != -ENOENT)
				break;
			err = symlink(udev_device_get_devpath(dev), filename);
			if (err != 0)
				err = -errno;
		} while (err == -ENOENT);
	}
}
const char *udev_get_dev_dir(void)
{
	if (!_udev) {
		log_debug(_no_context_msg);
		return NULL;
	}

	return udev_get_dev_path(_udev);
}
Example #9
0
static int name_index_get_devices(struct udev *udev, const char *name, struct udev_list_node *dev_list)
{
	char dirname[PATH_MAX];
	size_t devlen = strlen(udev_get_dev_path(udev))+1;
	size_t start;
	DIR *dir;
	int count = 0;

	util_strlcpy(dirname, udev_get_dev_path(udev), sizeof(dirname));
	start = util_strlcat(dirname, "/.udev/names/", sizeof(dirname));
	util_strlcat(dirname, &name[devlen], sizeof(dirname));
	util_path_encode(&dirname[start], sizeof(dirname) - start);
	dir = opendir(dirname);
	if (dir == NULL) {
		dbg(udev, "no index directory '%s': %m\n", dirname);
		count = -1;
		goto out;
	}
	dbg(udev, "found index directory '%s'\n", dirname);

	while (1) {
		struct dirent *ent;
		char device[UTIL_PATH_SIZE];

		ent = readdir(dir);
		if (ent == NULL || ent->d_name[0] == '\0')
			break;
		if (ent->d_name[0] == '.')
			continue;

		util_strlcpy(device, udev_get_sys_path(udev), sizeof(device));
		util_strlcat(device, ent->d_name, sizeof(device));
		util_path_decode(device);
		udev_list_entry_add(udev, dev_list, device, NULL, 1, 0);
		count++;
	}
	closedir(dir);
out:
	return count;
}
void udev_selinux_init(struct udev *udev)
{
	/* record the present security context */
	selinux_enabled = (is_selinux_enabled() > 0);
	info(udev, "selinux=%i\n", selinux_enabled);
	if (!selinux_enabled)
		return;
	matchpathcon_init_prefix(NULL, udev_get_dev_path(udev));
	if (getfscreatecon(&selinux_prev_scontext) < 0) {
		err(udev, "getfscreatecon failed\n");
		selinux_prev_scontext = NULL;
	}
}
Example #11
0
int udev_node_remove(struct udev_device *dev)
{
	struct udev *udev = udev_device_get_udev(dev);
	struct udev_list_entry *list_entry;
	const char *devnode;
	struct stat stats;
	struct udev_device *dev_check;
	char filename[UTIL_PATH_SIZE];
	int err = 0;

	/* remove/update symlinks, remove symlinks from name index */
	udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev))
		link_update(dev, udev_list_entry_get_name(list_entry), 0);

	devnode = udev_device_get_devnode(dev);
	if (devnode == NULL)
		goto out;

	if (stat(devnode, &stats) != 0) {
		info(udev, "device node '%s' not found\n", devnode);
		goto out;
	}

	if (stats.st_rdev != udev_device_get_devnum(dev)) {
		info(udev, "device node '%s' points to a different device, skip removal\n", devnode);
		err = -1;
		goto out;
	}

	dev_check = udev_device_new_from_syspath(udev, udev_device_get_syspath(dev));
	if (dev_check != NULL) {
		/* do not remove device node if the same sys-device is re-created in the meantime */
		info(udev, "keeping device node of existing device'%s'\n", devnode);
		udev_device_unref(dev_check);
		goto out;
	}

	info(udev, "removing device node '%s'\n", devnode);
	err = util_unlink_secure(udev, devnode);
	if (err == 0)
		util_delete_path(udev, devnode);

	/* remove /dev/{block,char}/$major:$minor */
	snprintf(filename, sizeof(filename), "%s/%s/%u:%u",
		 udev_get_dev_path(udev),
		 strcmp(udev_device_get_subsystem(dev), "block") == 0 ? "block" : "char",
		 major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)));
	unlink(filename);
out:
	return err;
}
static int setup_inotify(struct userdata *u) {
    char *dev_snd;
    int r;

    if (u->inotify_fd >= 0)
        return 0;

    if ((u->inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
        pa_log("inotify_init1() failed: %s", pa_cstrerror(errno));
        return -1;
    }

    dev_snd = pa_sprintf_malloc("%s/snd", udev_get_dev_path(u->udev));
    r = inotify_add_watch(u->inotify_fd, dev_snd, IN_ATTRIB|IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF);
    pa_xfree(dev_snd);

    if (r < 0) {
        int saved_errno = errno;

        pa_close(u->inotify_fd);
        u->inotify_fd = -1;

        if (saved_errno == ENOENT) {
            pa_log_debug("/dev/snd/ is apparently not existing yet, retrying to create inotify watch later.");
            return 0;
        }

        if (saved_errno == ENOSPC) {
            pa_log("You apparently ran out of inotify watches, probably because Tracker/Beagle took them all away. "
                   "I wished people would do their homework first and fix inotify before using it for watching whole "
                   "directory trees which is something the current inotify is certainly not useful for. "
                   "Please make sure to drop the Tracker/Beagle guys a line complaining about their broken use of inotify.");
            return 0;
        }

        pa_log("inotify_add_watch() failed: %s", pa_cstrerror(saved_errno));
        return -1;
    }

    pa_assert_se(u->inotify_io = u->core->mainloop->io_new(u->core->mainloop, u->inotify_fd, PA_IO_EVENT_INPUT, inotify_cb, u));

    return 0;
}
Example #13
0
void udev_node_remove(struct udev_device *dev)
{
        struct udev *udev = udev_device_get_udev(dev);
        struct udev_list_entry *list_entry;
        const char *devnode;
        struct stat stats;
        struct udev_device *dev_check;
        char filename[UTIL_PATH_SIZE];

        /* remove/update symlinks, remove symlinks from name index */
        udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev))
                link_update(dev, udev_list_entry_get_name(list_entry), 0);

        /* remove /dev/{block,char}/$major:$minor */
        snprintf(filename, sizeof(filename), "%s/%s/%u:%u",
                 udev_get_dev_path(udev),
                 strcmp(udev_device_get_subsystem(dev), "block") == 0 ? "block" : "char",
                 major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)));
        unlink(filename);
}
Example #14
0
struct udev_device *udev_watch_lookup(struct udev *udev, int wd)
{
    char filename[UTIL_PATH_SIZE];
    char buf[UTIL_PATH_SIZE];
    ssize_t syslen;
    ssize_t len;

    if (inotify_fd < 0 || wd < 0)
        return NULL;

    snprintf(filename, sizeof(filename), "%s/.udev/watch/%d", udev_get_dev_path(udev), wd);
    syslen = util_strlcpy(buf, udev_get_sys_path(udev), sizeof(buf));
    len = readlink(filename, &buf[syslen], sizeof(buf)-syslen);
    if (len > 0 || len < (ssize_t)(sizeof(buf)-syslen)) {
        buf[syslen + len] = '\0';
        return udev_device_new_from_syspath(udev, buf);
    }

    return NULL;
}
Example #15
0
void udev_watch_end(struct udev *udev, struct udev_device *dev)
{
    int wd;
    char filename[UTIL_PATH_SIZE];

    if (inotify_fd < 0)
        return;

    wd = udev_device_get_watch_handle(dev);
    if (wd < 0)
        return;

    info(udev, "removing watch on '%s'\n", udev_device_get_devnode(dev));
    inotify_rm_watch(inotify_fd, wd);

    snprintf(filename, sizeof(filename), "%s/.udev/watch/%d", udev_get_dev_path(udev), wd);
    unlink(filename);

    udev_device_set_watch_handle(dev, -1);
}
Example #16
0
void udev_watch_begin(struct udev *udev, struct udev_device *dev)
{
    char filename[UTIL_PATH_SIZE];
    int wd;

    if (inotify_fd < 0)
        return;

    info(udev, "adding watch on '%s'\n", udev_device_get_devnode(dev));
    wd = inotify_add_watch(inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE);
    if (wd < 0) {
        err(udev, "inotify_add_watch(%d, %s, %o) failed: %m\n",
            inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE);
    }

    snprintf(filename, sizeof(filename), "%s/.udev/watch/%d", udev_get_dev_path(udev), wd);
    util_create_path(udev, filename);
    unlink(filename);
    symlink(udev_device_get_devpath(dev), filename);

    udev_device_set_watch_handle(dev, wd);
}
Example #17
0
/* move any old watches directory out of the way, and then restore
 * the watches
 */
void udev_watch_restore(struct udev *udev)
{
    char filename[UTIL_PATH_SIZE], oldname[UTIL_PATH_SIZE];

    if (inotify_fd < 0)
        return;

    util_strlcpy(oldname, udev_get_dev_path(udev), sizeof(oldname));
    util_strlcat(oldname, "/.udev/watch.old", sizeof(oldname));

    util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
    util_strlcat(filename, "/.udev/watch", sizeof(filename));

    if (rename(filename, oldname) == 0) {
        DIR *dir;
        struct dirent *ent;

        dir = opendir(oldname);
        if (dir == NULL) {
            err(udev, "unable to open old watches dir '%s', old watches will not be restored: %m", oldname);
            return;
        }

        while ((ent = readdir(dir)) != NULL) {
            char path[UTIL_PATH_SIZE];
            char buf[UTIL_PATH_SIZE];
            ssize_t syslen;
            ssize_t len;
            struct udev_device *dev;

            if (ent->d_name[0] < '0' || ent->d_name[0] > '9')
                continue;

            util_strlcpy(path, oldname, sizeof(path));
            util_strlcat(path, "/", sizeof(path));
            util_strlcat(path, ent->d_name, sizeof(path));

            syslen = util_strlcpy(buf, udev_get_sys_path(udev), sizeof(buf));
            len = readlink(path, &buf[syslen], sizeof(buf)-syslen);
            if (len <= 0 || len >= (ssize_t)(sizeof(buf)-syslen)) {
                unlink(path);
                continue;
            }
            buf[syslen + len] = '\0';
            dbg(udev, "old watch to '%s' found\n", buf);
            dev = udev_device_new_from_syspath(udev, buf);
            if (dev == NULL) {
                unlink(path);
                continue;
            }

            info(udev, "restoring old watch on '%s'\n", udev_device_get_devnode(dev));
            udev_watch_begin(udev, dev);

            udev_device_unref(dev);
            unlink(path);
        }

        closedir(dir);
        rmdir(oldname);

    } else if (errno != ENOENT) {
        err(udev, "unable to move watches dir '%s', old watches will not be restored: %m", filename);
    }
}
Example #18
0
int main(int argc, char *argv[])
{
	struct udev *udev;
	static const struct option options[] = {
		{ "export", no_argument, NULL, 'x' },
		{ "debug", no_argument, NULL, 'd' },
		{ "help", no_argument, NULL, 'h' },
		{}
	};
	char **devices;
	FILE *fp;
	struct mntent *mnt;
	int rc = 1;

	udev = udev_new();
	if (udev == NULL)
		goto exit;

	udev_log_init("fstab_id");
	udev_set_log_fn(udev, log_fn);

	while (1) {
		int option;

		option = getopt_long(argc, argv, "dxh", options, NULL);
		if (option == -1)
			break;

		switch (option) {
		case 'd':
			debug = 1;
			if (udev_get_log_priority(udev) < LOG_INFO)
				udev_set_log_priority(udev, LOG_INFO);
			break;
		case 'h':
			printf("Usage: fstab_id [OPTIONS] name [...]\n"
			       "  --export        print environment keys\n"
			       "  --debug         debug to stderr\n"
			       "  --help          print this help text\n\n");
			goto exit;
		case 'x':
			break;
		default:
			rc = 2;
			goto exit;
		}
	}

	devices = &argv[optind];
	if (devices[0] == NULL) {
		fprintf(stderr, "error: missing device(s) to match\n");
		rc = 3;
		goto exit;
	}

	fp = setmntent ("/etc/fstab", "r");
	if (fp == NULL) {
		fprintf(stderr, "error: opening fstab: %s\n", strerror(errno));
		rc = 4;
		goto exit;
	}

	while (1) {
		mnt = getmntent(fp);
		if (mnt == NULL)
			break;

		info(udev, "found '%s'@'%s'\n", mnt->mnt_fsname, mnt->mnt_dir);

		/* skip root device */
		if (strcmp(mnt->mnt_dir, "/") == 0)
			continue;

		/* match LABEL */
		if (strncmp(mnt->mnt_fsname, "LABEL=", 6) == 0) {
			const char *label;
			char str[256];

			label = &mnt->mnt_fsname[6];
			if (label[0] == '"' || label[0] == '\'') {
				char *pos;

				util_strscpy(str, sizeof(str), &label[1]);
				pos = strrchr(str, label[0]);
				if (pos == NULL)
					continue;
				pos[0] = '\0';
				label = str;
			}
			if (matches_device_list(udev, devices, label)) {
				print_fstab_entry(udev, mnt);
				rc = 0;
				break;
			}
			continue;
		}

		/* match UUID */
		if (strncmp(mnt->mnt_fsname, "UUID=", 5) == 0) {
			const char *uuid;
			char str[256];

			uuid = &mnt->mnt_fsname[5];
			if (uuid[0] == '"' || uuid[0] == '\'') {
				char *pos;

				util_strscpy(str, sizeof(str), &uuid[1]);
				pos = strrchr(str, uuid[0]);
				if (pos == NULL)
					continue;
				pos[0] = '\0';
				uuid = str;
			}
			if (matches_device_list(udev, devices, uuid)) {
				print_fstab_entry(udev, mnt);
				rc = 0;
				break;
			}
			continue;
		}

		/* only devices */
		if (strncmp(mnt->mnt_fsname, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) != 0)
			continue;

		if (matches_device_list(udev, devices, &mnt->mnt_fsname[strlen(udev_get_dev_path(udev))+1])) {
			print_fstab_entry(udev, mnt);
			rc = 0;
			break;
		}
	}
	endmntent(fp);

exit:
	udev_unref(udev);
	udev_log_close();
	return rc;
}
static void verify_access(struct userdata *u, struct device *d) {
    char *cd;
    pa_card *card;
    pa_bool_t accessible;

    pa_assert(u);
    pa_assert(d);

    cd = pa_sprintf_malloc("%s/snd/controlC%s", udev_get_dev_path(u->udev), path_get_card_id(d->path));
    accessible = access(cd, R_OK|W_OK) >= 0;
    pa_log_debug("%s is accessible: %s", cd, pa_yes_no(accessible));

    pa_xfree(cd);

    if (d->module == PA_INVALID_INDEX) {

        /* If we are not loaded, try to load */

        if (accessible) {
            pa_module *m;
            pa_bool_t busy;

            /* Check if any of the PCM devices that belong to this
             * card are currently busy. If they are, don't try to load
             * right now, to make sure the probing phase can
             * successfully complete. When the current user of the
             * device closes it we will get another notification via
             * inotify and can then recheck. */

            busy = is_card_busy(path_get_card_id(d->path));
            pa_log_debug("%s is busy: %s", d->path, pa_yes_no(busy));

            if (!busy) {

                /* So, why do we rate limit here? It's certainly ugly,
                 * but there seems to be no other way. Problem is
                 * this: if we are unable to configure/probe an audio
                 * device after opening it we will close it again and
                 * the module initialization will fail. This will then
                 * cause an inotify event on the device node which
                 * will be forwarded to us. We then try to reopen the
                 * audio device again, practically entering a busy
                 * loop.
                 *
                 * A clean fix would be if we would be able to ignore
                 * our own inotify close events. However, inotify
                 * lacks such functionality. Also, during probing of
                 * the device we cannot really distinguish between
                 * other processes causing EBUSY or ourselves, which
                 * means we have no way to figure out if the probing
                 * during opening was canceled by a "try again"
                 * failure or a "fatal" failure. */

                if (pa_ratelimit_test(&d->ratelimit, PA_LOG_DEBUG)) {
                    pa_log_debug("Loading module-alsa-card with arguments '%s'", d->args);
                    m = pa_module_load(u->core, "module-alsa-card", d->args);

                    if (m) {
                        d->module = m->index;
                        pa_log_info("Card %s (%s) module loaded.", d->path, d->card_name);
                    } else
                        pa_log_info("Card %s (%s) failed to load module.", d->path, d->card_name);
                } else
                    pa_log_warn("Tried to configure %s (%s) more often than %u times in %llus",
                                d->path,
                                d->card_name,
                                d->ratelimit.burst,
                                (long long unsigned) (d->ratelimit.interval / PA_USEC_PER_SEC));
            }
        }

    } else {

        /* If we are already loaded update suspend status with
         * accessible boolean */

        if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD)))
            pa_card_suspend(card, !accessible, PA_SUSPEND_SESSION);
    }
}