Skip to content

Simple IPC Mechanism#766

Closed
jeffhostetler wants to merge 12 commits intogitgitgadget:masterfrom
jeffhostetler:simple-ipc
Closed

Simple IPC Mechanism#766
jeffhostetler wants to merge 12 commits intogitgitgadget:masterfrom
jeffhostetler:simple-ipc

Conversation

@jeffhostetler
Copy link

@jeffhostetler jeffhostetler commented Oct 20, 2020

Here is version V7 of my simple-ipc series. The only change from V6 is to squash in
the CALLOC_ARRAY() suggestion.

$ git range-diff v2.31.0-rc1..pr-766/jeffhostetler/simple-ipc-v6 v2.31.0-rc1..HEAD
1: fe35dc3 = 1: fe35dc3 pkt-line: eliminate the need for static buffer in packet_write_gently()
2: de11b30 = 2: de11b30 pkt-line: do not issue flush packets in write_packetized_*()
3: 3718da3 = 3: 3718da3 pkt-line: add PACKET_READ_GENTLE_ON_READ_ERROR option
4: b43df7a = 4: b43df7a pkt-line: add options argument to read_packetized_to_strbuf()
5: f829feb = 5: f829feb simple-ipc: design documentation for new IPC mechanism
6: 58c3fb7 = 6: 58c3fb7 simple-ipc: add win32 implementation
7: 4e8c352 = 7: 4e8c352 unix-socket: eliminate static unix_stream_socket() helper function
8: 3b71f52 = 8: 3b71f52 unix-socket: add backlog size option to unix_stream_listen()
9: 5972a19 = 9: 5972a19 unix-socket: disallow chdir() when creating unix domain sockets
10: 02c885f = 10: 02c885f unix-stream-server: create unix domain socket under lock
11: 4c21992 ! 11: eee5f47 simple-ipc: add Unix domain socket implementation
@@ compat/simple-ipc/ipc-unix-socket.c (new)
+ pthread_cond_init(&server_data->work_available_cond, NULL);
+
+ server_data->queue_size = nr_threads * FIFO_SCALE;
-+ server_data->fifo_fds = xcalloc(server_data->queue_size,
-+ sizeof(*server_data->fifo_fds));
++ CALLOC_ARRAY(server_data->fifo_fds, server_data->queue_size);
+
+ server_data->accept_thread =
+ xcalloc(1, sizeof(*server_data->accept_thread));
12: 132b6f3 = 12: 8b5dcca t0052: add simple-ipc tests and t/helper/test-simple-ipc tool

Jeff

cc: Ævar Arnfjörð Bjarmason avarab@gmail.com
cc: Jeff Hostetler git@jeffhostetler.com
cc: Jeff King peff@peff.net
cc: SZEDER Gábor szeder.dev@gmail.com
cc: Johannes Schindelin Johannes.Schindelin@gmx.de
cc: Chris Torek chris.torek@gmail.com

@jeffhostetler
Copy link
Author

PR 766 is based upon v2.29.0 whereas 717 was based on v2.28.0+strvec.

@dscho
Copy link
Member

dscho commented Oct 20, 2020

PR 766 is based upon v2.29.0 whereas 717 was based on v2.28.0+strvec.

For the record, if you hit Edit next to the PR title, it lets you re-target the PR to another branch.

@jeffhostetler
Copy link
Author

This force-push moves the base to v2.30.0.

@jeffhostetler
Copy link
Author

/submit

@gitgitgadget
Copy link

gitgitgadget bot commented Jan 12, 2021

Submitted as pull.766.git.1610465492.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git pr-766/jeffhostetler/simple-ipc-v1

To fetch this version to local tag pr-766/jeffhostetler/simple-ipc-v1:

git fetch --no-tags https://github.com/gitgitgadget/git tag pr-766/jeffhostetler/simple-ipc-v1

@@ -0,0 +1,31 @@
simple-ipc API
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Ævar Arnfjörð Bjarmason wrote (reply to this):


On Tue, Jan 12 2021, Jeff Hostetler via GitGitGadget wrote:

