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