Merge branch 'dc/fmt-merge-msg-microcleanup'
[git] / sub-process.c
1 /*
2  * Generic implementation of background process infrastructure.
3  */
4 #include "sub-process.h"
5 #include "sigchain.h"
6 #include "pkt-line.h"
7
8 int cmd2process_cmp(const void *unused_cmp_data,
9                     const void *entry,
10                     const void *entry_or_key,
11                     const void *unused_keydata)
12 {
13         const struct subprocess_entry *e1 = entry;
14         const struct subprocess_entry *e2 = entry_or_key;
15
16         return strcmp(e1->cmd, e2->cmd);
17 }
18
19 struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const char *cmd)
20 {
21         struct subprocess_entry key;
22
23         hashmap_entry_init(&key, strhash(cmd));
24         key.cmd = cmd;
25         return hashmap_get(hashmap, &key, NULL);
26 }
27
28 int subprocess_read_status(int fd, struct strbuf *status)
29 {
30         struct strbuf **pair;
31         char *line;
32         int len;
33
34         for (;;) {
35                 len = packet_read_line_gently(fd, NULL, &line);
36                 if ((len < 0) || !line)
37                         break;
38                 pair = strbuf_split_str(line, '=', 2);
39                 if (pair[0] && pair[0]->len && pair[1]) {
40                         /* the last "status=<foo>" line wins */
41                         if (!strcmp(pair[0]->buf, "status=")) {
42                                 strbuf_reset(status);
43                                 strbuf_addbuf(status, pair[1]);
44                         }
45                 }
46                 strbuf_list_free(pair);
47         }
48
49         return (len < 0) ? len : 0;
50 }
51
52 void 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, NULL);
62 }
63
64 static 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
75 int 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         const char *argv[] = { cmd, NULL };
81
82         entry->cmd = cmd;
83         process = &entry->process;
84
85         child_process_init(process);
86         process->argv = argv;
87         process->use_shell = 1;
88         process->in = -1;
89         process->out = -1;
90         process->clean_on_exit = 1;
91         process->clean_on_exit_handler = subprocess_exit_handler;
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, 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);
109         return 0;
110 }