> From: Jeff Hostetler <jeffhost@microsoft.com>
>
> Brief design documentation for new IPC mechanism allowing
> foreground Git client to talk with an existing daemon process
> at a known location using a named pipe or unix domain socket.
>
> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
> ---
>  Documentation/technical/api-simple-ipc.txt | 31 ++++++++++++++++++++++
>  1 file changed, 31 insertions(+)
>  create mode 100644 Documentation/technical/api-simple-ipc.txt
>
> diff --git a/Documentation/technical/api-simple-ipc.txt b/Documentation/technical/api-simple-ipc.txt
> new file mode 100644
> index 00000000000..920994a69d3
> --- /dev/null
> +++ b/Documentation/technical/api-simple-ipc.txt
> @@ -0,0 +1,31 @@
> +simple-ipc API
> +==============
> +
> +The simple-ipc API is used to send an IPC message and response between
> +a (presumably) foreground Git client process to a background server or
> +daemon process.  The server process must already be running.  Multiple
> +client processes can simultaneously communicate with the server
> +process.
> +
> +Communication occurs over a named pipe on Windows and a Unix domain
> +socket on other platforms.  Clients and the server rendezvous at a
> +previously agreed-to application-specific pathname (which is outside
> +the scope of this design).
> +
> +This IPC mechanism differs from the existing `sub-process.c` model
> +(Documentation/technical/long-running-process-protocol.txt) and used
> +by applications like Git-LFS because the server is assumed to be very

s/to be very long running/to be a long running/, or at least "s/to be
very/to be a very/.

> +long running system service.  In contrast, a "sub-process model process"
> +is started with the foreground process and exits when the foreground
> +process terminates.  How the server is started is also outside the
> +scope of the IPC mechanism.
> +
> +The IPC protocol consists of a single request message from the client and
> +an optional request message from the server.  For simplicity, pkt-line
> +routines are used to hide chunking and buffering concerns.  Each side
> +terminates their message with a flush packet.
> +(Documentation/technical/protocol-common.txt)
> +
> +The actual format of the client and server messages is application
> +specific.  The IPC layer transmits and receives an opaque buffer without
> +any concern for the content within.

@gitgitgadget
Copy link

gitgitgadget bot commented Jan 12, 2021

User Ævar Arnfjörð Bjarmason <avarab@gmail.com> has been added to the cc: list.

@gitgitgadget
Copy link

gitgitgadget bot commented Jan 12, 2021

On the Git mailing list, Ævar Arnfjörð Bjarmason wrote (reply to this):


On Tue, Jan 12 2021, Jeff Hostetler via GitGitGadget wrote:

> This series introduces a multi-threaded IPC mechanism called "Simple IPC".
> This is a library-layer feature to make it easy to create very long running
> daemon/service applications and for unrelated Git commands to communicate
> with them. Communication uses pkt-line messaging over a Windows named pipe
> or Unix domain socket.
>
> On the server side, Simple IPC implements a (platform-specific) connection
> listener and worker thread-pool to accept and handle a series of client
> connections. The server functionality is completely hidden behind the
> ipc_server_run() and ipc_server_run_async() APIs. The daemon/service
> application only needs to define an application-specific callback to handle
> client requests.
>
> Note that Simple IPC is completely unrelated to the long running process
> feature (described in sub-process.h) where the lifetime of a "sub-process"
> child is bound to that of the invoking parent process and communication
> occurs over the child's stdin/stdout.
>
> Simple IPC will serve as a basis for a future builtin FSMonitor daemon
> feature.

I only skimmed this so far. In the past we had a git-read-cache--daemon
-> git-index-helper[1] -> watchman. The last iteration of that seems to
be the [3] re-roll from Ben Peart in 2017. I used/tested that for a
while and had some near-production use-cases of it.

How does this new series relate to that past work (if at all), and (not
having re-read the old threads) were there reasons those old patch
serieses weren't merged in that are addressed here, mitigated etc?

1. https://lore.kernel.org/git/1402406665-27988-1-git-send-email-pclouds@gmail.com/
2. https://lore.kernel.org/git/1457548582-28302-1-git-send-email-dturner@twopensource.com/
3. https://lore.kernel.org/git/20170518201333.13088-1-benpeart@microsoft.com/
4. https://lore.kernel.org/git/87bmhfwmqa.fsf@evledraar.gmail.com/

@gitgitgadget
Copy link

gitgitgadget bot commented Jan 12, 2021

User Ævar Arnfjörð Bjarmason <avarab@gmail.com> has been added to the cc: list.

@gitgitgadget
Copy link

gitgitgadget bot commented Jan 12, 2021

On the Git mailing list, Jeff Hostetler wrote (reply to this):



