Coverage Report

Created: 2024-09-08 06:24

/src/git/sub-process.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Generic implementation of background process infrastructure.
3
 */
4
#include "git-compat-util.h"
5
#include "sub-process.h"
6
#include "sigchain.h"
7
#include "pkt-line.h"
8
9
int cmd2process_cmp(const void *cmp_data UNUSED,
10
        const struct hashmap_entry *eptr,
11
        const struct hashmap_entry *entry_or_key,
12
        const void *keydata UNUSED)
13
0
{
14
0
  const struct subprocess_entry *e1, *e2;
15
16
0
  e1 = container_of(eptr, const struct subprocess_entry, ent);
17
0
  e2 = container_of(entry_or_key, const struct subprocess_entry, ent);
18
19
0
  return strcmp(e1->cmd, e2->cmd);
20
0
}
21
22
struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const char *cmd)
23
0
{
24
0
  struct subprocess_entry key;
25
26
0
  hashmap_entry_init(&key.ent, strhash(cmd));
27
0
  key.cmd = cmd;
28
0
  return hashmap_get_entry(hashmap, &key, ent, NULL);
29
0
}
30
31
int subprocess_read_status(int fd, struct strbuf *status)
32
0
{
33
0
  struct strbuf **pair;
34
0
  char *line;
35
0
  int len;
36
37
0
  for (;;) {
38
0
    len = packet_read_line_gently(fd, NULL, &line);
39
0
    if ((len < 0) || !line)
40
0
      break;
41
0
    pair = strbuf_split_str(line, '=', 2);
42
0
    if (pair[0] && pair[0]->len && pair[1]) {
43
      /* the last "status=<foo>" line wins */
44
0
      if (!strcmp(pair[0]->buf, "status=")) {
45
0
        strbuf_reset(status);
46
0
        strbuf_addbuf(status, pair[1]);
47
0
      }
48
0
    }
49
0
    strbuf_list_free(pair);
50
0
  }
51
52
0
  return (len < 0) ? len : 0;
53
0
}
54
55
void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry)
56
0
{
57
0
  if (!entry)
58
0
    return;
59
60
0
  entry->process.clean_on_exit = 0;
61
0
  kill(entry->process.pid, SIGTERM);
62
0
  finish_command(&entry->process);
63
64
0
  hashmap_remove(hashmap, &entry->ent, NULL);
65
0
}
66
67
static void subprocess_exit_handler(struct child_process *process)
68
0
{
69
0
  sigchain_push(SIGPIPE, SIG_IGN);
70
  /* Closing the pipe signals the subprocess to initiate a shutdown. */
71
0
  close(process->in);
72
0
  close(process->out);
73
0
  sigchain_pop(SIGPIPE);
74
  /* Finish command will wait until the shutdown is complete. */
75
0
  finish_command(process);
76
0
}
77
78
int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, const char *cmd,
79
  subprocess_start_fn startfn)
80
0
{
81
0
  int err;
82
0
  struct child_process *process;
83
84
0
  entry->cmd = cmd;
85
0
  process = &entry->process;
86
87
0
  child_process_init(process);
88
0
  strvec_push(&process->args, cmd);
89
0
  process->use_shell = 1;
90
0
  process->in = -1;
91
0
  process->out = -1;
92
0
  process->clean_on_exit = 1;
93
0
  process->clean_on_exit_handler = subprocess_exit_handler;
94
0
  process->trace2_child_class = "subprocess";
95
96
0
  err = start_command(process);
97
0
  if (err) {
98
0
    error("cannot fork to run subprocess '%s'", cmd);
99
0
    return err;
100
0
  }
101
102
0
  hashmap_entry_init(&entry->ent, strhash(cmd));
103
104
0
  err = startfn(entry);
105
0
  if (err) {
106
0
    error("initialization for subprocess '%s' failed", cmd);
107
0
    subprocess_stop(hashmap, entry);
108
0
    return err;
109
0
  }
110
111
0
  hashmap_add(hashmap, &entry->ent);
112
0
  return 0;
113
0
}
114
115
static int handshake_version(struct child_process *process,
116
           const char *welcome_prefix, int *versions,
117
           int *chosen_version)
