Ejemplo n.º 1
0
static void patch_realtime(
                const char *dir,
                const char *fn,
                const struct stat *st,
                unsigned long long *realtime) {

        usec_t x;

#ifdef HAVE_XATTR
        uint64_t crtime;
        _cleanup_free_ const char *path = NULL;
#endif

        /* The timestamp was determined by the file name, but let's
         * see if the file might actually be older than the file name
         * suggested... */

        assert(dir);
        assert(fn);
        assert(st);
        assert(realtime);

        x = timespec_load(&st->st_ctim);
        if (x > 0 && x != (usec_t) -1 && x < *realtime)
                *realtime = x;

        x = timespec_load(&st->st_atim);
        if (x > 0 && x != (usec_t) -1 && x < *realtime)
                *realtime = x;

        x = timespec_load(&st->st_mtim);
        if (x > 0 && x != (usec_t) -1 && x < *realtime)
                *realtime = x;

#ifdef HAVE_XATTR
        /* Let's read the original creation time, if possible. Ideally
         * we'd just query the creation time the FS might provide, but
         * unfortunately there's currently no sane API to query
         * it. Hence let's implement this manually... */

        /* Unfortunately there is is not fgetxattrat(), so we need to
         * go via path here. :-( */

        path = strjoin(dir, "/", fn, NULL);
        if (!path)
                return;

        if (getxattr(path, "user.crtime_usec", &crtime, sizeof(crtime)) == sizeof(crtime)) {
                crtime = le64toh(crtime);

                if (crtime > 0 && crtime != (uint64_t) -1 && crtime < *realtime)
                        *realtime = crtime;
        }
#endif
}
Ejemplo n.º 2
0
int vconsole_write_data(Context *c) {
        _cleanup_strv_free_ char **l = NULL;
        struct stat st;
        int r;

        r = load_env_file(NULL, "/etc/vconsole.conf", NULL, &l);
        if (r < 0 && r != -ENOENT)
                return r;

        if (isempty(c->vc_keymap))
                l = strv_env_unset(l, "KEYMAP");
        else {
                _cleanup_free_ char *s = NULL;
                char **u;

                s = strappend("KEYMAP=", c->vc_keymap);
                if (!s)
                        return -ENOMEM;

                u = strv_env_set(l, s);
                if (!u)
                        return -ENOMEM;

                strv_free_and_replace(l, u);
        }

        if (isempty(c->vc_keymap_toggle))
                l = strv_env_unset(l, "KEYMAP_TOGGLE");
        else  {
                _cleanup_free_ char *s = NULL;
                char **u;

                s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
                if (!s)
                        return -ENOMEM;

                u = strv_env_set(l, s);
                if (!u)
                        return -ENOMEM;

                strv_free_and_replace(l, u);
        }

        if (strv_isempty(l)) {
                if (unlink("/etc/vconsole.conf") < 0)
                        return errno == ENOENT ? 0 : -errno;

                c->vc_mtime = USEC_INFINITY;
                return 0;
        }

        r = write_env_file_label("/etc/vconsole.conf", l);
        if (r < 0)
                return r;

        if (stat("/etc/vconsole.conf", &st) >= 0)
                c->vc_mtime = timespec_load(&st.st_mtim);

        return 0;
}
Ejemplo n.º 3
0
usec_t now(clockid_t clock_id) {
        struct timespec ts;

        assert_se(clock_gettime(clock_id, &ts) == 0);

        return timespec_load(&ts);
}
Ejemplo n.º 4
0
static void patch_realtime(
                int fd,
                const char *fn,
                const struct stat *st,
                unsigned long long *realtime) {

        usec_t x, crtime = 0;

        /* The timestamp was determined by the file name, but let's
         * see if the file might actually be older than the file name
         * suggested... */

        assert(fd >= 0);
        assert(fn);
        assert(st);
        assert(realtime);

        x = timespec_load(&st->st_ctim);
        if (x > 0 && x != USEC_INFINITY && x < *realtime)
                *realtime = x;

        x = timespec_load(&st->st_atim);
        if (x > 0 && x != USEC_INFINITY && x < *realtime)
                *realtime = x;

        x = timespec_load(&st->st_mtim);
        if (x > 0 && x != USEC_INFINITY && x < *realtime)
                *realtime = x;

        /* Let's read the original creation time, if possible. Ideally
         * we'd just query the creation time the FS might provide, but
         * unfortunately there's currently no sane API to query
         * it. Hence let's implement this manually... */

        if (fd_getcrtime_at(fd, fn, &crtime, 0) >= 0) {
                if (crtime < *realtime)
                        *realtime = crtime;
        }
}
Ejemplo n.º 5
0
static int load_clock_timestamp(uid_t uid, gid_t gid) {
        _cleanup_close_ int fd = -1;
        usec_t min = TIME_EPOCH * USEC_PER_SEC;
        usec_t ct;
        int r;

        /* Let's try to make sure that the clock is always
         * monotonically increasing, by saving the clock whenever we
         * have a new NTP time, or when we shut down, and restoring it
         * when we start again. This is particularly helpful on
         * systems lacking a battery backed RTC. We also will adjust
         * the time to at least the build time of systemd. */

        fd = open("/var/lib/systemd/clock", O_RDWR|O_CLOEXEC, 0644);
        if (fd >= 0) {
                struct stat st;
                usec_t stamp;

                /* check if the recorded time is later than the compiled-in one */
                r = fstat(fd, &st);
                if (r >= 0) {
                        stamp = timespec_load(&st.st_mtim);
                        if (stamp > min)
                                min = stamp;
                }

                /* Try to fix the access mode, so that we can still
                   touch the file after dropping priviliges */
                fchmod(fd, 0644);
                fchown(fd, uid, gid);

        } else
                /* create stamp file with the compiled-in date */
                touch_file("/var/lib/systemd/clock", true, min, uid, gid, 0644);

        ct = now(CLOCK_REALTIME);
        if (ct < min) {
                struct timespec ts;
                char date[FORMAT_TIMESTAMP_MAX];

                log_info("System clock time unset or jumped backwards, restoring from recorded timestamp: %s",
                         format_timestamp(date, sizeof(date), min));

                if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, min)) < 0)
                        log_error_errno(errno, "Failed to restore system clock: %m");
        }

        return 0;
}
Ejemplo n.º 6
0
int locale_write_data(Context *c, char ***settings) {
        _cleanup_strv_free_ char **l = NULL;
        struct stat st;
        int r, p;

        /* Set values will be returned as strv in *settings on success. */

        for (p = 0; p < _VARIABLE_LC_MAX; p++) {
                _cleanup_free_ char *t = NULL;
                char **u;
                const char *name;

                name = locale_variable_to_string(p);
                assert(name);

                if (isempty(c->locale[p]))
                        continue;

                if (asprintf(&t, "%s=%s", name, c->locale[p]) < 0)
                        return -ENOMEM;

                u = strv_env_set(l, t);
                if (!u)
                        return -ENOMEM;

                strv_free_and_replace(l, u);
        }

        if (strv_isempty(l)) {
                if (unlink("/etc/locale.conf") < 0)
                        return errno == ENOENT ? 0 : -errno;

                c->locale_mtime = USEC_INFINITY;
                return 0;
        }

        r = write_env_file_label("/etc/locale.conf", l);
        if (r < 0)
                return r;

        *settings = TAKE_PTR(l);

        if (stat("/etc/locale.conf", &st) >= 0)
                c->locale_mtime = timespec_load(&st.st_mtim);

        return 0;
}
Ejemplo n.º 7
0
int vconsole_read_data(Context *c, sd_bus_message *m) {
        struct stat st;
        usec_t t;
        int r;

        /* Do not try to re-read the file within single bus operation. */
        if (m) {
                if (m == c->vc_cache)
                        return 0;

                sd_bus_message_unref(c->vc_cache);
                c->vc_cache = sd_bus_message_ref(m);
        }

        if (stat("/etc/vconsole.conf", &st) < 0) {
                if (errno != ENOENT)
                        return -errno;

                c->vc_mtime = USEC_INFINITY;
                context_free_vconsole(c);
                return 0;
        }

        /* If mtime is not changed, then we do not need to re-read */
        t = timespec_load(&st.st_mtim);
        if (c->vc_mtime != USEC_INFINITY && t == c->vc_mtime)
                return 0;

        c->vc_mtime = t;
        context_free_vconsole(c);

        r = parse_env_file(NULL, "/etc/vconsole.conf", NEWLINE,
                           "KEYMAP",        &c->vc_keymap,
                           "KEYMAP_TOGGLE", &c->vc_keymap_toggle,
                           NULL);
        if (r < 0)
                return r;

        return 0;
}
Ejemplo n.º 8
0
static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
        ssize_t space, length;
        sd_lldp *lldp = userdata;
        struct timespec ts;

        assert(fd >= 0);
        assert(lldp);

        space = next_datagram_size_fd(fd);
        if (space < 0)
                return log_lldp_errno(space, "Failed to determine datagram size to read: %m");

        n = lldp_neighbor_new(space);
        if (!n)
                return -ENOMEM;

        length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
        if (length < 0) {
                if (errno == EAGAIN || errno == EINTR)
                        return 0;

                return log_lldp_errno(errno, "Failed to read LLDP datagram: %m");
        }

        if ((size_t) length != n->raw_size) {
                log_lldp("Packet size mismatch.");
                return -EINVAL;
        }

        /* Try to get the timestamp of this packet if it is known */
        if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
                triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
        else
                triple_timestamp_get(&n->timestamp);

        return lldp_handle_datagram(lldp, n);
}
Ejemplo n.º 9
0
static int image_make(
                const char *pretty,
                int dfd,
                const char *path,
                const char *filename,
                const struct stat *st,
                Image **ret) {

        _cleanup_free_ char *pretty_buffer = NULL;
        struct stat stbuf;
        bool read_only;
        int r;

        assert(dfd >= 0 || dfd == AT_FDCWD);
        assert(filename);

        /* We explicitly *do* follow symlinks here, since we want to allow symlinking trees, raw files and block
         * devices into /var/lib/machines/, and treat them normally.
         *
         * This function returns -ENOENT if we can't find the image after all, and -EMEDIUMTYPE if it's not a file we
         * recognize. */

        if (!st) {
                if (fstatat(dfd, filename, &stbuf, 0) < 0)
                        return -errno;

                st = &stbuf;
        }

        read_only =
                (path && path_startswith(path, "/usr")) ||
                (faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS);

        if (S_ISDIR(st->st_mode)) {
                _cleanup_close_ int fd = -1;
                unsigned file_attr = 0;

                if (!ret)
                        return 0;

                if (!pretty) {
                        r = extract_pretty(filename, NULL, &pretty_buffer);
                        if (r < 0)
                                return r;

                        pretty = pretty_buffer;
                }

                fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
                if (fd < 0)
                        return -errno;

                /* btrfs subvolumes have inode 256 */
                if (st->st_ino == 256) {

                        r = btrfs_is_filesystem(fd);
                        if (r < 0)
                                return r;
                        if (r) {
                                BtrfsSubvolInfo info;

                                /* It's a btrfs subvolume */

                                r = btrfs_subvol_get_info_fd(fd, 0, &info);
                                if (r < 0)
                                        return r;

                                r = image_new(IMAGE_SUBVOLUME,
                                              pretty,
                                              path,
                                              filename,
                                              info.read_only || read_only,
                                              info.otime,
                                              0,
                                              ret);
                                if (r < 0)
                                        return r;

                                if (btrfs_quota_scan_ongoing(fd) == 0) {
                                        BtrfsQuotaInfo quota;

                                        r = btrfs_subvol_get_subtree_quota_fd(fd, 0, &quota);
                                        if (r >= 0) {
                                                (*ret)->usage = quota.referenced;
                                                (*ret)->usage_exclusive = quota.exclusive;

                                                (*ret)->limit = quota.referenced_max;
                                                (*ret)->limit_exclusive = quota.exclusive_max;
                                        }
                                }

                                return 0;
                        }
                }

                /* If the IMMUTABLE bit is set, we consider the
                 * directory read-only. Since the ioctl is not
                 * supported everywhere we ignore failures. */
                (void) read_attr_fd(fd, &file_attr);

                /* It's just a normal directory. */
                r = image_new(IMAGE_DIRECTORY,
                              pretty,
                              path,
                              filename,
                              read_only || (file_attr & FS_IMMUTABLE_FL),
                              0,
                              0,
                              ret);
                if (r < 0)
                        return r;

                return 0;

        } else if (S_ISREG(st->st_mode) && endswith(filename, ".raw")) {
                usec_t crtime = 0;

                /* It's a RAW disk image */

                if (!ret)
                        return 0;

                (void) fd_getcrtime_at(dfd, filename, &crtime, 0);

                if (!pretty) {
                        r = extract_pretty(filename, ".raw", &pretty_buffer);
                        if (r < 0)
                                return r;

                        pretty = pretty_buffer;
                }

                r = image_new(IMAGE_RAW,
                              pretty,
                              path,
                              filename,
                              !(st->st_mode & 0222) || read_only,
                              crtime,
                              timespec_load(&st->st_mtim),
                              ret);
                if (r < 0)
                        return r;

                (*ret)->usage = (*ret)->usage_exclusive = st->st_blocks * 512;
                (*ret)->limit = (*ret)->limit_exclusive = st->st_size;

                return 0;

        } else if (S_ISBLK(st->st_mode)) {
                _cleanup_close_ int block_fd = -1;
                uint64_t size = UINT64_MAX;

                /* A block device */

                if (!ret)
                        return 0;

                if (!pretty) {
                        r = extract_pretty(filename, NULL, &pretty_buffer);
                        if (r < 0)
                                return r;

                        pretty = pretty_buffer;
                }

                block_fd = openat(dfd, filename, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
                if (block_fd < 0)
                        log_debug_errno(errno, "Failed to open block device %s/%s, ignoring: %m", path, filename);
                else {
                        /* Refresh stat data after opening the node */
                        if (fstat(block_fd, &stbuf) < 0)
                                return -errno;
                        st = &stbuf;

                        if (!S_ISBLK(st->st_mode)) /* Verify that what we opened is actually what we think it is */
                                return -ENOTTY;

                        if (!read_only) {
                                int state = 0;

                                if (ioctl(block_fd, BLKROGET, &state) < 0)
                                        log_debug_errno(errno, "Failed to issue BLKROGET on device %s/%s, ignoring: %m", path, filename);
                                else if (state)
                                        read_only = true;
                        }

                        if (ioctl(block_fd, BLKGETSIZE64, &size) < 0)
                                log_debug_errno(errno, "Failed to issue BLKGETSIZE64 on device %s/%s, ignoring: %m", path, filename);

                        block_fd = safe_close(block_fd);
                }

                r = image_new(IMAGE_BLOCK,
                              pretty,
                              path,
                              filename,
                              !(st->st_mode & 0222) || read_only,
                              0,
                              0,
                              ret);
                if (r < 0)
                        return r;

                if (size != 0 && size != UINT64_MAX)
                        (*ret)->usage = (*ret)->usage_exclusive = (*ret)->limit = (*ret)->limit_exclusive = size;

                return 0;
        }

        return -EMEDIUMTYPE;
}
Ejemplo n.º 10
0
int locale_read_data(Context *c, sd_bus_message *m) {
        struct stat st;
        int r;

        /* Do not try to re-read the file within single bus operation. */
        if (m) {
                if (m == c->locale_cache)
                        return 0;

                sd_bus_message_unref(c->locale_cache);
                c->locale_cache = sd_bus_message_ref(m);
        }

        r = stat("/etc/locale.conf", &st);
        if (r < 0 && errno != ENOENT)
                return -errno;

        if (r >= 0) {
                usec_t t;

                /* If mtime is not changed, then we do not need to re-read the file. */
                t = timespec_load(&st.st_mtim);
                if (c->locale_mtime != USEC_INFINITY && t == c->locale_mtime)
                        return 0;

                c->locale_mtime = t;
                context_free_locale(c);

                r = parse_env_file(NULL, "/etc/locale.conf", NEWLINE,
                                   "LANG",              &c->locale[VARIABLE_LANG],
                                   "LANGUAGE",          &c->locale[VARIABLE_LANGUAGE],
                                   "LC_CTYPE",          &c->locale[VARIABLE_LC_CTYPE],
                                   "LC_NUMERIC",        &c->locale[VARIABLE_LC_NUMERIC],
                                   "LC_TIME",           &c->locale[VARIABLE_LC_TIME],
                                   "LC_COLLATE",        &c->locale[VARIABLE_LC_COLLATE],
                                   "LC_MONETARY",       &c->locale[VARIABLE_LC_MONETARY],
                                   "LC_MESSAGES",       &c->locale[VARIABLE_LC_MESSAGES],
                                   "LC_PAPER",          &c->locale[VARIABLE_LC_PAPER],
                                   "LC_NAME",           &c->locale[VARIABLE_LC_NAME],
                                   "LC_ADDRESS",        &c->locale[VARIABLE_LC_ADDRESS],
                                   "LC_TELEPHONE",      &c->locale[VARIABLE_LC_TELEPHONE],
                                   "LC_MEASUREMENT",    &c->locale[VARIABLE_LC_MEASUREMENT],
                                   "LC_IDENTIFICATION", &c->locale[VARIABLE_LC_IDENTIFICATION],
                                   NULL);
                if (r < 0)
                        return r;
        } else {
                int p;

                c->locale_mtime = USEC_INFINITY;
                context_free_locale(c);

                /* Fill in what we got passed from systemd. */
                for (p = 0; p < _VARIABLE_LC_MAX; p++) {
                        const char *name;

                        name = locale_variable_to_string(p);
                        assert(name);

                        r = free_and_strdup(&c->locale[p], empty_to_null(getenv(name)));
                        if (r < 0)
                                return r;
                }
        }

        locale_simplify(c->locale);
        return 0;
}
Ejemplo n.º 11
0
int x11_write_data(Context *c) {
        _cleanup_fclose_ FILE *f = NULL;
        _cleanup_free_ char *temp_path = NULL;
        struct stat st;
        int r;

        if (isempty(c->x11_layout) &&
            isempty(c->x11_model) &&
            isempty(c->x11_variant) &&
            isempty(c->x11_options)) {

                if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
                        return errno == ENOENT ? 0 : -errno;

                c->vc_mtime = USEC_INFINITY;
                return 0;
        }

        mkdir_p_label("/etc/X11/xorg.conf.d", 0755);

        r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
        if (r < 0)
                return r;

        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
        (void) fchmod(fileno(f), 0644);

        fputs("# Written by systemd-localed(8), read by systemd-localed and Xorg. It's\n"
              "# probably wise not to edit this file manually. Use localectl(1) to\n"
              "# instruct systemd-localed to update it.\n"
              "Section \"InputClass\"\n"
              "        Identifier \"system-keyboard\"\n"
              "        MatchIsKeyboard \"on\"\n", f);

        if (!isempty(c->x11_layout))
                fprintf(f, "        Option \"XkbLayout\" \"%s\"\n", c->x11_layout);

        if (!isempty(c->x11_model))
                fprintf(f, "        Option \"XkbModel\" \"%s\"\n", c->x11_model);

        if (!isempty(c->x11_variant))
                fprintf(f, "        Option \"XkbVariant\" \"%s\"\n", c->x11_variant);

        if (!isempty(c->x11_options))
                fprintf(f, "        Option \"XkbOptions\" \"%s\"\n", c->x11_options);

        fputs("EndSection\n", f);

        r = fflush_sync_and_check(f);
        if (r < 0)
                goto fail;

        if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
                r = -errno;
                goto fail;
        }

        if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) >= 0)
                c->x11_mtime = timespec_load(&st.st_mtim);

        return 0;