On 1/12/21 11:50 AM, Ævar Arnfjörð Bjarmason wrote:
> 
> On Tue, Jan 12 2021, Jeff Hostetler via GitGitGadget wrote:
> 
>> This series introduces a multi-threaded IPC mechanism called "Simple IPC".
>> This is a library-layer feature to make it easy to create very long running
>> daemon/service applications and for unrelated Git commands to communicate
>> with them. Communication uses pkt-line messaging over a Windows named pipe
>> or Unix domain socket.
>>
>> On the server side, Simple IPC implements a (platform-specific) connection
>> listener and worker thread-pool to accept and handle a series of client
>> connections. The server functionality is completely hidden behind the
>> ipc_server_run() and ipc_server_run_async() APIs. The daemon/service
>> application only needs to define an application-specific callback to handle
>> client requests.
>>
>> Note that Simple IPC is completely unrelated to the long running process
>> feature (described in sub-process.h) where the lifetime of a "sub-process"
>> child is bound to that of the invoking parent process and communication
>> occurs over the child's stdin/stdout.
>>
>> Simple IPC will serve as a basis for a future builtin FSMonitor daemon
>> feature.
> 
> I only skimmed this so far. In the past we had a git-read-cache--daemon
> -> git-index-helper[1] -> watchman. The last iteration of that seems to
> be the [3] re-roll from Ben Peart in 2017. I used/tested that for a
> while and had some near-production use-cases of it.
> 
> How does this new series relate to that past work (if at all), and (not
> having re-read the old threads) were there reasons those old patch
> serieses weren't merged in that are addressed here, mitigated etc?
> 
> 1. https://lore.kernel.org/git/1402406665-27988-1-git-send-email-pclouds@gmail.com/
> 2. https://lore.kernel.org/git/1457548582-28302-1-git-send-email-dturner@twopensource.com/
> 3. https://lore.kernel.org/git/20170518201333.13088-1-benpeart@microsoft.com/
> 4. https://lore.kernel.org/git/87bmhfwmqa.fsf@evledraar.gmail.com/
> 

I'm starting with the model used by the existing FSMonitor feature
that Ben Peart and Kevin Willford added to Git.

Item [3] looks to be an earlier draft of that effort.  The idea there
was to add the fsmonitor hook that could talk to a daemon like Watchman
and quickly update the in-memory cache-entry flags without the need to
lstat() and similarly update the untracked-cache.  An index extension
was added to remember the last fsmonitor response processed.

Currently in Git, we have a fsmonitor hook (usually a perl script) that
talks to Watchman and translates the Watchman response back into
something that the Git client can understand.  This comes back as a
list of files that have changed since some timestamp (or in V2, relative
to some daemon-specific token).

Items [1,2] are not related to that.  That was a different effort to
quickly fetch a read-only copy of an already-parsed index via shared
memory.  In the last version I saw, there were 2 daemons.  index-helper
kept a fresh view of the index in shared memory and could give it to
the Git client.  The client could just mmap the pre-parsed index and
avoid calling `read_index()`.  Index-helper would drive Watchman to
keep track of cache-entries as they changed and handle the lstat's.

I'm not familiar with [4] (and I only quickly scanned it).  There are
several ideas for finding slow spots while reading the index.  I don't
want to go into all of them, but several are obsolete now.  They didn't
contribute to the current effort.


The Simple IPC series (and a soon to be submitted fsmonitor--daemon
series) are intended to be a follow on to FSMonitor effort that is
currently in Git.

1. Build a git-native daemon to watch the file system and avoid needing
a third-party tool.  This doesn't preclude the use of Watchman, but
having a builtin tool might simplify engineering support costs when
deploying to a large team.

2. Use direct IPC between the Git command and the daemon to avoid the
expense of the Hook API (which is expensive on Windows).

3. Make the daemon Git-aware.  For example, it might want to pre-filter
ignored files.  (This might not be present in V1.  And we might extend
the daemon to do more of this as we improve performance.)

Jeff

@gitgitgadget
Copy link

gitgitgadget bot commented Jan 12, 2021

User Jeff Hostetler <git@jeffhostetler.com> has been added to the cc: list.

@gitgitgadget
Copy link

gitgitgadget bot commented Jan 12, 2021

On the Git mailing list, Junio C Hamano wrote (reply to this):

"Jeff Hostetler via GitGitGadget" <gitgitgadget@gmail.com> writes:

> This series introduces a multi-threaded IPC mechanism called "Simple IPC".
> This is a library-layer feature to make it easy to create very long running
> daemon/service applications and for unrelated Git commands to communicate
> with them. Communication uses pkt-line messaging over a Windows named pipe
> or Unix domain socket.
>
> On the server side, Simple IPC implements a (platform-specific) connection
> listener and worker thread-pool to accept and handle a series of client
> connections. The server functionality is completely hidden behind the
> ipc_server_run() and ipc_server_run_async() APIs. The daemon/service
> application only needs to define an application-specific callback to handle
> client requests.
>
> Note that Simple IPC is completely unrelated to the long running process
> feature (described in sub-process.h) where the lifetime of a "sub-process"
> child is bound to that of the invoking parent process and communication
> occurs over the child's stdin/stdout.
>
> Simple IPC will serve as a basis for a future builtin FSMonitor daemon
> feature.

