3
3
#include "parse-options.h"
4
4
#include "fsmonitor.h"
5
5
#include "fsmonitor-ipc.h"
6
+ #include "compat/fsmonitor/fsm-listen.h"
7
+ #include "fsmonitor--daemon.h"
6
8
#include "simple-ipc.h"
7
9
#include "khash.h"
8
10
9
11
static const char * const builtin_fsmonitor__daemon_usage [] = {
12
+ N_ ("git fsmonitor--daemon run [<options>]" ),
10
13
N_ ("git fsmonitor--daemon stop" ),
11
14
N_ ("git fsmonitor--daemon status" ),
12
15
NULL
13
16
};
14
17
15
18
#ifdef HAVE_FSMONITOR_DAEMON_BACKEND
19
+ /*
20
+ * Global state loaded from config.
21
+ */
22
+ #define FSMONITOR__IPC_THREADS "fsmonitor.ipcthreads"
23
+ static int fsmonitor__ipc_threads = 8 ;
24
+
25
+ static int fsmonitor_config (const char * var , const char * value , void * cb )
26
+ {
27
+ if (!strcmp (var , FSMONITOR__IPC_THREADS )) {
28
+ int i = git_config_int (var , value );
29
+ if (i < 1 )
30
+ return error (_ ("value of '%s' out of range: %d" ),
31
+ FSMONITOR__IPC_THREADS , i );
32
+ fsmonitor__ipc_threads = i ;
33
+ return 0 ;
34
+ }
35
+
36
+ return git_default_config (var , value , cb );
37
+ }
38
+
16
39
/*
17
40
* Acting as a CLIENT.
18
41
*
@@ -57,11 +80,190 @@ static int do_as_client__status(void)
57
80
}
58
81
}
59
82
83
+ static ipc_server_application_cb handle_client ;
84
+
85
+ static int handle_client (void * data ,
86
+ const char * command , size_t command_len ,
87
+ ipc_server_reply_cb * reply ,
88
+ struct ipc_server_reply_data * reply_data )
89
+ {
90
+ /* struct fsmonitor_daemon_state *state = data; */
91
+ int result ;
92
+
93
+ /*
94
+ * The Simple IPC API now supports {char*, len} arguments, but
95
+ * FSMonitor always uses proper null-terminated strings, so
96
+ * we can ignore the command_len argument. (Trust, but verify.)
97
+ */
98
+ if (command_len != strlen (command ))
99
+ BUG ("FSMonitor assumes text messages" );
100
+
101
+ trace2_region_enter ("fsmonitor" , "handle_client" , the_repository );
102
+ trace2_data_string ("fsmonitor" , the_repository , "request" , command );
103
+
104
+ result = 0 ; /* TODO Do something here. */
105
+
106
+ trace2_region_leave ("fsmonitor" , "handle_client" , the_repository );
107
+
108
+ return result ;
109
+ }
110
+
111
+ static void * fsm_listen__thread_proc (void * _state )
112
+ {
113
+ struct fsmonitor_daemon_state * state = _state ;
114
+
115
+ trace2_thread_start ("fsm-listen" );
116
+
117
+ trace_printf_key (& trace_fsmonitor , "Watching: worktree '%s'" ,
118
+ state -> path_worktree_watch .buf );
119
+ if (state -> nr_paths_watching > 1 )
120
+ trace_printf_key (& trace_fsmonitor , "Watching: gitdir '%s'" ,
121
+ state -> path_gitdir_watch .buf );
122
+
123
+ fsm_listen__loop (state );
124
+
125
+ trace2_thread_exit ();
126
+ return NULL ;
127
+ }
128
+
129
+ static int fsmonitor_run_daemon_1 (struct fsmonitor_daemon_state * state )
130
+ {
131
+ struct ipc_server_opts ipc_opts = {
132
+ .nr_threads = fsmonitor__ipc_threads ,
133
+
134
+ /*
135
+ * We know that there are no other active threads yet,
136
+ * so we can let the IPC layer temporarily chdir() if
137
+ * it needs to when creating the server side of the
138
+ * Unix domain socket.
139
+ */
140
+ .uds_disallow_chdir = 0
141
+ };
142
+
143
+ /*
144
+ * Start the IPC thread pool before the we've started the file
145
+ * system event listener thread so that we have the IPC handle
146
+ * before we need it.
147
+ */
148
+ if (ipc_server_run_async (& state -> ipc_server_data ,
149
+ fsmonitor_ipc__get_path (), & ipc_opts ,
150
+ handle_client , state ))
151
+ return error (_ ("could not start IPC thread pool" ));
152
+
153
+ /*
154
+ * Start the fsmonitor listener thread to collect filesystem
155
+ * events.
156
+ */
157
+ if (pthread_create (& state -> listener_thread , NULL ,
158
+ fsm_listen__thread_proc , state ) < 0 ) {
159
+ ipc_server_stop_async (state -> ipc_server_data );
160
+ ipc_server_await (state -> ipc_server_data );
161
+
162
+ return error (_ ("could not start fsmonitor listener thread" ));
163
+ }
164
+
165
+ /*
166
+ * The daemon is now fully functional in background threads.
167
+ * Wait for the IPC thread pool to shutdown (whether by client
168
+ * request or from filesystem activity).
169
+ */
170
+ ipc_server_await (state -> ipc_server_data );
171
+
172
+ /*
173
+ * The fsmonitor listener thread may have received a shutdown
174
+ * event from the IPC thread pool, but it doesn't hurt to tell
175
+ * it again. And wait for it to shutdown.
176
+ */
177
+ fsm_listen__stop_async (state );
178
+ pthread_join (state -> listener_thread , NULL );
179
+
180
+ return state -> error_code ;
181
+ }
182
+
183
+ static int fsmonitor_run_daemon (void )
184
+ {
185
+ struct fsmonitor_daemon_state state ;
186
+ int err ;
187
+
188
+ memset (& state , 0 , sizeof (state ));
189
+
190
+ pthread_mutex_init (& state .main_lock , NULL );
191
+ state .error_code = 0 ;
192
+ state .current_token_data = NULL ;
193
+
194
+ /* Prepare to (recursively) watch the <worktree-root> directory. */
195
+ strbuf_init (& state .path_worktree_watch , 0 );
196
+ strbuf_addstr (& state .path_worktree_watch , absolute_path (get_git_work_tree ()));
197
+ state .nr_paths_watching = 1 ;
198
+
199
+ /*
200
+ * We create and delete cookie files somewhere inside the .git
201
+ * directory to help us keep sync with the file system. If
202
+ * ".git" is not a directory, then <gitdir> is not inside the
203
+ * cone of <worktree-root>, so set up a second watch to watch
204
+ * the <gitdir> so that we get events for the cookie files.
205
+ */
206
+ strbuf_init (& state .path_gitdir_watch , 0 );
207
+ strbuf_addbuf (& state .path_gitdir_watch , & state .path_worktree_watch );
208
+ strbuf_addstr (& state .path_gitdir_watch , "/.git" );
209
+ if (!is_directory (state .path_gitdir_watch .buf )) {
210
+ strbuf_reset (& state .path_gitdir_watch );
211
+ strbuf_addstr (& state .path_gitdir_watch , absolute_path (get_git_dir ()));
212
+ state .nr_paths_watching = 2 ;
213
+ }
214
+
215
+ /*
216
+ * Confirm that we can create platform-specific resources for the
217
+ * filesystem listener before we bother starting all the threads.
218
+ */
219
+ if (fsm_listen__ctor (& state )) {
220
+ err = error (_ ("could not initialize listener thread" ));
221
+ goto done ;
222
+ }
223
+
224
+ err = fsmonitor_run_daemon_1 (& state );
225
+
226
+ done :
227
+ pthread_mutex_destroy (& state .main_lock );
228
+ fsm_listen__dtor (& state );
229
+
230
+ ipc_server_free (state .ipc_server_data );
231
+
232
+ strbuf_release (& state .path_worktree_watch );
233
+ strbuf_release (& state .path_gitdir_watch );
234
+
235
+ return err ;
236
+ }
237
+
238
+ static int try_to_run_foreground_daemon (void )
239
+ {
240
+ /*
241
+ * Technically, we don't need to probe for an existing daemon
242
+ * process, since we could just call `fsmonitor_run_daemon()`
243
+ * and let it fail if the pipe/socket is busy.
244
+ *
245
+ * However, this method gives us a nicer error message for a
246
+ * common error case.
247
+ */
248
+ if (fsmonitor_ipc__get_state () == IPC_STATE__LISTENING )
249
+ die ("fsmonitor--daemon is already running '%s'" ,
250
+ the_repository -> worktree );
251
+
252
+ printf (_ ("running fsmonitor-daemon in '%s'\n" ),
253
+ the_repository -> worktree );
254
+ fflush (stdout );
255
+
256
+ return !!fsmonitor_run_daemon ();
257
+ }
258
+
60
259
int cmd_fsmonitor__daemon (int argc , const char * * argv , const char * prefix )
61
260
{
62
261
const char * subcmd ;
63
262
64
263
struct option options [] = {
264
+ OPT_INTEGER (0 , "ipc-threads" ,
265
+ & fsmonitor__ipc_threads ,
266
+ N_ ("use <n> ipc worker threads" )),
65
267
OPT_END ()
66
268
};
67
269
@@ -71,14 +273,20 @@ int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
71
273
if (argc == 2 && !strcmp (argv [1 ], "-h" ))
72
274
usage_with_options (builtin_fsmonitor__daemon_usage , options );
73
275
74
- git_config (git_default_config , NULL );
276
+ git_config (fsmonitor_config , NULL );
75
277
76
278
subcmd = argv [1 ];
77
279
argv -- ;
78
280
argc ++ ;
79
281
80
282
argc = parse_options (argc , argv , prefix , options ,
81
283
builtin_fsmonitor__daemon_usage , 0 );
284
+ if (fsmonitor__ipc_threads < 1 )
285
+ die (_ ("invalid 'ipc-threads' value (%d)" ),
286
+ fsmonitor__ipc_threads );
287
+
288
+ if (!strcmp (subcmd , "run" ))
289
+ return !!try_to_run_foreground_daemon ();
82
290
83
291
if (!strcmp (subcmd , "stop" ))
84
292
return !!do_as_client__send_stop ();
0 commit comments