static int exec_list(struct udev_enumerate *udev_enumerate, const char *action, Set *settle_set) { struct udev_list_entry *entry; int r; udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) { char filename[UTIL_PATH_SIZE]; const char *syspath; _cleanup_close_ int fd = -1; syspath = udev_list_entry_get_name(entry); if (verbose) printf("%s\n", syspath); if (dry_run) continue; strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL); fd = open(filename, O_WRONLY|O_CLOEXEC); if (fd < 0) continue; if (settle_set) { r = set_put_strdup(settle_set, syspath); if (r < 0) return log_oom(); } if (write(fd, action, strlen(action)) < 0) log_debug_errno(errno, "error writing '%s' to '%s': %m", action, filename); }
/* 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]; util_path_encode(slink + strlen("/dev"), name_enc, sizeof(name_enc)); strscpyl(dirname, sizeof(dirname), "/run/udev/links/", name_enc, NULL); strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL); if (!add && unlink(filename) == 0) rmdir(dirname); target = link_find_prioritized(dev, add, dirname, buf, sizeof(buf)); if (target == NULL) { log_debug("no reference left, remove '%s'", slink); if (unlink(slink) == 0) util_delete_path(udev, slink); } else { log_debug("creating link '%s' to '%s'", slink, target); node_symlink(dev, target, slink); } if (add) { int err; do { int fd; err = mkdir_parents(filename, 0755); 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); } }
static int parse_argv(int argc, char *argv[]) { static const struct option options[] = { { "action", required_argument, NULL, 'a' }, { "resolve-names", required_argument, NULL, 'N' }, { "version", no_argument, NULL, 'V' }, { "help", no_argument, NULL, 'h' }, {} }; int c; while ((c = getopt_long(argc, argv, "a:N:Vh", options, NULL)) >= 0) switch (c) { case 'a': arg_action = optarg; break; case 'N': if (streq (optarg, "early")) { arg_resolve_names = 1; } else if (streq (optarg, "late")) { arg_resolve_names = 0; } else if (streq (optarg, "never")) { arg_resolve_names = -1; } else { log_error("resolve-names must be early, late or never"); return -EINVAL; } break; case 'V': return print_version(); case 'h': return help(); case '?': return -EINVAL; default: assert_not_reached("Unknown option"); } if (!argv[optind]) { log_error("syspath parameter missing."); return -EINVAL; } /* add /sys if needed */ if (!startswith(argv[optind], "/sys")) strscpyl(arg_syspath, sizeof(arg_syspath), "/sys", argv[optind], NULL); else strscpy(arg_syspath, sizeof(arg_syspath), argv[optind]); return 1; }
static void exec_list(struct udev_enumerate *udev_enumerate, const char *action) { struct udev_list_entry *entry; udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) { char filename[UTIL_PATH_SIZE]; int fd; if (verbose) printf("%s\n", udev_list_entry_get_name(entry)); if (dry_run) continue; strscpyl(filename, sizeof(filename), udev_list_entry_get_name(entry), "/uevent", NULL); fd = open(filename, O_WRONLY|O_CLOEXEC); if (fd < 0) continue; if (write(fd, action, strlen(action)) < 0) log_debug_errno(errno, "error writing '%s' to '%s': %m", action, filename); close(fd); }
static void print_property(struct udev_device *dev, bool test, const char *name, const char *value) { char s[256]; s[0] = '\0'; if (streq(name, "TYPE")) { udev_builtin_add_property(dev, test, "ID_FS_TYPE", value); } else if (streq(name, "USAGE")) { udev_builtin_add_property(dev, test, "ID_FS_USAGE", value); } else if (streq(name, "VERSION")) { udev_builtin_add_property(dev, test, "ID_FS_VERSION", value); } else if (streq(name, "UUID")) { blkid_safe_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_FS_UUID", s); blkid_encode_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_FS_UUID_ENC", s); } else if (streq(name, "UUID_SUB")) { blkid_safe_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB", s); blkid_encode_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB_ENC", s); } else if (streq(name, "LABEL")) { blkid_safe_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_FS_LABEL", s); blkid_encode_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_FS_LABEL_ENC", s); } else if (streq(name, "PTTYPE")) { udev_builtin_add_property(dev, test, "ID_PART_TABLE_TYPE", value); } else if (streq(name, "PTUUID")) { udev_builtin_add_property(dev, test, "ID_PART_TABLE_UUID", value); } else if (streq(name, "PART_ENTRY_NAME")) { blkid_encode_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_PART_ENTRY_NAME", s); } else if (streq(name, "PART_ENTRY_TYPE")) { blkid_encode_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_PART_ENTRY_TYPE", s); } else if (startswith(name, "PART_ENTRY_")) { strscpyl(s, sizeof(s), "ID_", name, NULL); udev_builtin_add_property(dev, test, s, value); } else if (streq(name, "SYSTEM_ID")) { blkid_encode_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_FS_SYSTEM_ID", s); } else if (streq(name, "PUBLISHER_ID")) { blkid_encode_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_FS_PUBLISHER_ID", s); } else if (streq(name, "APPLICATION_ID")) { blkid_encode_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_FS_APPLICATION_ID", s); } else if (streq(name, "BOOT_SYSTEM_ID")) { blkid_encode_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_FS_BOOT_SYSTEM_ID", s); } }
static int adm_builtin(struct udev *udev, int argc, char *argv[]) { static const struct option options[] = { { "help", no_argument, NULL, 'h' }, {} }; char *command = NULL; char *syspath = NULL; char filename[UTIL_PATH_SIZE]; struct udev_device *dev = NULL; enum udev_builtin_cmd cmd; int rc = EXIT_SUCCESS, c; while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) switch (c) { case 'h': help(udev); goto out; } command = argv[optind++]; if (command == NULL) { fprintf(stderr, "command missing\n"); help(udev); rc = 2; goto out; } syspath = argv[optind++]; if (syspath == NULL) { fprintf(stderr, "syspath missing\n"); rc = 3; goto out; } udev_builtin_init(udev); cmd = udev_builtin_lookup(command); if (cmd >= UDEV_BUILTIN_MAX) { fprintf(stderr, "unknown command '%s'\n", command); help(udev); rc = 5; goto out; } /* add /sys if needed */ if (!startswith(syspath, "/sys")) strscpyl(filename, sizeof(filename), "/sys", syspath, NULL); else strscpy(filename, sizeof(filename), syspath); util_remove_trailing_chars(filename, '/'); dev = udev_device_new_from_syspath(udev, filename); if (dev == NULL) { fprintf(stderr, "unable to open device '%s'\n\n", filename); rc = 4; goto out; } rc = udev_builtin_run(dev, cmd, command, true); if (rc < 0) { fprintf(stderr, "error executing '%s', exit code %i\n\n", command, rc); rc = 6; } out: udev_device_unref(dev); udev_builtin_exit(udev); return rc; }
static int node_symlink(struct udev_device *dev, const char *node, const char *slink) { struct stat stats; char target[UTIL_PATH_SIZE]; char *s; size_t l; char slink_tmp[UTIL_PATH_SIZE + 32]; int i = 0; int tail = 0; int err = 0; /* use relative link */ target[0] = '\0'; while (node[i] && (node[i] == slink[i])) { if (node[i] == '/') tail = i+1; i++; } s = target; l = sizeof(target); while (slink[i] != '\0') { if (slink[i] == '/') l = strpcpy(&s, l, "../"); i++; } l = strscpy(s, l, &node[tail]); if (l == 0) { err = -EINVAL; goto exit; } /* preserve link with correct target, do not replace node of other device */ if (lstat(slink, &stats) == 0) { if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { log_error("conflicting device node '%s' found, link to '%s' will not be created", slink, node); goto exit; } else if (S_ISLNK(stats.st_mode)) { char buf[UTIL_PATH_SIZE]; int len; len = readlink(slink, buf, sizeof(buf)); if (len > 0 && len < (int)sizeof(buf)) { buf[len] = '\0'; if (streq(target, buf)) { log_debug("preserve already existing symlink '%s' to '%s'", slink, target); label_fix(slink, true, false); utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW); goto exit; } } } } else { log_debug("creating symlink '%s' to '%s'", slink, target); do { err = mkdir_parents_label(slink, 0755); if (err != 0 && err != -ENOENT) break; mac_selinux_create_file_prepare(slink, S_IFLNK); err = symlink(target, slink); if (err != 0) err = -errno; mac_selinux_create_file_clear(); } while (err == -ENOENT); if (err == 0) goto exit; } log_debug("atomically replace '%s'", slink); strscpyl(slink_tmp, sizeof(slink_tmp), slink, ".tmp-", udev_device_get_id_filename(dev), NULL); unlink(slink_tmp); do { err = mkdir_parents_label(slink_tmp, 0755); if (err != 0 && err != -ENOENT) break; mac_selinux_create_file_prepare(slink_tmp, S_IFLNK); err = symlink(target, slink_tmp); if (err != 0) err = -errno; mac_selinux_create_file_clear(); } while (err == -ENOENT); if (err != 0) { log_error_errno(errno, "symlink '%s' '%s' failed: %m", target, slink_tmp); goto exit; } err = rename(slink_tmp, slink); if (err != 0) { log_error_errno(errno, "rename '%s' '%s' failed: %m", slink_tmp, slink); unlink(slink_tmp); } exit: return err; }
int main(int argc, char *argv[]) { _cleanup_udev_unref_ struct udev *udev = NULL; _cleanup_udev_event_unref_ struct udev_event *event = NULL; _cleanup_udev_device_unref_ struct udev_device *dev = NULL; _cleanup_udev_rules_unref_ struct udev_rules *rules = NULL; char syspath[UTIL_PATH_SIZE]; const char *devpath; const char *action; sigset_t mask, sigmask_orig; int err; err = fake_filesystems(); if (err < 0) return EXIT_FAILURE; udev = udev_new(); if (udev == NULL) return EXIT_FAILURE; log_debug("version %s", VERSION); mac_selinux_init("/dev"); sigprocmask(SIG_SETMASK, NULL, &sigmask_orig); action = argv[1]; if (action == NULL) { log_error("action missing"); goto out; } devpath = argv[2]; if (devpath == NULL) { log_error("devpath missing"); goto out; } rules = udev_rules_new(udev, 1); strscpyl(syspath, sizeof(syspath), "/sys", devpath, NULL); dev = udev_device_new_from_synthetic_event(udev, syspath, action); if (dev == NULL) { log_debug("unknown device '%s'", devpath); goto out; } event = udev_event_new(dev); sigfillset(&mask); sigprocmask(SIG_SETMASK, &mask, &sigmask_orig); event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); if (event->fd_signal < 0) { fprintf(stderr, "error creating signalfd\n"); goto out; } /* do what devtmpfs usually provides us */ if (udev_device_get_devnode(dev) != NULL) { mode_t mode = 0600; if (streq(udev_device_get_subsystem(dev), "block")) mode |= S_IFBLK; else mode |= S_IFCHR; if (!streq(action, "remove")) { mkdir_parents_label(udev_device_get_devnode(dev), 0755); mknod(udev_device_get_devnode(dev), mode, udev_device_get_devnum(dev)); } else { unlink(udev_device_get_devnode(dev)); rmdir_parents(udev_device_get_devnode(dev), "/"); } } udev_event_execute_rules(event, 3 * USEC_PER_SEC, USEC_PER_SEC, NULL, rules, &sigmask_orig); udev_event_execute_run(event, 3 * USEC_PER_SEC, USEC_PER_SEC, NULL); out: if (event != NULL && event->fd_signal >= 0) close(event->fd_signal); mac_selinux_finish(); return err ? EXIT_FAILURE : EXIT_SUCCESS; }
static int builtin_firmware(struct udev_device *dev, int argc, char *argv[], bool test) { struct udev *udev = udev_device_get_udev(dev); static const char *searchpath[] = { FIRMWARE_PATH }; char loadpath[UTIL_PATH_SIZE]; char datapath[UTIL_PATH_SIZE]; char fwpath[UTIL_PATH_SIZE]; const char *firmware; FILE *fwfile = NULL; struct utsname kernel; struct stat statbuf; unsigned int i; int rc = EXIT_SUCCESS; firmware = udev_device_get_property_value(dev, "FIRMWARE"); if (firmware == NULL) { log_error("firmware parameter missing"); rc = EXIT_FAILURE; goto exit; } /* lookup firmware file */ uname(&kernel); for (i = 0; i < ELEMENTSOF(searchpath); i++) { strscpyl(fwpath, sizeof(fwpath), searchpath[i], kernel.release, "/", firmware, NULL); fwfile = fopen(fwpath, "re"); if (fwfile != NULL) break; strscpyl(fwpath, sizeof(fwpath), searchpath[i], firmware, NULL); fwfile = fopen(fwpath, "re"); if (fwfile != NULL) break; } strscpyl(loadpath, sizeof(loadpath), udev_device_get_syspath(dev), "/loading", NULL); if (fwfile == NULL) { log_debug("did not find firmware file '%s'", firmware); rc = EXIT_FAILURE; /* * Do not cancel the request in the initrd, the real root might have * the firmware file and the 'coldplug' run in the real root will find * this pending request and fulfill or cancel it. * */ if (!in_initrd()) set_loading(udev, loadpath, "-1"); goto exit; } if (stat(fwpath, &statbuf) < 0 || statbuf.st_size == 0) { if (!in_initrd()) set_loading(udev, loadpath, "-1"); rc = EXIT_FAILURE; goto exit; } if (!set_loading(udev, loadpath, "1")) goto exit; strscpyl(datapath, sizeof(datapath), udev_device_get_syspath(dev), "/data", NULL); if (!copy_firmware(udev, fwpath, datapath, statbuf.st_size)) { log_error("error sending firmware '%s' to device", firmware); set_loading(udev, loadpath, "-1"); rc = EXIT_FAILURE; goto exit; }; set_loading(udev, loadpath, "0"); exit: if (fwfile) fclose(fwfile); return rc; }
int main(int argc, char *argv[]) { _cleanup_udev_unref_ struct udev *udev = NULL; _cleanup_udev_event_unref_ struct udev_event *event = NULL; _cleanup_udev_device_unref_ struct udev_device *dev = NULL; _cleanup_udev_rules_unref_ struct udev_rules *rules = NULL; char syspath[UTIL_PATH_SIZE]; const char *devpath; const char *action; int err; err = fake_filesystems(); if (err < 0) return EXIT_FAILURE; udev = udev_new(); if (udev == NULL) return EXIT_FAILURE; log_debug("version %s", VERSION); mac_selinux_init("/dev"); action = argv[1]; if (action == NULL) { log_error("action missing"); goto out; } devpath = argv[2]; if (devpath == NULL) { log_error("devpath missing"); goto out; } rules = udev_rules_new(udev, 1); strscpyl(syspath, sizeof(syspath), "/sys", devpath, NULL); dev = udev_device_new_from_synthetic_event(udev, syspath, action); if (dev == NULL) { log_debug("unknown device '%s'", devpath); goto out; } event = udev_event_new(dev); assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGHUP, SIGCHLD, -1) >= 0); /* do what devtmpfs usually provides us */ if (udev_device_get_devnode(dev) != NULL) { mode_t mode = 0600; if (streq(udev_device_get_subsystem(dev), "block")) mode |= S_IFBLK; else mode |= S_IFCHR; if (!streq(action, "remove")) { mkdir_parents_label(udev_device_get_devnode(dev), 0755); mknod(udev_device_get_devnode(dev), mode, udev_device_get_devnum(dev)); } else { unlink(udev_device_get_devnode(dev)); rmdir_parents(udev_device_get_devnode(dev), "/"); } } udev_event_execute_rules(event, 3 * USEC_PER_SEC, USEC_PER_SEC, NULL, rules); udev_event_execute_run(event, 3 * USEC_PER_SEC, USEC_PER_SEC); out: mac_selinux_finish(); return err ? EXIT_FAILURE : EXIT_SUCCESS; }