What kind of security implications does this bring into the system?

Can a Simple IPC daemon be connected by any client?  How does the
daemon know that the other side is authorized to make requests?
When a git binary acting as client connect to whatever happens to be
listening to the well-known location, how does it know if the other
side is the daemon it wanted to talk to and not a malicious MITM or
other impersonator?

Or is this to be only used for "this is meant to be used to give
read-only access to data that is public anyway" kind of daemon,
e.g. "git://" transport that serves clones and fetches?

Or is this meant to be used on client machines where all the
processes are assumed to be working for the end user, so it is OK to
declare that anything will go (good old DOS mental model?)

I know at the Mechanism level we do not yet know how it will be
used, but we cannot retrofit sufficient security, so it would be
necessary to know answers to these questions.

Thanks.

@gitgitgadget
Copy link

gitgitgadget bot commented Jan 12, 2021

On the Git mailing list, Jeff Hostetler wrote (reply to this):



On 1/12/21 3:01 PM, Junio C Hamano wrote:
> "Jeff Hostetler via GitGitGadget" <gitgitgadget@gmail.com> writes:
> 
>> This series introduces a multi-threaded IPC mechanism called "Simple IPC".
>> This is a library-layer feature to make it easy to create very long running
>> daemon/service applications and for unrelated Git commands to communicate
>> with them. Communication uses pkt-line messaging over a Windows named pipe
>> or Unix domain socket.
>>
>> On the server side, Simple IPC implements a (platform-specific) connection
>> listener and worker thread-pool to accept and handle a series of client
>> connections. The server functionality is completely hidden behind the
>> ipc_server_run() and ipc_server_run_async() APIs. The daemon/service
>> application only needs to define an application-specific callback to handle
>> client requests.
>>
>> Note that Simple IPC is completely unrelated to the long running process
>> feature (described in sub-process.h) where the lifetime of a "sub-process"
>> child is bound to that of the invoking parent process and communication
>> occurs over the child's stdin/stdout.
>>
>> Simple IPC will serve as a basis for a future builtin FSMonitor daemon
>> feature.
> 
> What kind of security implications does this bring into the system?
> 
> Can a Simple IPC daemon be connected by any client?  How does the
> daemon know that the other side is authorized to make requests?
> When a git binary acting as client connect to whatever happens to be
> listening to the well-known location, how does it know if the other
> side is the daemon it wanted to talk to and not a malicious MITM or
> other impersonator?
> 
> Or is this to be only used for "this is meant to be used to give
> read-only access to data that is public anyway" kind of daemon,
> e.g. "git://" transport that serves clones and fetches?
> 
> Or is this meant to be used on client machines where all the
> processes are assumed to be working for the end user, so it is OK to
> declare that anything will go (good old DOS mental model?)
> 
> I know at the Mechanism level we do not yet know how it will be
> used, but we cannot retrofit sufficient security, so it would be
> necessary to know answers to these questions.
> 
> Thanks.
> 

Good questions.

Yes, this is a local-only mechanism.  A local-only named pipe on
Windows and a Unix domain socket on Unix.  In both cases the daemon
creates the pipe/socket as the foreground user (since the daemon
process will be implicitly started by the first Git command that
needs to talk to it).  Later client process try to open the pipe/socket
with RW access if they can.

On Windows a local named pipe is created by the server side.  It rejects
remote connections.  I did not put an ACL, so it should inherit the
system default which grants the user RW access (since the daemon is
implicitly started by the first foreground client command that needs
to talk to it.)  Other users in the user's group and the anonymous
user should have R but not W access to it, so they could not be able
to connect.  The name pipe is kept in the local Named Pipe File System
(NPFS) as `\\.\pipe\<unique-path>` so it is globally visible on the 
system, but I don't think it is a problem.

On the Unix side, the socket is created inside the .git directory
by the daemon.  Potential clients would have to have access to the
working directory and the .git directory to connect to the socket,
so in normal circumstances they would be able to read everything in
the WD anyway.  So again, I don't think it is a problem.


Alternatively, if a malicious server is started and holds the named
pipe or socket:  the client might be able to talk to it (assuming
that bad server grants access or impersonates the rightful user).
The client might not be able to tell they've been tricked, but at
that point the system is already compromised.

