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