Imported Upstream version 2.25.4
[platform/upstream/git.git] / sub-process.c
index a3cfab1..1b1af9d 100644 (file)
@@ -6,10 +6,15 @@
 #include "pkt-line.h"
 
 int cmd2process_cmp(const void *unused_cmp_data,
-                   const struct subprocess_entry *e1,
-                   const struct subprocess_entry *e2,
+                   const struct hashmap_entry *eptr,
+                   const struct hashmap_entry *entry_or_key,
                    const void *unused_keydata)
 {
+       const struct subprocess_entry *e1, *e2;
+
+       e1 = container_of(eptr, const struct subprocess_entry, ent);
+       e2 = container_of(entry_or_key, const struct subprocess_entry, ent);
+
        return strcmp(e1->cmd, e2->cmd);
 }
 
@@ -17,9 +22,9 @@ struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const ch
 {
        struct subprocess_entry key;
 
-       hashmap_entry_init(&key, strhash(cmd));
+       hashmap_entry_init(&key.ent, strhash(cmd));
        key.cmd = cmd;
-       return hashmap_get(hashmap, &key, NULL);
+       return hashmap_get_entry(hashmap, &key, ent, NULL);
 }
 
 int subprocess_read_status(int fd, struct strbuf *status)
@@ -55,7 +60,7 @@ void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry)
        kill(entry->process.pid, SIGTERM);
        finish_command(&entry->process);
 
-       hashmap_remove(hashmap, entry, NULL);
+       hashmap_remove(hashmap, &entry->ent, NULL);
 }
 
 static void subprocess_exit_handler(struct child_process *process)
@@ -74,18 +79,18 @@ int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, co
 {
        int err;
        struct child_process *process;
-       const char *argv[] = { cmd, NULL };
 
        entry->cmd = cmd;
        process = &entry->process;
 
        child_process_init(process);
-       process->argv = argv;
+       argv_array_push(&process->args, cmd);
        process->use_shell = 1;
        process->in = -1;
        process->out = -1;
        process->clean_on_exit = 1;
        process->clean_on_exit_handler = subprocess_exit_handler;
+       process->trace2_child_class = "subprocess";
 
        err = start_command(process);
        if (err) {
@@ -93,7 +98,7 @@ int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, co
                return err;
        }
 
-       hashmap_entry_init(entry, strhash(cmd));
+       hashmap_entry_init(&entry->ent, strhash(cmd));
 
        err = startfn(entry);
        if (err) {
@@ -102,6 +107,110 @@ int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, co
                return err;
        }
 
-       hashmap_add(hashmap, entry);
+       hashmap_add(hashmap, &entry->ent);
        return 0;
 }
+
+static int handshake_version(struct child_process *process,
+                            const char *welcome_prefix, int *versions,
+                            int *chosen_version)
+{
+       int version_scratch;
+       int i;
+       char *line;
+       const char *p;
+
+       if (!chosen_version)
+               chosen_version = &version_scratch;
+
+       if (packet_write_fmt_gently(process->in, "%s-client\n",
+                                   welcome_prefix))
+               return error("Could not write client identification");
+       for (i = 0; versions[i]; i++) {
+               if (packet_write_fmt_gently(process->in, "version=%d\n",
+                                           versions[i]))
+                       return error("Could not write requested version");
+       }
+       if (packet_flush_gently(process->in))
+               return error("Could not write flush packet");
+
+       if (!(line = packet_read_line(process->out, NULL)) ||
+           !skip_prefix(line, welcome_prefix, &p) ||
+           strcmp(p, "-server"))
+               return error("Unexpected line '%s', expected %s-server",
+                            line ? line : "<flush packet>", welcome_prefix);
+       if (!(line = packet_read_line(process->out, NULL)) ||
+           !skip_prefix(line, "version=", &p) ||
+           strtol_i(p, 10, chosen_version))
+               return error("Unexpected line '%s', expected version",
+                            line ? line : "<flush packet>");
+       if ((line = packet_read_line(process->out, NULL)))
+               return error("Unexpected line '%s', expected flush", line);
+
+       /* Check to make sure that the version received is supported */
+       for (i = 0; versions[i]; i++) {
+               if (versions[i] == *chosen_version)
+                       break;
+       }
+       if (!versions[i])
+               return error("Version %d not supported", *chosen_version);
+
+       return 0;
+}
+
+static int handshake_capabilities(struct child_process *process,
+                                 struct subprocess_capability *capabilities,
+                                 unsigned int *supported_capabilities)
+{
+       int i;
+       char *line;
+
+       for (i = 0; capabilities[i].name; i++) {
+               if (packet_write_fmt_gently(process->in, "capability=%s\n",
+                                           capabilities[i].name))
+                       return error("Could not write requested capability");
+       }
+       if (packet_flush_gently(process->in))
+               return error("Could not write flush packet");
+
+       while ((line = packet_read_line(process->out, NULL))) {
+               const char *p;
+               if (!skip_prefix(line, "capability=", &p))
+                       continue;
+
+               for (i = 0;
+                    capabilities[i].name && strcmp(p, capabilities[i].name);
+                    i++)
+                       ;
+               if (capabilities[i].name) {
+                       if (supported_capabilities)
+                               *supported_capabilities |= capabilities[i].flag;
+               } else {
+                       die("subprocess '%s' requested unsupported capability '%s'",
+                           process->argv[0], p);
+               }
+       }
+
+       return 0;
+}
+
+int subprocess_handshake(struct subprocess_entry *entry,
+                        const char *welcome_prefix,
+                        int *versions,
+                        int *chosen_version,
+                        struct subprocess_capability *capabilities,
+                        unsigned int *supported_capabilities)
+{
+       int retval;
+       struct child_process *process = &entry->process;
+
+       sigchain_push(SIGPIPE, SIG_IGN);
+
+       retval = handshake_version(process, welcome_prefix, versions,
+                                  chosen_version) ||
+                handshake_capabilities(process, capabilities,
+                                       supported_capabilities);
+
+       sigchain_pop(SIGPIPE);
+       return retval;
+}