1 /* Copyright 2012-present Facebook, Inc.
2 * Licensed under the Apache License, Version 2.0 */
3
4 #include "watchman.h"
5 #include "watchman_synchronized.h"
6
7 using watchman::FileDescriptor;
8
9 /** The state saving thread is responsible for writing out the
10 * persistent information about the users watches.
11 * It runs in its own thread so that we avoid the possibility
12 * of self deadlock if various threads were to immediately
prepare_stdin(struct watchman_trigger_command * cmd,w_query_res * res)13 * save the state when things are changing.
14 *
15 * This uses a simple condition variable to wait for and be
16 * notified of state changes.
17 */
18
19 namespace {
20 struct state {
21 bool needsSave{false};
22 };
23 watchman::Synchronized<state, std::mutex> saveState;
24 std::condition_variable stateCond;
25 std::thread state_saver_thread;
26 }
27
28 static bool do_state_save(void);
29
30 static void state_saver() {
31 bool do_save;
32
33 w_set_thread_name("statesaver");
34
35 while (!w_is_stopping()) {
36 {
37 auto state = saveState.wlock();
38 if (!state->needsSave) {
39 stateCond.wait(state.getUniqueLock());
40 }
41 do_save = state->needsSave;
42 state->needsSave = false;
43 }
44
45 if (do_save) {
46 do_state_save();
47 }
48 }
49 }
50
51 void w_state_shutdown(void) {
52 if (dont_save_state) {
53 return;
54 }
55
56 stateCond.notify_one();
57 state_saver_thread.join();
58 }
59
60 bool w_state_load(void)
61 {
62 json_error_t err;
63
64 if (dont_save_state) {
65 return true;
66 }
67
68 state_saver_thread = std::thread(state_saver);
69
70 auto state = json_load_file(watchman_state_file, 0, &err);
71
72 if (!state) {
73 w_log(W_LOG_ERR, "failed to parse json from %s: %s\n",
74 watchman_state_file,
75 err.text);
76 return false;
77 }
78
79 if (!w_root_load_state(state)) {
80 return false;
81 }
82
83 return true;
spawn_command(const std::shared_ptr<w_root_t> & root,struct watchman_trigger_command * cmd,w_query_res * res,struct ClockSpec * since_spec)84 }
85
86 #if defined(HAVE_MKOSTEMP) && defined(sun)
87 // Not guaranteed to be defined in stdlib.h
88 extern int mkostemp(char *, int);
89 #endif
90
91 std::unique_ptr<watchman_stream> w_mkstemp(char* templ) {
92 #if defined(_WIN32)
93 char *name = _mktemp(templ);
94 if (!name) {
95 return nullptr;
96 }
97 // Most annoying aspect of windows is the latency around
98 // file handle exclusivity. We could avoid this dumb loop
99 // by implementing our own mkostemp, but this is the most
100 // expedient option for the moment.
101 for (size_t attempts = 0; attempts < 10; ++attempts) {
102 auto stm = w_stm_open(name, O_RDWR | O_CLOEXEC | O_CREAT | O_TRUNC, 0600);
103 if (stm) {
104 return stm;
105 }
106 if (errno == EACCES) {
107 /* sleep override */ usleep(2000);
108 continue;
109 }
110 return nullptr;
111 }
112 return nullptr;
113 #else
114 FileDescriptor fd;
115 # ifdef HAVE_MKOSTEMP
116 fd = FileDescriptor(mkostemp(templ, O_CLOEXEC));
117 # else
118 fd = FileDescriptor(mkstemp(templ));
119 # endif
120 if (!fd) {
121 return nullptr;
122 }
123 fd.setCloExec();
124
125 return w_stm_fdopen(std::move(fd));
126 #endif
127 }
128
129 static bool do_state_save(void) {
130 w_jbuffer_t buffer;
131
132 auto state = json_object();
133
134 auto file =
135 w_stm_open(watchman_state_file, O_WRONLY | O_TRUNC | O_CREAT, 0600);
136 if (!file) {
137 w_log(
138 W_LOG_ERR,
139 "save_state: unable to open %s for write: %s\n",
140 watchman_state_file,
141 strerror(errno));
142 return false;
143 }
144
145 state.set("version", typed_string_to_json(PACKAGE_VERSION, W_STRING_UNICODE));
146
147 /* now ask the different subsystems to fill out the state */
148 if (!w_root_save_state(state)) {
149 return false;
150 }
151
152 /* we've prepared what we're going to save, so write it out */
153 buffer.jsonEncodeToStream(state, file.get(), JSON_INDENT(4));
154 return true;
155 }
156
157 /** Arranges for the state to be saved.
158 * Does not immediately save the state. */
159 void w_state_save(void) {
160 if (dont_save_state) {
161 return;
162 }
163
164 saveState.wlock()->needsSave = true;
165 stateCond.notify_one();
166 }
167
168 /* vim:ts=2:sw=2:et:
169 */
170