So unless I'm missing something, I think we're OK either way.

Jeff

@gitgitgadget
Copy link

gitgitgadget bot commented Jan 13, 2021

On the Git mailing list, Jeff Hostetler wrote (reply to this):



On 1/12/21 7:13 PM, Junio C Hamano wrote:
> Jeff Hostetler <git@jeffhostetler.com> writes:
> 
>> On Windows a local named pipe is created by the server side.  It rejects
>> remote connections.  I did not put an ACL, so it should inherit the
>> system default which grants the user RW access (since the daemon is
>> implicitly started by the first foreground client command that needs
>> to talk to it.)  Other users in the user's group and the anonymous
>> user should have R but not W access to it, so they could not be able
>> to connect.  The name pipe is kept in the local Named Pipe File System
>> (NPFS) as `\\.\pipe\<unique-path>` so it is globally visible on the
>> system, but I don't think it is a problem.
> 
> It is not intuitively obvious why globalluy visible thing is OK to
> me, but I'll take your word for it on stuff about Windows.

Sorry, that's a quirk of Windows.  Windows has a funky virtual drive
where named pipes are stored -- kind of like a magic directory in /proc
on Linux.  All local named pipes have the "\\.\pipe\" path prefix.

So they are globally visible as a side-effect of that "namespace"
restriction.

> 
>> On the Unix side, the socket is created inside the .git directory
>> by the daemon.  Potential clients would have to have access to the
>> working directory and the .git directory to connect to the socket,
>> so in normal circumstances they would be able to read everything in
>> the WD anyway.  So again, I don't think it is a problem.
> 
> OK, yes, writability to .git would automatically mean that
> everything is a fair game to those who can talk to the daemon, so
> there is no new issue here, as long as the first process that
> creates the socket is careful not to loosen the permission.
> 
> Thanks.
> 

@gitgitgadget
Copy link

gitgitgadget bot commented Jan 13, 2021

On the Git mailing list, Junio C Hamano wrote (reply to this):

Jeff Hostetler <git@jeffhostetler.com> writes:

> On Windows a local named pipe is created by the server side.  It rejects
> remote connections.  I did not put an ACL, so it should inherit the
> system default which grants the user RW access (since the daemon is
> implicitly started by the first foreground client command that needs
> to talk to it.)  Other users in the user's group and the anonymous
> user should have R but not W access to it, so they could not be able
> to connect.  The name pipe is kept in the local Named Pipe File System
> (NPFS) as `\\.\pipe\<unique-path>` so it is globally visible on the
> system, but I don't think it is a problem.

It is not intuitively obvious why globalluy visible thing is OK to
me, but I'll take your word for it on stuff about Windows.

> On the Unix side, the socket is created inside the .git directory
> by the daemon.  Potential clients would have to have access to the
> working directory and the .git directory to connect to the socket,
> so in normal circumstances they would be able to read everything in
> the WD anyway.  So again, I don't think it is a problem.

OK, yes, writability to .git would automatically mean that
everything is a fair game to those who can talk to the daemon, so
there is no new issue here, as long as the first process that
creates the socket is careful not to loosen the permission.

Thanks.

@@ -196,7 +196,7 @@ int packet_write_fmt_gently(int fd, const char *fmt, ...)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Jeff King wrote (reply to this):

On Tue, Jan 12, 2021 at 03:31:23PM +0000, Jeff Hostetler via GitGitGadget wrote:

> Teach packet_write_gently() to use a stack buffer rather than a static
> buffer when composing the packet line message.  This helps get us ready
> for threaded operations.

Sounds like a good goal, but...

