Skip to content

git_remote_disconnect leaves open file descriptors behind #7356

Description

@civodul

As of 1.9.7, the program below shows that, after git_remote_disconnect, an open file descriptor (socket connected to the Git server) is left behind:

#include <git2.h>
#include <assert.h>
#include <stdio.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <sys/socket.h>
#include <assert.h>

#define REPO_URL "https://codeberg.org/shepherd/shepherd.git"
GIT_EXTERN(int) git_repository__cleanup(git_repository *repo);

static void
show_open_file_descriptors ()
{
  DIR *dir;
  struct dirent *ent;

  dir = opendir ("/proc/self/fd");
  chdir ("/proc/self/fd");
  for (ent = NULL; ent = readdir (dir), ent != NULL; )
    {
      if (strcmp (ent->d_name, ".") == 0 || strcmp (ent->d_name, "..") == 0)
        continue;

      char target[1024];
      ssize_t size;
      size = readlink (ent->d_name, target, sizeof target);
      target[size < 0 ? 0 : size] = '\0';
      printf ("%s -> %s\n", ent->d_name, target);
    }
  closedir (dir);
}

int
main ()
{
  int err;
  err = git_libgit2_init ();
  assert (err == 1);

  git_clone_options opts;
  err = git_clone_init_options (&opts, GIT_CLONE_OPTIONS_VERSION);
  assert (err == 0);

  git_repository *repo;
  err = git_clone (&repo, REPO_URL, "/tmp/example", &opts);
  assert (err == 0);
  git_repository_free (repo);

  show_open_file_descriptors ();

  err = git_repository_open (&repo, "/tmp/example");
  assert (err == 0);

  git_remote *remote;
  err = git_remote_lookup (&remote, repo, "origin");
  assert (err == 0);

  err = git_remote_fetch (remote, NULL, NULL, NULL);
  assert (err == 0);

  err = git_remote_disconnect (remote);
  assert (err == 0);

  /* git_remote_free (remote); */
  /* assert (err == 0); */

  /* git_repository__cleanup (repo); */
  git_repository_free (repo);
  printf ("%s:%i\n", __FILE__, __LINE__);
  show_open_file_descriptors ();

  return 0;
}

Uncomment the git_remote_free call and the socket gets closed.

My suggestion is to change git_remote_disconnect or rather its backend so that no file descriptor is left open once it's been called; the patch below does exactly that:

diff --git a/src/libgit2/transports/http.c b/src/libgit2/transports/http.c
index f344888d0..15fe8dfb6 100644
--- a/src/libgit2/transports/http.c
+++ b/src/libgit2/transports/http.c
@@ -729,6 +729,10 @@ static int http_close(git_smart_subtransport *t)
 	git_net_url_dispose(&transport->server.url);
 	git_net_url_dispose(&transport->proxy.url);
 
+	/* The call below closes associated file descriptors.  */
+	git_http_client_free(transport->http_client);
+	transport->http_client = NULL;
+
 	return 0;
 }
 
@@ -736,8 +740,6 @@ static void http_free(git_smart_subtransport *t)
 {
 	http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
 
-	git_http_client_free(transport->http_client);
-
 	http_close(t);
 	git__free(transport);
 }

WDYT? I can make it a pull request if that sounds like the right approach.

(This was originally reported here.)

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions