Replace files through one atomic write helper
7820356cf0ce772f614f386ac36aeab638765e16
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 | } |