>  static int packet_write_gently(const int fd_out, const char *buf, size_t size)
>  {
> -	static char packet_write_buffer[LARGE_PACKET_MAX];
> +	char packet_write_buffer[LARGE_PACKET_MAX];
>  	size_t packet_size;

64k is awfully big for the stack, especially if you are thinking about
having threads. I know we've run into issues around that size before
(though I don't offhand recall whether there was any recursion
involved).

We might need to use thread-local storage here. Heap would also
obviously work, but I don't think we'd want a new allocation per write
(or maybe it wouldn't matter; we're making a syscall, so a malloc() may
not be that big a deal in terms of performance).

-Peff

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Jeff Hostetler wrote (reply to this):



On 1/13/21 8:29 AM, Jeff King wrote:
> On Tue, Jan 12, 2021 at 03:31:23PM +0000, Jeff Hostetler via GitGitGadget wrote:
> 
>> Teach packet_write_gently() to use a stack buffer rather than a static
>> buffer when composing the packet line message.  This helps get us ready
>> for threaded operations.
> 
> Sounds like a good goal, but...
> 
>>   static int packet_write_gently(const int fd_out, const char *buf, size_t size)
>>   {
>> -	static char packet_write_buffer[LARGE_PACKET_MAX];
>> +	char packet_write_buffer[LARGE_PACKET_MAX];
>>   	size_t packet_size;
> 
> 64k is awfully big for the stack, especially if you are thinking about
> having threads. I know we've run into issues around that size before
> (though I don't offhand recall whether there was any recursion
> involved).
> 
> We might need to use thread-local storage here. Heap would also
> obviously work, but I don't think we'd want a new allocation per write
> (or maybe it wouldn't matter; we're making a syscall, so a malloc() may
> not be that big a deal in terms of performance).
> 
> -Peff
> 

Good point.

I'll look at the callers and see if I can do something safer.

Jeeff

@gitgitgadget
Copy link

gitgitgadget bot commented Jan 13, 2021

User Jeff King <peff@peff.net> has been added to the cc: list.

@gitgitgadget
Copy link

gitgitgadget bot commented Jan 13, 2021

On the Git mailing list, Jeff King wrote (reply to this):

On Tue, Jan 12, 2021 at 06:25:20PM -0500, Jeff Hostetler wrote:

> On the Unix side, the socket is created inside the .git directory
> by the daemon.  Potential clients would have to have access to the
> working directory and the .git directory to connect to the socket,
> so in normal circumstances they would be able to read everything in
> the WD anyway.  So again, I don't think it is a problem.

Just thinking out loud, here are two potential issues with putting it in
.git that we may have to deal with later:

  - fsmonitor is conceptually a read-only thing (i.e., it would speed up
    "git status", etc). And not knowing much about how it will work, I'd
    guess that is carried through (i.e., even though you may open the
    socket R/W so that you can write requests and read them back, there
    is no operation you can request that will overwrite data). But the
    running user may not have write access to .git.

    As long as we cleanly bail to the non-fsmonitor code paths, I don't
    think it's the end of the world. Those read-only users just won't
    get to use the speedup (and it may even be desirable). They may
    complain, but it is open source so the onus is on them to improve
    it. You will not have made anything worse. :)

  - repositories may be on network filesystems that do not support unix
    sockets.

So it would be nice if there was some way to specify an alternate path
to be used for the socket. Possibly one or both of:

  - a config option to give a root path for sockets, where Git would
    then canonicalize the $GIT_DIR name and use $root/$GIT_DIR for the
    socket. That solves the problem for a given user once for all repos.

  - a config option to say "use this path for the socket". This would be
    per-repo, but is more flexible and possibly less confusing.

One final note: on some systems[1] the permissions on the socket file
itself are ignored. The safe way to protect it is to make sure the
permissions on the surrounding directory are what you want. See
credential-cache's init_socket_directory() for an example.

-Peff

[1] Sorry, I don't remember which systems. This is one of those random
    bits of Unix lore I've carried around for 20 years, and it's
    entirely possible it is simply obsolete at this point.

unix-socket.c Outdated
@@ -121,3 +121,42 @@ int unix_stream_listen(const char *path)
errno = saved_errno;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Jeff King wrote (reply to this):

On Tue, Jan 12, 2021 at 03:31:29PM +0000, Jeff Hostetler via GitGitGadget wrote:

> From: Jeff Hostetler <jeffhost@microsoft.com>
> 
> Create a gentle version of `unix_stream_listen()`.  This version does
> not call `die()` if a socket-fd cannot be created and does not assume
> that it is safe to `unlink()` an existing socket-inode.

The existing one is meant to be gentle. Maybe it is worth fixing it
instead.

> `unix_stream_listen()` uses `unix_stream_socket()` helper function to
> create the socket-fd.  Avoid that helper because it calls `die()` on
> errors.

Yeah, I think this is just a bug. My thinking in the original was that
socket() would basically never fail. And it generally wouldn't, but
things like EMFILE do happen. There are only two callers, and both would
be one-liners to propagate the error up the stack.

> `unix_stream_listen()` always tries to `unlink()` the socket-path before
> calling `bind()`.  If there is an existing server/daemon already bound
> and listening on that socket-path, our `unlink()` would have the effect
> of disassociating the existing server's bound-socket-fd from the socket-path
> without notifying the existing server.  The existing server could continue
> to service existing connections (accepted-socket-fd's), but would not
> receive any futher new connections (since clients rendezvous via the
> socket-path).  The existing server would effectively be offline but yet
> appear to be active.

The trouble here is that one cannot tell if the existing file is active,
and you are orphaning an existing server, or if there is leftover cruft
from an exited server that did not clean up after itself (you will get
EADDRINUSE either way).

Handling those cases (and especially doing so in a non-racy way) is
probably outside the scope of unix_stream_listen(), but it makes sense
for this to be an option. And it looks like you even made it so here,
so unix_stream_listen() could just become a wrapper that sets the
option. Or since there is only one caller in the whole code-base,
perhaps it could just learn to pass the option struct. :)

Likewise for the no-chdir option added in the follow-on patch.

> Furthermore, `unix_stream_listen()` creates an opportunity for a brief
> race condition for connecting clients if they try to connect in the
> interval between the forced `unlink()` and the subsequent `bind()` (which
> recreates the socket-path that is bound to a new socket-fd in the current
> process).

I'll be curious to see how you do this atomically. From my skim of patch
10, you will connect to see if it's active, and unlink if it's not. But
then two simultaneous new processes could both see an inactive one and
race to forcefully create the new one. One of them will lose and be
orphaned with a socket that has no filesystem name.

There might be a solution using link() to have an atomic winner, but it
gets tricky around unlinking the old name out of the way. You might need
a separate dot-lock to make sure only one process does the
unlink-and-create process at a time.

-Peff

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Chris Torek wrote (reply to this):

I had saved this to comment on, but Peff beat me to it :-)

