Skip to content

Commit af303ee

Browse files
committed
Merge branch 'jh/builtin-fsmonitor-part1'
Built-in fsmonitor (part 1). * jh/builtin-fsmonitor-part1: t/helper/simple-ipc: convert test-simple-ipc to use start_bg_command run-command: create start_bg_command simple-ipc/ipc-win32: add Windows ACL to named pipe simple-ipc/ipc-win32: add trace2 debugging simple-ipc: move definition of ipc_active_state outside of ifdef simple-ipc: preparations for supporting binary messages. trace2: add trace2_child_ready() to report on background children
2 parents a5e61a4 + 05881a6 commit af303ee

File tree

13 files changed

+587
-198
lines changed

13 files changed

+587
-198
lines changed

Documentation/technical/api-trace2.txt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,46 @@ stopping after the waitpid() and includes OS process creation overhead).
613613
So this time will be slightly larger than the atexit time reported by
614614
the child process itself.
615615

616+
`"child_ready"`::
617+
This event is generated after the current process has started
618+
a background process and released all handles to it.
619+
+
620+
------------
621+
{
622+
"event":"child_ready",
623+
...
624+
"child_id":2,
625+
"pid":14708, # child PID
626+
"ready":"ready", # child ready state
627+
"t_rel":0.110605 # observed run-time of child process
628+
}
629+
------------
630+
+
631+
Note that the session-id of the child process is not available to
632+
the current/spawning process, so the child's PID is reported here as
633+
a hint for post-processing. (But it is only a hint because the child
634+
process may be a shell script which doesn't have a session-id.)
635+
+
636+
This event is generated after the child is started in the background
637+
and given a little time to boot up and start working. If the child
638+
startups normally and while the parent is still waiting, the "ready"
639+
field will have the value "ready".
640+
If the child is too slow to start and the parent times out, the field
641+
will have the value "timeout".
642+
If the child starts but the parent is unable to probe it, the field
643+
will have the value "error".
644+
+
645+
After the parent process emits this event, it will release all of its
646+
handles to the child process and treat the child as a background
647+
daemon. So even if the child does eventually finish booting up,
648+
the parent will not emit an updated event.
649+
+
650+
Note that the `t_rel` field contains the observed run time in seconds
651+
when the parent released the child process into the background.
652+
The child is assumed to be a long-running daemon process and may
653+
outlive the parent process. So the parent's child event times should
654+
not be compared to the child's atexit times.
655+
616656
`"exec"`::
617657
This event is generated before git attempts to `exec()`
618658
another command rather than starting a child process.

compat/simple-ipc/ipc-unix-socket.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,16 @@ void ipc_client_close_connection(struct ipc_client_connection *connection)
168168

169169
int ipc_client_send_command_to_connection(
170170
struct ipc_client_connection *connection,
171-
const char *message, struct strbuf *answer)
171+
const char *message, size_t message_len,
172+
struct strbuf *answer)
172173
{
173174
int ret = 0;
174175

175176
strbuf_setlen(answer, 0);
176177

177178
trace2_region_enter("ipc-client", "send-command", NULL);
178179

179-
if (write_packetized_from_buf_no_flush(message, strlen(message),
180+
if (write_packetized_from_buf_no_flush(message, message_len,
180181
connection->fd) < 0 ||
181182
packet_flush_gently(connection->fd) < 0) {
182183
ret = error(_("could not send IPC command"));
@@ -197,7 +198,8 @@ int ipc_client_send_command_to_connection(
197198

198199
int ipc_client_send_command(const char *path,
199200
const struct ipc_client_connect_options *options,
200-
const char *message, struct strbuf *answer)
201+
const char *message, size_t message_len,
202+
struct strbuf *answer)
201203
{
202204
int ret = -1;
203205
enum ipc_active_state state;
@@ -208,7 +210,9 @@ int ipc_client_send_command(const char *path,
208210
if (state != IPC_STATE__LISTENING)
209211
return ret;
210212

211-
ret = ipc_client_send_command_to_connection(connection, message, answer);
213+
ret = ipc_client_send_command_to_connection(connection,
214+
message, message_len,
215+
answer);
212216

213217
ipc_client_close_connection(connection);
214218

@@ -503,7 +507,7 @@ static int worker_thread__do_io(
503507
if (ret >= 0) {
504508
ret = worker_thread_data->server_data->application_cb(
505509
worker_thread_data->server_data->application_data,
506-
buf.buf, do_io_reply_callback, &reply_data);
510+
buf.buf, buf.len, do_io_reply_callback, &reply_data);
507511

508512
packet_flush_gently(reply_data.fd);
509513
}

compat/simple-ipc/ipc-win32.c

Lines changed: 162 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include "strbuf.h"
44
#include "pkt-line.h"
55
#include "thread-utils.h"
6+
#include "accctrl.h"
7+
#include "aclapi.h"
68

79
#ifndef SUPPORTS_SIMPLE_IPC
810
/*
@@ -49,6 +51,9 @@ static enum ipc_active_state get_active_state(wchar_t *pipe_path)
4951
if (GetLastError() == ERROR_FILE_NOT_FOUND)
5052
return IPC_STATE__PATH_NOT_FOUND;
5153

54+
trace2_data_intmax("ipc-debug", NULL, "getstate/waitpipe/gle",
55+
(intmax_t)GetLastError());
56+
5257
return IPC_STATE__OTHER_ERROR;
5358
}
5459

@@ -109,9 +114,15 @@ static enum ipc_active_state connect_to_server(
109114
t_start_ms = (DWORD)(getnanotime() / 1000000);
110115

111116
if (!WaitNamedPipeW(wpath, timeout_ms)) {
112-
if (GetLastError() == ERROR_SEM_TIMEOUT)
117+
DWORD gleWait = GetLastError();
118+
119+
if (gleWait == ERROR_SEM_TIMEOUT)
113120
return IPC_STATE__NOT_LISTENING;
114121

122+
trace2_data_intmax("ipc-debug", NULL,
123+
"connect/waitpipe/gle",
124+
(intmax_t)gleWait);
125+
115126
return IPC_STATE__OTHER_ERROR;
116127
}
117128

@@ -133,17 +144,31 @@ static enum ipc_active_state connect_to_server(
133144
break; /* try again */
134145

135146
default:
147+
trace2_data_intmax("ipc-debug", NULL,
148+
"connect/createfile/gle",
149+
(intmax_t)gle);
150+
136151
return IPC_STATE__OTHER_ERROR;
137152
}
138153
}
139154

140155
if (!SetNamedPipeHandleState(hPipe, &mode, NULL, NULL)) {
156+
gle = GetLastError();
157+
trace2_data_intmax("ipc-debug", NULL,
158+
"connect/setpipestate/gle",
159+
(intmax_t)gle);
160+
141161
CloseHandle(hPipe);
142162
return IPC_STATE__OTHER_ERROR;
143163
}
144164

145165
*pfd = _open_osfhandle((intptr_t)hPipe, O_RDWR|O_BINARY);
146166
if (*pfd < 0) {
167+
gle = GetLastError();
168+
trace2_data_intmax("ipc-debug", NULL,
169+
"connect/openosfhandle/gle",
170+
(intmax_t)gle);
171+
147172
CloseHandle(hPipe);
148173
return IPC_STATE__OTHER_ERROR;
149174
}
@@ -208,15 +233,16 @@ void ipc_client_close_connection(struct ipc_client_connection *connection)
208233

209234
int ipc_client_send_command_to_connection(
210235
struct ipc_client_connection *connection,
211-
const char *message, struct strbuf *answer)
236+
const char *message, size_t message_len,
237+
struct strbuf *answer)
212238
{
213239
int ret = 0;
214240

215241
strbuf_setlen(answer, 0);
216242

217243
trace2_region_enter("ipc-client", "send-command", NULL);
218244

219-
if (write_packetized_from_buf_no_flush(message, strlen(message),
245+
if (write_packetized_from_buf_no_flush(message, message_len,
220246
connection->fd) < 0 ||
221247
packet_flush_gently(connection->fd) < 0) {
222248
ret = error(_("could not send IPC command"));
@@ -239,7 +265,8 @@ int ipc_client_send_command_to_connection(
239265

240266
int ipc_client_send_command(const char *path,
241267
const struct ipc_client_connect_options *options,
242-
const char *message, struct strbuf *response)
268+
const char *message, size_t message_len,
269+
struct strbuf *response)
243270
{
244271
int ret = -1;
245272
enum ipc_active_state state;
@@ -250,7 +277,9 @@ int ipc_client_send_command(const char *path,
250277
if (state != IPC_STATE__LISTENING)
251278
return ret;
252279

253-
ret = ipc_client_send_command_to_connection(connection, message, response);
280+
ret = ipc_client_send_command_to_connection(connection,
281+
message, message_len,
282+
response);
254283

255284
ipc_client_close_connection(connection);
256285

@@ -458,7 +487,7 @@ static int do_io(struct ipc_server_thread_data *server_thread_data)
458487
if (ret >= 0) {
459488
ret = server_thread_data->server_data->application_cb(
460489
server_thread_data->server_data->application_data,
461-
buf.buf, do_io_reply_callback, &reply_data);
490+
buf.buf, buf.len, do_io_reply_callback, &reply_data);
462491

463492
packet_flush_gently(reply_data.fd);
464493

@@ -565,11 +594,132 @@ static void *server_thread_proc(void *_server_thread_data)
565594
return NULL;
566595
}
567596

597+
/*
598+
* We need to build a Windows "SECURITY_ATTRIBUTES" object and use it
599+
* to apply an ACL when we create the initial instance of the Named
600+
* Pipe. The construction is somewhat involved and consists of
601+
* several sequential steps and intermediate objects.
602+
*
603+
* We use this structure to hold these intermediate pointers so that
604+
* we can free them as a group. (It is unclear from the docs whether
605+
* some of these intermediate pointers can be freed before we are
606+
* finished using the "lpSA" member.)
607+
*/
608+
struct my_sa_data
609+
{
610+
PSID pEveryoneSID;
611+
PACL pACL;
612+
PSECURITY_DESCRIPTOR pSD;
613+
LPSECURITY_ATTRIBUTES lpSA;
614+
};
615+
616+
static void init_sa(struct my_sa_data *d)
617+
{
618+
memset(d, 0, sizeof(*d));
619+
}
620+
621+
static void release_sa(struct my_sa_data *d)
622+
{
623+
if (d->pEveryoneSID)
624+
FreeSid(d->pEveryoneSID);
625+
if (d->pACL)
626+
LocalFree(d->pACL);
627+
if (d->pSD)
628+
LocalFree(d->pSD);
629+
if (d->lpSA)
630+
LocalFree(d->lpSA);
631+
632+
memset(d, 0, sizeof(*d));
633+
}
634+
635+
/*
636+
* Create SECURITY_ATTRIBUTES to apply to the initial named pipe. The
637+
* creator of the first server instance gets to set the ACLs on it.
638+
*
639+
* We allow the well-known group `EVERYONE` to have read+write access
640+
* to the named pipe so that clients can send queries to the daemon
641+
* and receive the response.
642+
*
643+
* Normally, this is not necessary since the daemon is usually
644+
* automatically started by a foreground command like `git status`,
645+
* but in those cases where an elevated Git command started the daemon
646+
* (such that the daemon itself runs with elevation), we need to add
647+
* the ACL so that non-elevated commands can write to it.
648+
*
649+
* The following document was helpful:
650+
* https://docs.microsoft.com/en-us/windows/win32/secauthz/creating-a-security-descriptor-for-a-new-object-in-c--
651+
*
652+
* Returns d->lpSA set to a SA or NULL.
653+
*/
654+
static LPSECURITY_ATTRIBUTES get_sa(struct my_sa_data *d)
655+
{
656+
SID_IDENTIFIER_AUTHORITY sid_auth_world = SECURITY_WORLD_SID_AUTHORITY;
657+
#define NR_EA (1)
658+
EXPLICIT_ACCESS ea[NR_EA];
659+
DWORD dwResult;
660+
661+
if (!AllocateAndInitializeSid(&sid_auth_world, 1,
662+
SECURITY_WORLD_RID, 0,0,0,0,0,0,0,
663+
&d->pEveryoneSID)) {
664+
DWORD gle = GetLastError();
665+
trace2_data_intmax("ipc-debug", NULL, "alloc-world-sid/gle",
666+
(intmax_t)gle);
667+
goto fail;
668+
}
669+
670+
memset(ea, 0, NR_EA * sizeof(EXPLICIT_ACCESS));
671+
672+
ea[0].grfAccessPermissions = GENERIC_READ | GENERIC_WRITE;
673+
ea[0].grfAccessMode = SET_ACCESS;
674+
ea[0].grfInheritance = NO_INHERITANCE;
675+
ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
676+
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
677+
ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
678+
ea[0].Trustee.ptstrName = (LPTSTR)d->pEveryoneSID;
679+
680+
dwResult = SetEntriesInAcl(NR_EA, ea, NULL, &d->pACL);
681+
if (dwResult != ERROR_SUCCESS) {
682+
DWORD gle = GetLastError();
683+
trace2_data_intmax("ipc-debug", NULL, "set-acl-entry/gle",
684+
(intmax_t)gle);
685+
trace2_data_intmax("ipc-debug", NULL, "set-acl-entry/dw",
686+
(intmax_t)dwResult);
687+
goto fail;
688+
}
689+
690+
d->pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(
691+
LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
692+
if (!InitializeSecurityDescriptor(d->pSD, SECURITY_DESCRIPTOR_REVISION)) {
693+
DWORD gle = GetLastError();
694+
trace2_data_intmax("ipc-debug", NULL, "init-sd/gle", (intmax_t)gle);
695+
goto fail;
696+
}
697+
698+
if (!SetSecurityDescriptorDacl(d->pSD, TRUE, d->pACL, FALSE)) {
699+
DWORD gle = GetLastError();
700+
trace2_data_intmax("ipc-debug", NULL, "set-sd-dacl/gle", (intmax_t)gle);
701+
goto fail;
702+
}
703+
704+
d->lpSA = (LPSECURITY_ATTRIBUTES)LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES));
705+
d->lpSA->nLength = sizeof(SECURITY_ATTRIBUTES);
706+
d->lpSA->lpSecurityDescriptor = d->pSD;
707+
d->lpSA->bInheritHandle = FALSE;
708+
709+
return d->lpSA;
710+
711+
fail:
712+
release_sa(d);
713+
return NULL;
714+
}
715+
568716
static HANDLE create_new_pipe(wchar_t *wpath, int is_first)
569717
{
570718
HANDLE hPipe;
571719
DWORD dwOpenMode, dwPipeMode;
572-
LPSECURITY_ATTRIBUTES lpsa = NULL;
720+
struct my_sa_data my_sa_data;
721+
722+
init_sa(&my_sa_data);
573723

574724
dwOpenMode = PIPE_ACCESS_INBOUND | PIPE_ACCESS_OUTBOUND |
575725
FILE_FLAG_OVERLAPPED;
@@ -585,20 +735,15 @@ static HANDLE create_new_pipe(wchar_t *wpath, int is_first)
585735
* set the ACL / Security Attributes on the named
586736
* pipe; subsequent instances inherit and cannot
587737
* change them.
588-
*
589-
* TODO Should we allow the application layer to
590-
* specify security attributes, such as `LocalService`
591-
* or `LocalSystem`, when we create the named pipe?
592-
* This question is probably not important when the
593-
* daemon is started by a foreground user process and
594-
* only needs to talk to the current user, but may be
595-
* if the daemon is run via the Control Panel as a
596-
* System Service.
597738
*/
739+
get_sa(&my_sa_data);
598740
}
599741

600742
hPipe = CreateNamedPipeW(wpath, dwOpenMode, dwPipeMode,
601-
PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, lpsa);
743+
PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0,
744+
my_sa_data.lpSA);
745+
746+
release_sa(&my_sa_data);
602747

603748
return hPipe;
604749
}

0 commit comments

Comments
 (0)