選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

index.js 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. var spawn = require("child_process").spawn;
  2. var async = require("../../js/async");
  3. exports.start = start;
  4. exports.stop = stop;
  5. exports.event = event;
  6. var conf;
  7. var logger;
  8. var modules;
  9. class Process {
  10. constructor(id, cmd, options) {
  11. this.id = id;
  12. this.restart = options.restart;
  13. this.stopping = false;
  14. this.running = false;
  15. this.restarts = 0;
  16. this.name = cmd[0];
  17. cmd.shift();
  18. this.args = cmd;
  19. this.childOpts = {
  20. env: options.env,
  21. cwd: options.cwd,
  22. };
  23. this.info = logger.info.bind(logger, this.id+":");
  24. this.warn = logger.warn.bind(logger, this.id+":");
  25. }
  26. logOutput(stream, data) {
  27. data.toString()
  28. .split("\n")
  29. .map(line => line.trim())
  30. .forEach(line => {
  31. if (line === "") return;
  32. this.info(stream+":", line);
  33. });
  34. }
  35. onexit() {
  36. if (!this.restart)
  37. return;
  38. if (this.restarts < 2) {
  39. this.restarts += 1;
  40. var restarts = this.restarts;
  41. this.info("Restarting in 2 seconds.");
  42. setTimeout(() => {
  43. this.start();
  44. this.restarts = restarts;
  45. }, 2000);
  46. } else {
  47. this.warn("Not restarting anymore after 2 restarts.");
  48. }
  49. }
  50. start() {
  51. this.stopping = false;
  52. this.running = true;
  53. this.restarts = 0;
  54. this.child = spawn(this.name, this.args, this.childOpts);
  55. this.child.stdout.on("data",
  56. d => this.logOutput("stdout", d));
  57. this.child.stderr.on("data",
  58. d => this.logOutput("stderr", d));
  59. this.child.once("error", err => {
  60. if (!this.stopping)
  61. this.warn("Failed to start:", err);
  62. this.onexit();
  63. });
  64. this.child.once("close", code => {
  65. this.running = false;
  66. if (this.stopping)
  67. return;
  68. if (code === 0)
  69. this.info("Exited with status code 0");
  70. else
  71. this.warn("Exited with status code", code);
  72. this.onexit();
  73. });
  74. }
  75. stop(cb) {
  76. this.stopping = true;
  77. if (!this.running)
  78. return cb();
  79. this.info("Sending SIGTERM.");
  80. this.child.kill("SIGTERM");
  81. setTimeout(() => {
  82. if (this.running) {
  83. this.info("Sending SIGKILL.");
  84. this.child.kill("SIGKILL");
  85. }
  86. this.stopping = false;
  87. cb();
  88. }, 1000);
  89. }
  90. }
  91. class ProcessGroup {
  92. constructor(id, cmds, options) {
  93. this.procs = [];
  94. cmds.forEach(cmd => {
  95. var name = cmd[0];
  96. this.procs.push(new Process(id+"("+name+")", cmd, options));
  97. });
  98. }
  99. start() {
  100. this.procs.forEach(p => p.start());
  101. }
  102. stop(cb) {
  103. this.stopping = true;
  104. var next = async(this.procs.length, () => {
  105. this.stopping = false;
  106. cb();
  107. });
  108. this.procs.forEach(p => p.stop(next));
  109. }
  110. }
  111. var procs;
  112. function start(conf_, logger_, modules_) {
  113. conf = conf_ || conf;
  114. logger = logger_ || logger;
  115. modules = modules_ || modules;
  116. procs = {};
  117. if (conf == null) {
  118. logger.info("No processes.");
  119. return;
  120. }
  121. conf.forEach(proc => {
  122. if (procs[proc.name])
  123. return logger.warn("Igonring duplicate process: "+proc.name);
  124. var env = null;
  125. if (proc.env) {
  126. env = {};
  127. for (var i in process.env) {
  128. env[i] = process.env[i];
  129. }
  130. for (var i in proc.env) {
  131. env[i] = proc.env[i];
  132. }
  133. }
  134. var opts = {
  135. cwd: proc.in,
  136. env: env,
  137. restart: !!proc.restart,
  138. };
  139. var p;
  140. if (!proc.as || proc.as === "process") {
  141. p = new Process(proc.name, proc.run, opts);
  142. } else if (proc.as === "group") {
  143. p = new ProcessGroup(proc.name, proc.run, opts);
  144. } else {
  145. return logger.warn(
  146. proc.name+":",
  147. "Invalid 'as' attribute:",
  148. proc.as);
  149. }
  150. procs[proc.name] = p;
  151. if (proc.delay != null) {
  152. setTimeout(() => {
  153. if (!p.stopping)
  154. p.start();
  155. }, proc.delay);
  156. } else {
  157. p.start();
  158. }
  159. });
  160. }
  161. function stop(cb) {
  162. var keys = Object.keys(procs);
  163. var next = async(keys.length, cb);
  164. keys.forEach(i => procs[i].stop(next));
  165. }
  166. function event(name, ...params) {
  167. switch (name) {
  168. default:
  169. logger.warn("Unknown event: "+name);
  170. }
  171. }