118
0
{
119
0
  int version_scratch;
120
0
  int i;
121
0
  char *line;
122
0
  const char *p;
123
124
0
  if (!chosen_version)
125
0
    chosen_version = &version_scratch;
126
127
0
  if (packet_write_fmt_gently(process->in, "%s-client\n",
128
0
            welcome_prefix))
129
0
    return error("Could not write client identification");
130
0
  for (i = 0; versions[i]; i++) {
131
0
    if (packet_write_fmt_gently(process->in, "version=%d\n",
132
0
              versions[i]))
133
0
      return error("Could not write requested version");
134
0
  }
135
0
  if (packet_flush_gently(process->in))
136
0
    return error("Could not write flush packet");
137
138
0
  if (!(line = packet_read_line(process->out, NULL)) ||
139
0
      !skip_prefix(line, welcome_prefix, &p) ||
140
0
      strcmp(p, "-server"))
141
0
    return error("Unexpected line '%s', expected %s-server",
142
0
           line ? line : "<flush packet>", welcome_prefix);
143
0
  if (!(line = packet_read_line(process->out, NULL)) ||
144
0
      !skip_prefix(line, "version=", &p) ||
145
0
      strtol_i(p, 10, chosen_version))
146
0
    return error("Unexpected line '%s', expected version",
147
0
           line ? line : "<flush packet>");
148
0
  if ((line = packet_read_line(process->out, NULL)))
149
0
    return error("Unexpected line '%s', expected flush", line);
150
151
  /* Check to make sure that the version received is supported */
152
0
  for (i = 0; versions[i]; i++) {
153
0
    if (versions[i] == *chosen_version)
154
0
      break;
155
0
  }
156
0
  if (!versions[i])
157
0
    return error("Version %d not supported", *chosen_version);
158
159
0
  return 0;
160
0
}
161
162
static int handshake_capabilities(struct child_process *process,
163
          struct subprocess_capability *capabilities,
164
          unsigned int *supported_capabilities)
165
0
{
166
0
  int i;
167
0
  char *line;
168
169
0
  for (i = 0; capabilities[i].name; i++) {
170
0
    if (packet_write_fmt_gently(process->in, "capability=%s\n",
171
0
              capabilities[i].name))
172
0
      return error("Could not write requested capability");
173
0
  }
174
0
  if (packet_flush_gently(process->in))
175
0
    return error("Could not write flush packet");
176
177
0
  while ((line = packet_read_line(process->out, NULL))) {
178
0
    const char *p;
179
0
    if (!skip_prefix(line, "capability=", &p))
180
0
      continue;
181
182
0
    for (i = 0;
183
0
         capabilities[i].name && strcmp(p, capabilities[i].name);
184
0
         i++)
185
0
      ;
186
0
    if (capabilities[i].name) {
187
0
      if (supported_capabilities)
188
0
        *supported_capabilities |= capabilities[i].flag;
189
0
    } else {
190
0
      die("subprocess '%s' requested unsupported capability '%s'",
191
0
          process->args.v[0], p);
192
0
    }
193
0
  }
194
195
0
  return 0;
196
0
}
197
198
int subprocess_handshake(struct subprocess_entry *entry,
199
       const char *welcome_prefix,
200
       int *versions,
201
       int *chosen_version,
202
       struct subprocess_capability *capabilities,
203
       unsigned int *supported_capabilities)
204
0
{
205
0
  int retval;
206
0
  struct child_process *process = &entry->process;
207
208
0
  sigchain_push(SIGPIPE, SIG_IGN);
209
210
0
  retval = handshake_version(process, welcome_prefix, versions,
211
0
           chosen_version) ||
212
0
     handshake_capabilities(process, capabilities,
213
0
          supported_capabilities);
214
215
0
  sigchain_pop(SIGPIPE);
216
0
  return retval;
217
0
}