singleInstance.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import * as electron from "electron"
  2. import * as net from "net"
  3. import * as path from "path"
  4. import * as fs from "fs"
  5. import * as os from "os"
  6. import * as buildinfo from "./buildInfo"
  7. function deleteSocketFile(socketPath) {
  8. if (process.platform === 'win32') {
  9. return;
  10. }
  11. if (fs.existsSync(socketPath)) {
  12. try {
  13. fs.unlinkSync(socketPath);
  14. } catch (error) {
  15. // Ignore ENOENT errors in case the file was deleted between the exists
  16. // check and the call to unlink sync. This occurred occasionally on CI
  17. // which is why this check is here.
  18. if (error.code !== 'ENOENT') {
  19. throw error;
  20. }
  21. }
  22. }
  23. }
  24. /**
  25. * Creates server to listen for additional atom application launches.
  26. *
  27. * You can run the command multiple times, but after the first launch
  28. * the other launches will just pass their information to this server and then
  29. * close immediately.
  30. */
  31. function listenForArgumentsFromNewProcess(socketPath, callback) {
  32. deleteSocketFile(socketPath);
  33. const server = net.createServer(connection => {
  34. connection.on('data', data => {
  35. const args = JSON.parse(data.toString());
  36. callback(args);
  37. });
  38. });
  39. server.listen(socketPath);
  40. server.on('error', error => console.error('Application server failed', error));
  41. return server;
  42. }
  43. function tryStart(socketPath, callback, otherAppFound) {
  44. // FIXME: Sometimes when socketPath doesn't exist, net.connect would strangely
  45. // take a few seconds to trigger 'error' event, it could be a bug of node
  46. // or atom-shell, before it's fixed we check the existence of socketPath to
  47. // speedup startup.
  48. if (process.platform !== 'win32' && !fs.existsSync(socketPath)) {
  49. callback();
  50. return;
  51. }
  52. const client = net.connect({ path: socketPath }, () => {
  53. client.write(JSON.stringify(process.argv.slice(1)), () => {
  54. client.end();
  55. otherAppFound();
  56. });
  57. });
  58. client.on('error', callback);
  59. }
  60. function makeSocketPath() {
  61. let name = electron.app.name ? electron.app.name : electron.app.getName();
  62. if (buildinfo.releaseChannel !== 'stable') {
  63. name += buildinfo.releaseChannel;
  64. }
  65. if (process.platform === 'win32') {
  66. return '\\\\.\\pipe\\' + name + '-sock';
  67. } else {
  68. return path.join(os.tmpdir(), name + '.sock');
  69. }
  70. }
  71. export function create(startCallback, newProcessCallback) {
  72. const socketPath = makeSocketPath();
  73. tryStart(socketPath, () => {
  74. const server = listenForArgumentsFromNewProcess(socketPath, newProcessCallback);
  75. electron.app.on('will-quit', () => {
  76. server.close();
  77. deleteSocketFile(socketPath);
  78. });
  79. //@ts-ignore
  80. electron.app.on('will-exit', () => {
  81. server.close();
  82. deleteSocketFile(socketPath);
  83. });
  84. startCallback();
  85. }, () => {
  86. console.log('Another instance exists. Quitting.');
  87. electron.app.exit(0);
  88. });
  89. }
  90. export function pipeCommandLineArgs(noOtherAppFoundCallback, otherAppFound) {
  91. tryStart(makeSocketPath(), noOtherAppFoundCallback, otherAppFound);
  92. }