On Wed, Jan 13, 2021 at 6:07 AM Jeff King <peff@peff.net> wrote:
> There might be a solution using link() to have an atomic winner, but it
> gets tricky around unlinking the old name out of the way.

You definitely should be able to do this atomically with link(), but
the cleanup is indeed messy, and there's already existing locking
code, so it's probably better to press that into service here.

Chris

@gitgitgadget
Copy link

gitgitgadget bot commented Jan 13, 2021

On the Git mailing list, Ævar Arnfjörð Bjarmason wrote (reply to this):


On Wed, Jan 13 2021, Jeff King wrote:

> On Tue, Jan 12, 2021 at 06:25:20PM -0500, Jeff Hostetler wrote:
>
>> On the Unix side, the socket is created inside the .git directory
>> by the daemon.  Potential clients would have to have access to the
>> working directory and the .git directory to connect to the socket,
>> so in normal circumstances they would be able to read everything in
>> the WD anyway.  So again, I don't think it is a problem.
>
> Just thinking out loud, here are two potential issues with putting it in
> .git that we may have to deal with later:
>
>   - fsmonitor is conceptually a read-only thing (i.e., it would speed up
>     "git status", etc). And not knowing much about how it will work, I'd
>     guess that is carried through (i.e., even though you may open the
>     socket R/W so that you can write requests and read them back, there
>     is no operation you can request that will overwrite data). But the
>     running user may not have write access to .git.
>
>     As long as we cleanly bail to the non-fsmonitor code paths, I don't
>     think it's the end of the world. Those read-only users just won't
>     get to use the speedup (and it may even be desirable). They may
>     complain, but it is open source so the onus is on them to improve
>     it. You will not have made anything worse. :)
>
>   - repositories may be on network filesystems that do not support unix
>     sockets.
>
> So it would be nice if there was some way to specify an alternate path
> to be used for the socket. Possibly one or both of:
>
>   - a config option to give a root path for sockets, where Git would
>     then canonicalize the $GIT_DIR name and use $root/$GIT_DIR for the
>     socket. That solves the problem for a given user once for all repos.
>
>   - a config option to say "use this path for the socket". This would be
>     per-repo, but is more flexible and possibly less confusing.
>
> One final note: on some systems[1] the permissions on the socket file
> itself are ignored. The safe way to protect it is to make sure the
> permissions on the surrounding directory are what you want. See
> credential-cache's init_socket_directory() for an example.
>
> -Peff
>
> [1] Sorry, I don't remember which systems. This is one of those random
>     bits of Unix lore I've carried around for 20 years, and it's
>     entirely possible it is simply obsolete at this point.

According to StackExchange lore this seems to have been the case with
4.2 BSD & maybe something obscure like HP/UX:
https://unix.stackexchange.com/questions/83032/which-systems-do-not-honor-socket-read-write-permissions

I'd say it's probably safe to ignore this as a concern for new features
in git in general, and certainly for something like a thing intended for
a watchman-like program which users are likely to only run on modern
OS's.

