Coverage Report

Created: 2026-11-12 06:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/unix-stream-server.c
Line
Count
Source
1
#include "git-compat-util.h"
2
#include "lockfile.h"
3
#include "unix-socket.h"
4
#include "unix-stream-server.h"
5
6
0
#define DEFAULT_LOCK_TIMEOUT (100)
7
8
/*
9
 * Try to connect to a unix domain socket at `path` (if it exists) and
10
 * see if there is a server listening.
11
 *
12
 * We don't know if the socket exists, whether a server died and
13
 * failed to cleanup, or whether we have a live server listening, so
14
 * we "poke" it.
15
 *
16
 * We immediately hangup without sending/receiving any data because we
17
 * don't know anything about the protocol spoken and don't want to
18
 * block while writing/reading data.  It is sufficient to just know
19
 * that someone is listening.
20
 */
21
static int is_another_server_alive(const char *path,
22
           const struct unix_stream_listen_opts *opts)
23
0
{
24
0
  int fd = unix_stream_connect(path, opts->disallow_chdir);
25
0
  if (fd >= 0) {
26
0
    close(fd);
27
0
    return 1;
28
0
  }
29
30
0
  return 0;
31
0
}
32
33
int unix_ss_create(const char *path,
34
       const struct unix_stream_listen_opts *opts,
35
       long timeout_ms,
36
       struct unix_ss_socket **new_server_socket)
37
0
{
38
0
  struct lock_file lock = LOCK_INIT;
39
0
  int fd_socket;
40
0
  struct unix_ss_socket *server_socket;
41
42
0
  *new_server_socket = NULL;
43
44
0
  if (timeout_ms < 0)
45
0
    timeout_ms = DEFAULT_LOCK_TIMEOUT;
46
47
  /*
48
   * Create a lock at "<path>.lock" if we can.
49
   */
50
0
  if (hold_lock_file_for_update_timeout(&lock, path, 0, timeout_ms) < 0)
51
0
    return -1;
52
53
  /*
54
   * If another server is listening on "<path>" give up.  We do not
55
   * want to create a socket and steal future connections from them.
56
   */
57
0
  if (is_another_server_alive(path, opts)) {
58
0
    rollback_lock_file(&lock);
59
0
    errno = EADDRINUSE;
60
0
    return -2;
61
0
  }
62
63
  /*
64
   * Create and bind to a Unix domain socket at "<path>".
65
   */
66
0
  fd_socket = unix_stream_listen(path, opts);
67
0
  if (fd_socket < 0) {
68
0
    int saved_errno = errno;
69
0
    rollback_lock_file(&lock);
70
0
    errno = saved_errno;
71
0
    return -1;
72
0
  }
73
74
0
  server_socket = xcalloc(1, sizeof(*server_socket));
75
0
  server_socket->path_socket = strdup(path);
76
0
  server_socket->fd_socket = fd_socket;
77
0
  lstat(path, &server_socket->st_socket);
78
79
0
  *new_server_socket = server_socket;
80
81
  /*
82
   * Always rollback (just delete) "<path>.lock" because we already created
83
   * "<path>" as a socket and do not want to commit_lock to do the atomic
84
   * rename trick.
85
   */
86
0
  rollback_lock_file(&lock);
87
88
0
  return 0;
89
0
}
90
91
void unix_ss_free(struct unix_ss_socket *server_socket)
92
0
{
93
0
  if (!server_socket)
94
0
    return;
95
96
0
  if (server_socket->fd_socket >= 0) {
97
0
    if (!unix_ss_was_stolen(server_socket))
98
0
      unlink(server_socket->path_socket);
99
0
    close(server_socket->fd_socket);
100
0
  }
101
102
0
  free(server_socket->path_socket);
103
0
  free(server_socket);
104
0
}
105
106
int unix_ss_was_stolen(struct unix_ss_socket *server_socket)
107
0
{
108
0
  struct stat st_now;
109
110
0
  if (!server_socket)
111
0
    return 0;
112
113
0
  if (lstat(server_socket->path_socket, &st_now) == -1)
114
0
    return 1;
115
116
0
  if (st_now.st_ino != server_socket->st_socket.st_ino)
117
0
    return 1;
118
0
  if (st_now.st_dev != server_socket->st_socket.st_dev)
119
0
    return 1;
120
121
0
  if (!S_ISSOCK(st_now.st_mode))
122
0
    return 1;
123
124
0
  return 0;
125
0
}