fail:
        if (temp_path)
                (void) unlink(temp_path);

        return r;
}
Ejemplo n.º 12
0
int x11_read_data(Context *c, sd_bus_message *m) {
        _cleanup_fclose_ FILE *f = NULL;
        bool in_section = false;
        char line[LINE_MAX];
        struct stat st;
        usec_t t;
        int r;

        /* Do not try to re-read the file within single bus operation. */
        if (m) {
                if (m == c->x11_cache)
                        return 0;

                sd_bus_message_unref(c->x11_cache);
                c->x11_cache = sd_bus_message_ref(m);
        }

        if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) < 0) {
                if (errno != ENOENT)
                        return -errno;

                c->x11_mtime = USEC_INFINITY;
                context_free_x11(c);
                return 0;
        }

        /* If mtime is not changed, then we do not need to re-read */
        t = timespec_load(&st.st_mtim);
        if (c->x11_mtime != USEC_INFINITY && t == c->x11_mtime)
                return 0;

        c->x11_mtime = t;
        context_free_x11(c);

        f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
        if (!f)
                return -errno;

        while (fgets(line, sizeof(line), f)) {
                char *l;

                char_array_0(line);
                l = strstrip(line);

                if (IN_SET(l[0], 0, '#'))
                        continue;

                if (in_section && first_word(l, "Option")) {
                        _cleanup_strv_free_ char **a = NULL;

                        r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_QUOTES);
                        if (r < 0)
                                return r;

                        if (strv_length(a) == 3) {
                                char **p = NULL;

                                if (streq(a[1], "XkbLayout"))
                                        p = &c->x11_layout;
                                else if (streq(a[1], "XkbModel"))
                                        p = &c->x11_model;
                                else if (streq(a[1], "XkbVariant"))
                                        p = &c->x11_variant;
                                else if (streq(a[1], "XkbOptions"))
                                        p = &c->x11_options;

                                if (p) {
                                        free_and_replace(*p, a[2]);
                                }
                        }

                } else if (!in_section && first_word(l, "Section")) {
                        _cleanup_strv_free_ char **a = NULL;

                        r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_QUOTES);
                        if (r < 0)
                                return -ENOMEM;

                        if (strv_length(a) == 2 && streq(a[1], "InputClass"))
                                in_section = true;

                } else if (in_section && first_word(l, "EndSection"))
                        in_section = false;
        }

        return 0;
}
Ejemplo n.º 13
0
static int image_make(
                const char *pretty,
                int dfd,
                const char *path,
                const char *filename,
                Image **ret) {

        struct stat st;
        bool read_only;
        int r;

        assert(filename);

        /* We explicitly *do* follow symlinks here, since we want to
         * allow symlinking trees into /var/lib/machines/, and treat
         * them normally. */

        if (fstatat(dfd, filename, &st, 0) < 0)
                return -errno;

        read_only =
                (path && path_startswith(path, "/usr")) ||
                (faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS);

        if (S_ISDIR(st.st_mode)) {
                _cleanup_close_ int fd = -1;
                unsigned file_attr = 0;

                if (!ret)
                        return 1;

                if (!pretty)
                        pretty = filename;

                fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
                if (fd < 0)
                        return -errno;

                /* btrfs subvolumes have inode 256 */
                if (st.st_ino == 256) {

                        r = btrfs_is_filesystem(fd);
                        if (r < 0)
                                return r;
                        if (r) {
                                BtrfsSubvolInfo info;

                                /* It's a btrfs subvolume */

                                r = btrfs_subvol_get_info_fd(fd, 0, &info);
                                if (r < 0)
                                        return r;

                                r = image_new(IMAGE_SUBVOLUME,
                                              pretty,
                                              path,
                                              filename,
                                              info.read_only || read_only,
                                              info.otime,
                                              0,
                                              ret);
                                if (r < 0)
                                        return r;

                                if (btrfs_quota_scan_ongoing(fd) == 0) {
                                        BtrfsQuotaInfo quota;

                                        r = btrfs_subvol_get_subtree_quota_fd(fd, 0, &quota);
                                        if (r >= 0) {
                                                (*ret)->usage = quota.referenced;
                                                (*ret)->usage_exclusive = quota.exclusive;

                                                (*ret)->limit = quota.referenced_max;
                                                (*ret)->limit_exclusive = quota.exclusive_max;
                                        }
                                }

                                return 1;
                        }
                }

                /* If the IMMUTABLE bit is set, we consider the
                 * directory read-only. Since the ioctl is not
                 * supported everywhere we ignore failures. */
                (void) read_attr_fd(fd, &file_attr);

                /* It's just a normal directory. */
                r = image_new(IMAGE_DIRECTORY,
                              pretty,
                              path,
                              filename,
                              read_only || (file_attr & FS_IMMUTABLE_FL),
                              0,
                              0,
                              ret);
                if (r < 0)
                        return r;

                return 1;

        } else if (S_ISREG(st.st_mode) && endswith(filename, ".raw")) {
                usec_t crtime = 0;

                /* It's a RAW disk image */

                if (!ret)
                        return 1;

                fd_getcrtime_at(dfd, filename, &crtime, 0);

                if (!pretty)
                        pretty = strndupa(filename, strlen(filename) - 4);

                r = image_new(IMAGE_RAW,
                              pretty,
                              path,
                              filename,
                              !(st.st_mode & 0222) || read_only,
                              crtime,
                              timespec_load(&st.st_mtim),
                              ret);
                if (r < 0)
                        return r;

                (*ret)->usage = (*ret)->usage_exclusive = st.st_blocks * 512;
                (*ret)->limit = (*ret)->limit_exclusive = st.st_size;

                return 1;
        }

        return 0;
}
Ejemplo n.º 14
0
int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) {
        _cleanup_closedir_ DIR *d = NULL;
        struct stat exclude_st;
        int r;

        if (keep_free == 0 && max_use == 0)
                return 0;

        if (exclude_fd >= 0) {
                if (fstat(exclude_fd, &exclude_st) < 0)
                        return log_error_errno(errno, "Failed to fstat(): %m");
        }

        /* This algorithm will keep deleting the oldest file of the
         * user with the most coredumps until we are back in the size
         * limits. Note that vacuuming for journal files is different,
         * because we rely on rate-limiting of the messages there,
         * to avoid being flooded. */

        d = opendir("/var/lib/systemd/coredump");
        if (!d) {
                if (errno == ENOENT)
                        return 0;

                log_error_errno(errno, "Can't open coredump directory: %m");
                return -errno;
        }

        for (;;) {
                _cleanup_(vacuum_candidate_hasmap_freep) Hashmap *h = NULL;
                struct vacuum_candidate *worst = NULL;
                struct dirent *de;
                uint64_t sum = 0;

                rewinddir(d);

                FOREACH_DIRENT(de, d, goto fail) {
                        struct vacuum_candidate *c;
                        struct stat st;
                        uid_t uid;
                        usec_t t;

                        r = uid_from_file_name(de->d_name, &uid);
                        if (r < 0)
                                continue;

                        if (fstatat(dirfd(d), de->d_name, &st, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW) < 0) {
                                if (errno == ENOENT)
                                        continue;

                                log_warning("Failed to stat /var/lib/systemd/coredump/%s", de->d_name);
                                continue;
                        }

                        if (!S_ISREG(st.st_mode))
                                continue;

                        if (exclude_fd >= 0 &&
                            exclude_st.st_dev == st.st_dev &&
                            exclude_st.st_ino == st.st_ino)
                                continue;

                        r = hashmap_ensure_allocated(&h, NULL);
                        if (r < 0)
                                return log_oom();

                        t = timespec_load(&st.st_mtim);

                        c = hashmap_get(h, UINT32_TO_PTR(uid));
                        if (c) {

                                if (t < c->oldest_mtime) {
                                        char *n;

                                        n = strdup(de->d_name);
                                        if (!n)
                                                return log_oom();

                                        free(c->oldest_file);
                                        c->oldest_file = n;
                                        c->oldest_mtime = t;
                                }

                        } else {
                                _cleanup_(vacuum_candidate_freep) struct vacuum_candidate *n = NULL;

                                n = new0(struct vacuum_candidate, 1);
                                if (!n)
                                        return log_oom();

                                n->oldest_file = strdup(de->d_name);
                                if (!n->oldest_file)
                                        return log_oom();

                                n->oldest_mtime = t;

                                r = hashmap_put(h, UINT32_TO_PTR(uid), n);
                                if (r < 0)
                                        return log_oom();

                                c = n;
                                n = NULL;
                        }

                        c->n_files++;

                        if (!worst ||
                            worst->n_files < c->n_files ||
                            (worst->n_files == c->n_files && c->oldest_mtime < worst->oldest_mtime))
                                worst = c;

                        sum += st.st_blocks * 512;
                }

                if (!worst)
                        break;

                r = vacuum_necessary(dirfd(d), sum, keep_free, max_use);
                if (r <= 0)
                        return r;

                if (unlinkat(dirfd(d), worst->oldest_file, 0) < 0) {

                        if (errno == ENOENT)
                                continue;

                        log_error_errno(errno, "Failed to remove file %s: %m", worst->oldest_file);
                        return -errno;
                } else
                        log_info("Removed old coredump %s.", worst->oldest_file);
        }

        return 0;

fail:
        log_error_errno(errno, "Failed to read directory: %m");
        return -errno;
}
Ejemplo n.º 15
0
static int dir_cleanup(
                const char *p,
                DIR *d,
                const struct stat *ds,
                usec_t cutoff,
                dev_t rootdev,
                bool mountpoint,
                int maxdepth,
                bool keep_this_level)
{
        struct dirent *dent;
        struct timespec times[2];
        bool deleted = false;
        char *sub_path = NULL;
        int r = 0;

        while ((dent = readdir(d))) {
                struct stat s;
                usec_t age;

                if (streq(dent->d_name, ".") ||
                    streq(dent->d_name, ".."))
                        continue;

                if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {

                        if (errno != ENOENT) {
                                log_error("stat(%s/%s) failed: %m", p, dent->d_name);
                                r = -errno;
                        }

                        continue;
                }

                /* Stay on the same filesystem */
                if (s.st_dev != rootdev)
                        continue;

                /* Do not delete read-only files owned by root */
                if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
                        continue;

                free(sub_path);
                sub_path = NULL;

                if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) {
                        log_error("Out of memory");
                        r = -ENOMEM;
                        goto finish;
                }

                /* Is there an item configured for this path? */
                if (hashmap_get(items, sub_path))
                        continue;

                if (find_glob(globs, sub_path))
                        continue;

                if (S_ISDIR(s.st_mode)) {

                        if (mountpoint &&
                            streq(dent->d_name, "lost+found") &&
                            s.st_uid == 0)
                                continue;

                        if (maxdepth <= 0)
                                log_warning("Reached max depth on %s.", sub_path);
                        else {
                                DIR *sub_dir;
                                int q;

                                sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
                                if (sub_dir == NULL) {
                                        if (errno != ENOENT) {
                                                log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
                                                r = -errno;
                                        }

                                        continue;
                                }

                                q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
                                closedir(sub_dir);

                                if (q < 0)
                                        r = q;
                        }

                        /* Note: if you are wondering why we don't
                         * support the sticky bit for excluding
                         * directories from cleaning like we do it for
                         * other file system objects: well, the sticky
                         * bit already has a meaning for directories,
                         * so we don't want to overload that. */

                        if (keep_this_level)
                                continue;

                        /* Ignore ctime, we change it when deleting */
                        age = MAX(timespec_load(&s.st_mtim),
                                  timespec_load(&s.st_atim));
                        if (age >= cutoff)
                                continue;

                        log_debug("rmdir '%s'\n", sub_path);

                        if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
                                if (errno != ENOENT && errno != ENOTEMPTY) {
                                        log_error("rmdir(%s): %m", sub_path);
                                        r = -errno;
                                }
                        }

                } else {
                        /* Skip files for which the sticky bit is
                         * set. These are semantics we define, and are
                         * unknown elsewhere. See XDG_RUNTIME_DIR
                         * specification for details. */
                        if (s.st_mode & S_ISVTX)
                                continue;

                        if (mountpoint && S_ISREG(s.st_mode)) {
                                if (streq(dent->d_name, ".journal") &&
                                    s.st_uid == 0)
                                        continue;

                                if (streq(dent->d_name, "aquota.user") ||
                                    streq(dent->d_name, "aquota.group"))
                                        continue;
                        }

                        /* Ignore sockets that are listed in /proc/net/unix */
                        if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path))
                                continue;

                        /* Ignore device nodes */
                        if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
                                continue;

                        /* Keep files on this level around if this is
                         * requested */
                        if (keep_this_level)
                                continue;

                        age = MAX3(timespec_load(&s.st_mtim),
                                   timespec_load(&s.st_atim),
                                   timespec_load(&s.st_ctim));

                        if (age >= cutoff)
                                continue;

                        log_debug("unlink '%s'\n", sub_path);

                        if (unlinkat(dirfd(d), dent->d_name, 0) < 0) {
                                if (errno != ENOENT) {
                                        log_error("unlink(%s): %m", sub_path);
                                        r = -errno;
                                }
                        }

                        deleted = true;
                }
        }