@gitgitgadget
Copy link

gitgitgadget bot commented Jan 14, 2021

User Chris Torek <chris.torek@gmail.com> has been added to the cc: list.

@jeffhostetler jeffhostetler changed the title [RFC] Simple IPC Mechanism Simple IPC Mechanism Feb 1, 2021
@gitgitgadget
Copy link

gitgitgadget bot commented Mar 15, 2021

This patch series was integrated into seen via git@b24af91.

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 17, 2021

There was a status update about the branch jh/simple-ipc on the Git mailing list:

A simple IPC interface gets introduced to build services like
fsmonitor on top.

Will merge to 'next'.

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 19, 2021

This patch series was integrated into seen via git@667a6e3.

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 19, 2021

This patch series was integrated into seen via git@0e46061.

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 20, 2021

This patch series was integrated into seen via git@1f6aaaa.

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 20, 2021

There was a status update about the branch jh/simple-ipc on the Git mailing list:

A simple IPC interface gets introduced to build services like
fsmonitor on top.

Expecting an update for SQUASH???

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 22, 2021

This patch series was integrated into seen via git@48491b7.

Create Unix domain socket based implementation of "simple-ipc".

A set of `ipc_client` routines implement a client library to connect
to an `ipc_server` over a Unix domain socket, send a simple request,
and receive a single response.  Clients use blocking IO on the socket.

A set of `ipc_server` routines implement a thread pool to listen for
and concurrently service client connections.

The server creates a new Unix domain socket at a known location.  If a
socket already exists with that name, the server tries to determine if
another server is already listening on the socket or if the socket is
dead.  If socket is busy, the server exits with an error rather than
stealing the socket.  If the socket is dead, the server creates a new
one and starts up.

If while running, the server detects that its socket has been stolen
by another server, it automatically exits.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Create t0052-simple-ipc.sh with unit tests for the "simple-ipc" mechanism.

Create t/helper/test-simple-ipc test tool to exercise the "simple-ipc"
functions.

When the tool is invoked with "run-daemon", it runs a server to listen
for "simple-ipc" connections on a test socket or named pipe and
responds to a set of commands to exercise/stress the communication
setup.

When the tool is invoked with "start-daemon", it spawns a "run-daemon"
command in the background and waits for the server to become ready
before exiting.  (This helps make unit tests in t0052 more predictable
and avoids the need for arbitrary sleeps in the test script.)

The tool also has a series of client "send" commands to send commands
and data to a server instance.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
@jeffhostetler
Copy link
Author

/submit

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 22, 2021

Submitted as pull.766.v7.git.1616408989.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git pr-766/jeffhostetler/simple-ipc-v7

To fetch this version to local tag pr-766/jeffhostetler/simple-ipc-v7:

git fetch --no-tags https://github.com/gitgitgadget/git tag pr-766/jeffhostetler/simple-ipc-v7

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 22, 2021

This patch series was integrated into seen via git@8652114.

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 22, 2021

This patch series was integrated into seen via git@d562ddd.

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 23, 2021

This patch series was integrated into seen via git@17271fa.

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 23, 2021

There was a status update about the branch jh/simple-ipc on the Git mailing list:

A simple IPC interface gets introduced to build services like
fsmonitor on top.

Will merge to 'next'.

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 23, 2021

This patch series was integrated into seen via git@3ecd81b.

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 24, 2021

This patch series was integrated into seen via git@32f3cf4.

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 24, 2021

This patch series was integrated into next via git@3ebcedf.

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 26, 2021

This patch series was integrated into seen via git@bc9ebc4.

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 30, 2021

This patch series was integrated into seen via git@910d4f2.

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 31, 2021

There was a status update about the branch jh/simple-ipc on the Git mailing list:

A simple IPC interface gets introduced to build services like
fsmonitor on top.

Will merge to 'master'.

@gitgitgadget
Copy link

gitgitgadget bot commented Mar 31, 2021

This patch series was integrated into seen via git@99e734d.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 2, 2021

This patch series was integrated into seen via git@ff338b5.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 2, 2021

This patch series was integrated into seen via git@861794b.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 2, 2021

This patch series was integrated into next via git@861794b.

@gitgitgadget
Copy link

gitgitgadget bot commented Apr 2, 2021

This patch series was integrated into master via git@861794b.

@gitgitgadget gitgitgadget bot added the master label Apr 2, 2021
@gitgitgadget gitgitgadget bot closed this Apr 2, 2021
@gitgitgadget
Copy link

gitgitgadget bot commented Apr 2, 2021

Closed via 861794b.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants