socat.js 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. // Simple interface for making a socat process and interacting with it.
  2. // Assumes access to the `socat` command as a child process; if it's not
  3. // present, it will fall back to just writing to the specified file.
  4. import {spawn} from 'node:child_process'
  5. import {writeFile} from 'node:fs/promises'
  6. import EventEmitter from 'node:events'
  7. import path from 'node:path'
  8. import {killProcess, commandExists} from './general-util.js'
  9. export default class Socat extends EventEmitter {
  10. constructor(path) {
  11. super()
  12. this.setPath(path)
  13. }
  14. setPath(path) {
  15. this.stop()
  16. this.path = path
  17. }
  18. async start() {
  19. this.stop()
  20. if (await commandExists('socat')) {
  21. this.subprocess = spawn('socat', ['-', this.path])
  22. this.subprocess.stdout.on('data', data => this.emit('data', data))
  23. this.subprocess.on('close', () => {
  24. this.subprocess = null
  25. })
  26. this.subprocess.stdin.on('error', () => {
  27. this.stop()
  28. })
  29. }
  30. }
  31. async stop() {
  32. const proc = this.subprocess
  33. if (proc) {
  34. this.subprocess = null
  35. await killProcess(proc)
  36. }
  37. }
  38. async dispose() {
  39. // Don't accept any more messages.
  40. this.disposed = true
  41. await this.stop()
  42. }
  43. async send(message) {
  44. if (this.disposed) {
  45. return
  46. }
  47. if (!this.subprocess) {
  48. await this.start()
  49. }
  50. if (this.subprocess) {
  51. try {
  52. this.subprocess.stdin.write(message + '\r\n')
  53. } catch (error) {
  54. // There's no guarantee we'll actually suceed to write - the process
  55. // or pipe we're writing to could have closed unexpectedly. If that
  56. // happens, unestablish the socat process; it'll try to reconnect if
  57. // we send another message.
  58. this.stop()
  59. }
  60. } else {
  61. try {
  62. await writeFile(path.resolve(process.cwd(), this.path), message + '\r\n')
  63. } catch (error) {
  64. // :shrug: We tried!
  65. // -- It's possible to get here if the specified path isn't an actual
  66. // device, which is the case on Linux. Writing to that file (hopefully)
  67. // works on Windows though, which is the case we're trying to support
  68. // here. (On Linux you should have socat installed.)
  69. }
  70. }
  71. }
  72. }