finish:
        if (deleted) {
                /* Restore original directory timestamps */
                times[0] = ds->st_atim;
                times[1] = ds->st_mtim;

                if (futimens(dirfd(d), times) < 0)
                        log_error("utimensat(%s): %m", p);
        }

        free(sub_path);

        return r;
}
Ejemplo n.º 16
0
int manager_read_resolv_conf(Manager *m) {
        _cleanup_fclose_ FILE *f = NULL;
        struct stat st, own;
        char line[LINE_MAX];
        usec_t t;
        int r;

        assert(m);

        /* Reads the system /etc/resolv.conf, if it exists and is not
         * symlinked to our own resolv.conf instance */

        if (!m->read_resolv_conf)
                return 0;

        r = stat("/etc/resolv.conf", &st);
        if (r < 0) {
                if (errno == ENOENT)
                        return 0;

                r = log_warning_errno(errno, "Failed to stat /etc/resolv.conf: %m");
                goto clear;
        }

        /* Have we already seen the file? */
        t = timespec_load(&st.st_mtim);
        if (t == m->resolv_conf_mtime)
                return 0;

        /* Is it symlinked to our own file? */
        if (stat(PRIVATE_RESOLV_CONF, &own) >= 0 &&
            st.st_dev == own.st_dev &&
            st.st_ino == own.st_ino)
                return 0;

        f = fopen("/etc/resolv.conf", "re");
        if (!f) {
                if (errno == ENOENT)
                        return 0;

                r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m");
                goto clear;
        }

        if (fstat(fileno(f), &st) < 0) {
                r = log_error_errno(errno, "Failed to stat open file: %m");
                goto clear;
        }

        dns_server_mark_all(m->dns_servers);
        dns_search_domain_mark_all(m->search_domains);

        FOREACH_LINE(line, f, r = -errno; goto clear) {
                const char *a;
                char *l;

                l = strstrip(line);
                if (IN_SET(*l, '#', ';'))
                        continue;

                a = first_word(l, "nameserver");
                if (a) {
                        r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_SYSTEM, a);
                        if (r < 0)
                                log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a);

                        continue;
                }

                a = first_word(l, "domain");
                if (!a) /* We treat "domain" lines, and "search" lines as equivalent, and add both to our list. */
                        a = first_word(l, "search");
                if (a) {
                        r = manager_parse_search_domains_and_warn(m, a);
                        if (r < 0)
                                log_warning_errno(r, "Failed to parse search domain string '%s', ignoring.", a);
                }
        }

        m->resolv_conf_mtime = t;

        /* Flush out all servers and search domains that are still
         * marked. Those are then ones that didn't appear in the new
         * /etc/resolv.conf */
        dns_server_unlink_marked(m->dns_servers);
        dns_search_domain_unlink_marked(m->search_domains);

        /* Whenever /etc/resolv.conf changes, start using the first
         * DNS server of it. This is useful to deal with broken
         * network managing implementations (like NetworkManager),
         * that when connecting to a VPN place both the VPN DNS
         * servers and the local ones in /etc/resolv.conf. Without
         * resetting the DNS server to use back to the first entry we
         * will continue to use the local one thus being unable to
         * resolve VPN domains. */
        manager_set_dns_server(m, m->dns_servers);

        /* Unconditionally flush the cache when /etc/resolv.conf is
         * modified, even if the data it contained was completely
         * identical to the previous version we used. We do this
         * because altering /etc/resolv.conf is typically done when
         * the network configuration changes, and that should be
         * enough to flush the global unicast DNS cache. */
        if (m->unicast_scope)
                dns_cache_flush(&m->unicast_scope->cache);

        /* If /etc/resolv.conf changed, make sure to forget everything we learned about the DNS servers. After all we
         * might now talk to a very different DNS server that just happens to have the same IP address as an old one
         * (think 192.168.1.1). */
        dns_server_reset_features_all(m->dns_servers);

        return 0;

clear:
        dns_server_unlink_all(m->dns_servers);
        dns_search_domain_unlink_all(m->search_domains);
        return r;
}