Replace files through one atomic write helper

7820356cf0ce772f614f386ac36aeab638765e16
Alexis Sellier committed ago 1 parent d0029ccd
swm.c +33 -37
107 107
#define MAX_POPUPS              256
108 108
#define MAX_SESSION_LOCKS       4
109 109
#define MAX_PENDING_SPAWNS      256
110 110
#define MAX_WINDOW_STATES       512
111 111
#define MAX_WINDOW_STATE_FIELD  256
112 +
#define MAX_STATUS_FIELD        1024
112 113
#define MAX_WINDOW_STATE_LINE   1024
113 114
#define MAX_STATE_PATH          4096
114 115
#define MAX_WORKSPACE_TITLE     256
115 116
#define MAX_WS_ID               16
116 117
#define MAX_STATIC_LISTENERS    512
3806 3807
    LISTEN(&session_lock->events.unlock, &lock->unlock, unlock_session);
3807 3808
3808 3809
    wlr_session_lock_v1_send_locked(session_lock);
3809 3810
}
3810 3811
3812 +
/* Open a temporary file to be renamed over path once it is fully written. */
3813 +
static FILE *file_replace_begin(const char *path, char *tmppath, size_t size) {
3814 +
    if (snprintf(tmppath, size, "%s.tmp.%ld", path, (long)getpid()) >= (int)size)
3815 +
        return nullptr;
3816 +
    return fopen(tmppath, "w");
3817 +
}
3818 +
3819 +
/* Put a fully written temporary file in place, or discard it. */
3820 +
static bool file_replace_commit(FILE *f, const char *tmppath, const char *path) {
3821 +
    bool ok = !ferror(f) && fflush(f) == 0;
3822 +
3823 +
    if (fclose(f) < 0)
3824 +
        ok = false;
3825 +
3826 +
    if (ok && rename(tmppath, path) == 0)
3827 +
        return true;
3828 +
    unlink(tmppath);
3829 +
    return false;
3830 +
}
3831 +
3811 3832
/* Build the path used to persist floating window geometry. */
3812 3833
static const char *window_state_path(void) {
3813 3834
    static char path[MAX_STATE_PATH];
3814 3835
    const char *base = getenv("XDG_STATE_HOME");
3815 3836
    const char *home = getenv("HOME");
3846 3867
void save_window_states(void) {
3847 3868
    window_state_t *state;
3848 3869
    FILE           *f;
3849 3870
    const char     *path = window_state_path();
3850 3871
    char            dir[MAX_STATE_PATH], tmppath[MAX_STATE_PATH], *slash, *p;
3851 -
    int             ok;
3852 3872
3853 3873
    snprintf(dir, sizeof(dir), "%s", path);
3854 3874
3855 3875
    if ((slash = strrchr(dir, '/'))) {
3856 3876
        *slash = '\0';
3863 3883
            *p = '/';
3864 3884
        }
3865 3885
        if (mkdir(dir, 0700) < 0 && errno != EEXIST)
3866 3886
            return;
3867 3887
    }
3868 -
    if (snprintf(tmppath, sizeof(tmppath), "%s.tmp.%ld", path, (long)getpid()) >=
3869 -
        (int)sizeof(tmppath))
3870 -
        return;
3871 -
3872 -
    if (!(f = fopen(tmppath, "w"))) {
3888 +
    if (!(f = file_replace_begin(path, tmppath, sizeof(tmppath)))) {
3873 3889
        fprintf(stderr, "swm: cannot save floating window state: %s\n", strerror(errno));
3874 3890
        return;
3875 3891
    }
3876 3892
    wl_list_for_each(state, &window_states, link) fprintf(
3877 3893
        f,
3881 3897
        state->geom.x,
3882 3898
        state->geom.y,
3883 3899
        state->geom.width,
3884 3900
        state->geom.height
3885 3901
    );
3886 -
    ok = !ferror(f) && fflush(f) == 0;
3887 -
3888 -
    if (fclose(f) < 0)
3889 -
        ok = 0;
3890 -
3891 -
    if (!ok || rename(tmppath, path) < 0) {
3902 +
    if (!file_replace_commit(f, tmppath, path))
3892 3903
        fprintf(stderr, "swm: cannot save floating window state: %s\n", strerror(errno));
3893 -
        unlink(tmppath);
3894 -
    }
3895 3904
}
3896 3905
3897 3906
/* Load remembered floating-window geometries from disk. */
3898 3907
void load_window_states(void) {
3899 3908
    FILE           *f;
4591 4600
/* Publish visible window rectangles in slurp's predefined-region format. */
4592 4601
void publish_windows(const char *runtime) {
4593 4602
    client_t *c;
4594 4603
    FILE     *file;
4595 4604
    char      path[MAX_STATE_PATH], tmppath[MAX_STATE_PATH], title[MAX_WINDOW_STATE_FIELD];
4596 -
    int       ok = 1;
4597 4605
4598 4606
    if (!runtime || snprintf(path, sizeof(path), "%s/swm-windows", runtime) >= (int)sizeof(path) ||
4599 -
        snprintf(tmppath, sizeof(tmppath), "%s.tmp.%ld", path, (long)getpid()) >=
4600 -
            (int)sizeof(tmppath) ||
4601 -
        !(file = fopen(tmppath, "w")))
4607 +
        !(file = file_replace_begin(path, tmppath, sizeof(tmppath))))
4602 4608
        return;
4603 4609
4604 4610
    wl_list_for_each(c, &clients, link) {
4605 4611
        if (!c->mon || !client_surface(c)->mapped || !c->scene->node.enabled)
4606 4612
            continue;
4607 4613
        status_field(title, sizeof(title), client_get_title(c));
4608 -
        if (fprintf(
4609 -
                file, "%d,%d %dx%d %s\n", c->geom.x, c->geom.y, c->geom.width, c->geom.height, title
4610 -
            ) < 0)
4611 -
            ok = 0;
4614 +
        fprintf(
4615 +
            file, "%d,%d %dx%d %s\n", c->geom.x, c->geom.y, c->geom.width, c->geom.height, title
4616 +
        );
4612 4617
    }
4613 -
    if (fflush(file) < 0 || fclose(file) < 0)
4614 -
        ok = 0;
4615 -
    if (!ok || rename(tmppath, path) < 0)
4616 -
        unlink(tmppath);
4618 +
    file_replace_commit(file, tmppath, path);
4617 4619
}
4618 4620
4619 4621
/* Publish current display, workspace, and focus state on standard output. */
4620 4622
void print_status(void) {
4621 4623
    monitor_t  *m = nullptr;
4622 4624
    client_t   *c;
4623 4625
    FILE       *titlefile;
4624 -
    char        titlepath[4096], tmppath[4096], title[1024], appid[1024];
4626 +
    char        titlepath[MAX_STATE_PATH], tmppath[MAX_STATE_PATH];
4627 +
    char        title[MAX_STATUS_FIELD], appid[MAX_STATUS_FIELD];
4625 4628
    const char *runtime, *src;
4626 4629
    uint32_t    occ, urg;
4627 4630
    int         i;
4628 4631
4629 4632
    occ = urg = 0;
4676 4679
    if (runtime && selmon) {
4677 4680
        src = (c = focus_top(selmon)) ? client_get_title(c) : "";
4678 4681
        status_field(title, sizeof(title), src);
4679 4682
        snprintf(titlepath, sizeof(titlepath), "%s/swm-title", runtime);
4680 4683
4681 -
        if (snprintf(tmppath, sizeof(tmppath), "%s.tmp.%ld", titlepath, (long)getpid()) <
4682 -
                (int)sizeof(tmppath) &&
4683 -
            (titlefile = fopen(tmppath, "w"))) {
4684 -
            int ok = fprintf(titlefile, "%s\n", title) >= 0 && fflush(titlefile) == 0;
4685 -
4686 -
            if (fclose(titlefile) < 0)
4687 -
                ok = 0;
4688 -
4689 -
            if (!ok || rename(tmppath, titlepath) < 0)
4690 -
                unlink(tmppath);
4684 +
        if ((titlefile = file_replace_begin(titlepath, tmppath, sizeof(tmppath)))) {
4685 +
            fprintf(titlefile, "%s\n", title);
4686 +
            file_replace_commit(titlefile, tmppath, titlepath);
4691 4687
        }
4692 4688
    }
4693 4689
    fflush(stdout);
4694 4690
    workspace_broadcast();
4695 4691
}