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