static Set *new_matches(void) { Set *set; char *tmp; int r; set = set_new(NULL); if (!set) { log_oom(); return NULL; } tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"); if (!tmp) { log_oom(); set_free(set); return NULL; } r = set_consume(set, tmp); if (r < 0) { log_error_errno(r, "failed to add to set: %m"); set_free(set); return NULL; } return set; }
static int add_locales_from_libdir (Set *locales) { _cleanup_closedir_ DIR *dir = NULL; struct dirent *entry; int r; dir = opendir("/usr/lib/locale"); if (!dir) return errno == ENOENT ? 0 : -errno; FOREACH_DIRENT(entry, dir, return -errno) { char *z; dirent_ensure_type(dir, entry); if (entry->d_type != DT_DIR) continue; z = strdup(entry->d_name); if (!z) return -ENOMEM; r = set_consume(locales, z); if (r < 0 && r != -EEXIST) return r; } return 0; }
static Set *new_matches(void) { Set *set; char *tmp; int r; set = set_new(trivial_hash_func, trivial_compare_func); if (!set) { log_oom(); return NULL; } tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"); if (!tmp) { log_oom(); set_free(set); return NULL; } r = set_consume(set, tmp); if (r < 0) { log_error("failed to add to set: %s", strerror(-r)); set_free(set); return NULL; } return set; }
static int nftw_cb( const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf) { char *p, *e; int r; if (tflag != FTW_F) return 0; if (!endswith(fpath, ".map") && !endswith(fpath, ".map.gz")) return 0; p = strdup(basename(fpath)); if (!p) return FTW_STOP; e = endswith(p, ".map"); if (e) *e = 0; e = endswith(p, ".map.gz"); if (e) *e = 0; r = set_consume(keymaps, p); if (r < 0 && r != -EEXIST) return r; return 0; }
static int add_match(Set *set, const char *match) { _cleanup_free_ char *p = NULL; char *pattern = NULL; const char* prefix; pid_t pid; int r; if (strchr(match, '=')) prefix = ""; else if (strchr(match, '/')) { r = path_make_absolute_cwd(match, &p); if (r < 0) goto fail; match = p; prefix = "COREDUMP_EXE="; } else if (parse_pid(match, &pid) >= 0) prefix = "COREDUMP_PID="; else prefix = "COREDUMP_COMM="; pattern = strjoin(prefix, match, NULL); if (!pattern) { r = -ENOMEM; goto fail; } log_debug("Adding pattern: %s", pattern); r = set_consume(set, pattern); if (r < 0) goto fail; return 0; fail: return log_error_errno(r, "Failed to add match: %m"); }
int set_put_strdup(Set *s, const char *p) { char *c; int r; assert(s); assert(p); c = strdup(p); if (!c) return -ENOMEM; r = set_consume(s, c); if (r == -EEXIST) return 0; return r; }
static int add_locales_from_libdir (Set *locales) { _cleanup_closedir_ DIR *dir; struct dirent *entry; int r; dir = opendir("/usr/lib/locale"); if (!dir) { log_error("Failed to open locale directory: %m"); return -errno; } errno = 0; while ((entry = readdir(dir))) { char *z; if (entry->d_type != DT_DIR) continue; if (ignore_file(entry->d_name)) continue; z = strdup(entry->d_name); if (!z) return log_oom(); r = set_consume(locales, z); if (r < 0 && r != -EEXIST) { log_error("Failed to add locale: %s", strerror(-r)); return r; } errno = 0; } if (errno > 0) { log_error("Failed to read locale directory: %m"); return -errno; } return 0; }
static int add_match(Set *set, const char *match) { int r = -ENOMEM; unsigned pid; const char* prefix; char *pattern = NULL; _cleanup_free_ char *p = NULL; if (strchr(match, '=')) prefix = ""; else if (strchr(match, '/')) { p = path_make_absolute_cwd(match); if (!p) goto fail; match = p; prefix = "COREDUMP_EXE="; } else if (safe_atou(match, &pid) == 0) prefix = "COREDUMP_PID="; else prefix = "COREDUMP_COMM="; pattern = strjoin(prefix, match, NULL); if (!pattern) goto fail; log_debug("Adding pattern: %s", pattern); r = set_consume(set, pattern); if (r < 0) { log_error("Failed to add pattern '%s': %s", pattern, strerror(-r)); goto fail; } return 0; fail: log_error("Failed to add match: %s", strerror(-r)); return r; }
static int nftw_cb( const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf) { char *p, *e; int r; if (tflag != FTW_F) return 0; if (!endswith(fpath, ".map") && !endswith(fpath, ".map.gz")) return 0; p = strdup(basename(fpath)); if (!p) return log_oom(); e = endswith(p, ".map"); if (e) *e = 0; e = endswith(p, ".map.gz"); if (e) *e = 0; r = set_consume(keymaps, p); if (r < 0 && r != -EEXIST) { log_error("Can't add keymap: %s", strerror(-r)); return r; } return 0; }
int mount_cgroup_controllers(char ***join_controllers) { int r; char buf[LINE_MAX]; _cleanup_set_free_free_ Set *controllers = NULL; _cleanup_fclose_ FILE *f; /* Mount all available cgroup controllers that are built into the kernel. */ f = fopen("/proc/cgroups", "re"); if (!f) { log_error("Failed to enumerate cgroup controllers: %m"); return 0; } controllers = set_new(string_hash_func, string_compare_func); if (!controllers) return log_oom(); /* Ignore the header line */ (void) fgets(buf, sizeof(buf), f); for (;;) { char *controller; int enabled = 0; if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) { if (feof(f)) break; log_error("Failed to parse /proc/cgroups."); return -EIO; } if (!enabled) { free(controller); continue; } r = set_consume(controllers, controller); if (r < 0) { log_error("Failed to add controller to set."); return r; } } for (;;) { MountPoint p = { .what = "cgroup", .type = "cgroup", .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV, .mode = MNT_IN_CONTAINER, }; char ***k = NULL; _cleanup_free_ char *options = NULL, *controller; controller = set_steal_first(controllers); if (!controller) break; if (join_controllers) for (k = join_controllers; *k; k++) if (strv_find(*k, controller)) break; if (k && *k) { char **i, **j; for (i = *k, j = *k; *i; i++) { if (!streq(*i, controller)) { char _cleanup_free_ *t; t = set_remove(controllers, *i); if (!t) { free(*i); continue; } } *(j++) = *i; } *j = NULL; options = strv_join(*k, ","); if (!options) return log_oom(); } else { options = controller; controller = NULL; } p.where = strappenda("/sys/fs/cgroup/", options); p.options = options; r = mount_one(&p, true); if (r < 0) return r; if (r > 0 && k && *k) { char **i; for (i = *k; *i; i++) { char *t = strappenda("/sys/fs/cgroup/", *i); r = symlink(options, t); if (r < 0 && errno != EEXIST) { log_error("Failed to create symlink %s: %m", t); return -errno; } } } } return 0; } static int nftw_cb( const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf) { /* No need to label /dev twice in a row... */ if (_unlikely_(ftwbuf->level == 0)) return FTW_CONTINUE; label_fix(fpath, false, false); /* /run/initramfs is static data and big, no need to * dynamically relabel its contents at boot... */ if (_unlikely_(ftwbuf->level == 1 && tflag == FTW_D && streq(fpath, "/run/initramfs"))) return FTW_SKIP_SUBTREE; return FTW_CONTINUE; }; int mount_setup(bool loaded_policy) { int r; unsigned i; for (i = 0; i < ELEMENTSOF(mount_table); i ++) { r = mount_one(mount_table + i, true); if (r < 0) return r; } /* Nodes in devtmpfs and /run need to be manually updated for * the appropriate labels, after mounting. The other virtual * API file systems like /sys and /proc do not need that, they * use the same label for all their files. */ if (loaded_policy) { usec_t before_relabel, after_relabel; char timespan[FORMAT_TIMESPAN_MAX]; before_relabel = now(CLOCK_MONOTONIC); nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); after_relabel = now(CLOCK_MONOTONIC); log_info("Relabelled /dev and /run in %s.", format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel, 0)); } /* Create a few default symlinks, which are normally created * by udevd, but some scripts might need them before we start * udevd. */ dev_setup(NULL); /* Mark the root directory as shared in regards to mount * propagation. The kernel defaults to "private", but we think * it makes more sense to have a default of "shared" so that * nspawn and the container tools work out of the box. If * specific setups need other settings they can reset the * propagation mode to private if needed. */ if (detect_container(NULL) <= 0) if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0) log_warning("Failed to set up the root directory for shared mount propagation: %m"); /* Create a few directories we always want around, Note that * sd_booted() checks for /run/systemd/system, so this mkdir * really needs to stay for good, otherwise software that * copied sd-daemon.c into their sources will misdetect * systemd. */ mkdir_label("/run/systemd", 0755); mkdir_label("/run/systemd/system", 0755); mkdir_label("/run/systemd/inaccessible", 0000); return 0; }
static int add_locales_from_archive(Set *locales) { /* Stolen from glibc... */ struct locarhead { uint32_t magic; /* Serial number. */ uint32_t serial; /* Name hash table. */ uint32_t namehash_offset; uint32_t namehash_used; uint32_t namehash_size; /* String table. */ uint32_t string_offset; uint32_t string_used; uint32_t string_size; /* Table with locale records. */ uint32_t locrectab_offset; uint32_t locrectab_used; uint32_t locrectab_size; /* MD5 sum hash table. */ uint32_t sumhash_offset; uint32_t sumhash_used; uint32_t sumhash_size; }; struct namehashent { /* Hash value of the name. */ uint32_t hashval; /* Offset of the name in the string table. */ uint32_t name_offset; /* Offset of the locale record. */ uint32_t locrec_offset; }; const struct locarhead *h; const struct namehashent *e; const void *p = MAP_FAILED; _cleanup_close_ int fd = -1; size_t sz = 0; struct stat st; size_t i; int r; fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC); if (fd < 0) return errno == ENOENT ? 0 : -errno; if (fstat(fd, &st) < 0) return -errno; if (!S_ISREG(st.st_mode)) return -EBADMSG; if (st.st_size < (off_t) sizeof(struct locarhead)) return -EBADMSG; p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (p == MAP_FAILED) return -errno; h = (const struct locarhead *) p; if (h->magic != 0xde020109 || h->namehash_offset + h->namehash_size > st.st_size || h->string_offset + h->string_size > st.st_size || h->locrectab_offset + h->locrectab_size > st.st_size || h->sumhash_offset + h->sumhash_size > st.st_size) { r = -EBADMSG; goto finish; } e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset); for (i = 0; i < h->namehash_size; i++) { char *z; if (e[i].locrec_offset == 0) continue; if (!utf8_is_valid((char*) p + e[i].name_offset)) continue; z = strdup((char*) p + e[i].name_offset); if (!z) { r = -ENOMEM; goto finish; } r = set_consume(locales, z); if (r < 0) goto finish; } r = 0; finish: if (p != MAP_FAILED) munmap((void*) p, sz); return r; }
/* Use this function only if you do not have direct access to /proc/self/mountinfo but the caller can open it * for you. This is the case when /proc is masked or not mounted. Otherwise, use bind_remount_recursive. */ int bind_remount_recursive_with_mountinfo( const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **blacklist, FILE *proc_self_mountinfo) { _cleanup_set_free_free_ Set *done = NULL; _cleanup_free_ char *cleaned = NULL; int r; assert(proc_self_mountinfo); /* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already * mounted, we reuse the mount and simply mark it MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to * all submounts we can access, too. When mounts are stacked on the same mount point we only care for each * individual "top-level" mount on each point, as we cannot influence/access the underlying mounts anyway. We * do not have any effect on future submounts that might get propagated, they migt be writable. This includes * future submounts that have been triggered via autofs. * * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the * remount operation. Note that we'll ignore the blacklist for the top-level path. */ cleaned = strdup(prefix); if (!cleaned) return -ENOMEM; path_simplify(cleaned, false); done = set_new(&path_hash_ops); if (!done) return -ENOMEM; for (;;) { _cleanup_set_free_free_ Set *todo = NULL; bool top_autofs = false; char *x; unsigned long orig_flags; todo = set_new(&path_hash_ops); if (!todo) return -ENOMEM; rewind(proc_self_mountinfo); for (;;) { _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL; int k; k = fscanf(proc_self_mountinfo, "%*s " /* (1) mount id */ "%*s " /* (2) parent id */ "%*s " /* (3) major:minor */ "%*s " /* (4) root */ "%ms " /* (5) mount point */ "%*s" /* (6) mount options (superblock) */ "%*[^-]" /* (7) optional fields */ "- " /* (8) separator */ "%ms " /* (9) file system type */ "%*s" /* (10) mount source */ "%*s" /* (11) mount options (bind mount) */ "%*[^\n]", /* some rubbish at the end */ &path, &type); if (k != 2) { if (k == EOF) break; continue; } r = cunescape(path, UNESCAPE_RELAX, &p); if (r < 0) return r; if (!path_startswith(p, cleaned)) continue; /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount we shall * operate on. */ if (!path_equal(cleaned, p)) { bool blacklisted = false; char **i; STRV_FOREACH(i, blacklist) { if (path_equal(*i, cleaned)) continue; if (!path_startswith(*i, cleaned)) continue; if (path_startswith(p, *i)) { blacklisted = true; log_debug("Not remounting %s blacklisted by %s, called for %s", p, *i, cleaned); break; } } if (blacklisted) continue; } /* Let's ignore autofs mounts. If they aren't * triggered yet, we want to avoid triggering * them, as we don't make any guarantees for * future submounts anyway. If they are * already triggered, then we will find * another entry for this. */ if (streq(type, "autofs")) { top_autofs = top_autofs || path_equal(cleaned, p); continue; } if (!set_contains(done, p)) { r = set_consume(todo, p); p = NULL; if (r == -EEXIST) continue; if (r < 0) return r; } } /* If we have no submounts to process anymore and if * the root is either already done, or an autofs, we * are done */ if (set_isempty(todo) && (top_autofs || set_contains(done, cleaned))) return 0; if (!set_contains(done, cleaned) && !set_contains(todo, cleaned)) { /* The prefix directory itself is not yet a mount, make it one. */ if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0) return -errno; orig_flags = 0; (void) get_mount_flags(cleaned, &orig_flags); orig_flags &= ~MS_RDONLY; if (mount(NULL, cleaned, NULL, (orig_flags & ~flags_mask)|MS_BIND|MS_REMOUNT|new_flags, NULL) < 0) return -errno; log_debug("Made top-level directory %s a mount point.", prefix); r = set_put_strdup(done, cleaned); if (r < 0) return r; } while ((x = set_steal_first(todo))) { r = set_consume(done, x); if (IN_SET(r, 0, -EEXIST)) continue; if (r < 0) return r; /* Deal with mount points that are obstructed by a later mount */ r = path_is_mount_point(x, NULL, 0); if (IN_SET(r, 0, -ENOENT)) continue; if (IN_SET(r, -EACCES, -EPERM)) { /* Even if root user invoke this, submounts under private FUSE or NFS mount points * may not be acceessed. E.g., * * $ bindfs --no-allow-other ~/mnt/mnt ~/mnt/mnt * $ bindfs --no-allow-other ~/mnt ~/mnt * * Then, root user cannot access the mount point ~/mnt/mnt. * In such cases, the submounts are ignored, as we have no way to manage them. */ log_debug_errno(r, "Failed to determine '%s' is mount point or not, ignoring: %m", x); continue; } if (r < 0) return r; /* Try to reuse the original flag set */ orig_flags = 0; (void) get_mount_flags(x, &orig_flags); orig_flags &= ~MS_RDONLY; if (mount(NULL, x, NULL, (orig_flags & ~flags_mask)|MS_BIND|MS_REMOUNT|new_flags, NULL) < 0) return -errno; log_debug("Remounted %s read-only.", x); } }
int devnode_acl_all(struct udev *udev, const char *seat, bool flush, bool del, uid_t old_uid, bool add, uid_t new_uid) { _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; struct udev_list_entry *item = NULL, *first = NULL; _cleanup_set_free_free_ Set *nodes = NULL; _cleanup_closedir_ DIR *dir = NULL; struct dirent *dent; Iterator i; char *n; int r; assert(udev); nodes = set_new(&string_hash_ops); if (!nodes) return -ENOMEM; e = udev_enumerate_new(udev); if (!e) return -ENOMEM; if (isempty(seat)) seat = "seat0"; /* We can only match by one tag in libudev. We choose * "uaccess" for that. If we could match for two tags here we * could add the seat name as second match tag, but this would * be hardly optimizable in libudev, and hence checking the * second tag manually in our loop is a good solution. */ r = udev_enumerate_add_match_tag(e, "uaccess"); if (r < 0) return r; r = udev_enumerate_add_match_is_initialized(e); if (r < 0) return r; r = udev_enumerate_scan_devices(e); if (r < 0) return r; first = udev_enumerate_get_list_entry(e); udev_list_entry_foreach(item, first) { _cleanup_udev_device_unref_ struct udev_device *d = NULL; const char *node, *sn; d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); if (!d) return -ENOMEM; sn = udev_device_get_property_value(d, "ID_SEAT"); if (isempty(sn)) sn = "seat0"; if (!streq(seat, sn)) continue; node = udev_device_get_devnode(d); /* In case people mistag devices with nodes, we need to ignore this */ if (!node) continue; n = strdup(node); if (!n) return -ENOMEM; log_debug("Found udev node %s for seat %s", n, seat); r = set_consume(nodes, n); if (r < 0) return r; }
static void test_struct(void) { Prioq *q; Set *s; unsigned previous = 0, i; int r; srand(0); q = prioq_new(test_compare); assert_se(q); s = set_new(test_hash, test_compare); assert_se(s); for (i = 0; i < SET_SIZE; i++) { struct test *t; t = new0(struct test, 1); assert_se(t); t->value = (unsigned) rand(); r = prioq_put(q, t, &t->idx); assert_se(r >= 0); if (i % 4 == 0) { r = set_consume(s, t); assert_se(r >= 0); } } for (;;) { struct test *t; t = set_steal_first(s); if (!t) break; r = prioq_remove(q, t, &t->idx); assert_se(r > 0); free(t); } for (i = 0; i < SET_SIZE * 3 / 4; i++) { struct test *t; assert_se(prioq_size(q) == (SET_SIZE * 3 / 4) - i); t = prioq_pop(q); assert_se(t); assert_se(previous <= t->value); previous = t->value; free(t); } assert_se(prioq_isempty(q)); prioq_free(q); assert_se(set_isempty(s)); set_free(s); }