THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Git Source Code Mirror - This is a publish-only repository and all pull requests are ignored. Please follow Documentation/SubmittingPatches procedure for any of your improvements.
git
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

217 lines
5.5KB

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