2121// Import required modules
2222const CONFIGURATION = require ( '../etc/configuration.json' ) ;
2323const { spawn } = require ( 'child_process' ) ;
24- const { Address4 } = require ( 'ip-address' ) ;
24+ const { Address4, Address6 } = require ( 'ip-address' ) ;
2525const socketio = require ( 'socket.io' ) ;
2626const os = require ( 'os' ) ;
2727//=============================================================================
@@ -49,7 +49,25 @@ function startNelsonProcess() {
4949 let io = [ ] ; // Array to store socket connections
5050 let nelsonApp ; // Child process running Nelson application
5151 let commandio ; // Socket.IO instance for command communication
52+ let hasCleaned = false ;
5253
54+ function cleanup ( terminateNelson = false ) {
55+ if ( hasCleaned ) {
56+ return ;
57+ }
58+ hasCleaned = true ;
59+ if ( terminateNelson ) {
60+ nelsonApp ?. kill ( ) ;
61+ }
62+ commandio ?. close ?. ( ) ;
63+ commandio = undefined ;
64+ nelsonApp = undefined ;
65+ }
66+
67+ process . once ( 'SIGINT' , ( ) => cleanup ( true ) ) ;
68+ process . once ( 'SIGTERM' , ( ) => cleanup ( true ) ) ;
69+ process . once ( 'disconnect' , ( ) => cleanup ( true ) ) ;
70+ process . once ( 'exit' , ( ) => cleanup ( ) ) ;
5371 // Listen for messages from the parent process
5472 process . on ( 'message' , ( msg ) => {
5573 switch ( msg . msgtype ) {
@@ -77,10 +95,11 @@ function startNelsonProcess() {
7795 case 'stop' :
7896 // Stop the application
7997 commandio ?. emit ( 'stop' ) ;
98+ cleanup ( true ) ;
8099 break ;
81100 case 'quit' :
82101 // Terminate Nelson application
83- nelsonApp ?. kill ( ) ;
102+ cleanup ( true ) ;
84103 break ;
85104 default :
86105 // Log unknown message types
@@ -93,6 +112,7 @@ function startNelsonProcess() {
93112 * @param {number } port - Port number for socket connection
94113 */
95114 function handleInitialization ( port ) {
115+ hasCleaned = false ;
96116 // Set up socket connection
97117 const { io : socketIOInstance , address : commandAddress } =
98118 initializeSocket ( port ) ;
@@ -106,6 +126,13 @@ function startNelsonProcess() {
106126 // Determine and spawn Nelson application
107127 const { app, parameters } = getAppAndParameters ( commandAddress ) ;
108128 nelsonApp = spawn ( app , parameters ) ;
129+ nelsonApp . on ( 'exit' , ( code , signal ) => {
130+ process . send ?. ( { msgtype : 'process_exit' , code, signal } ) ;
131+ cleanup ( ) ;
132+ } ) ;
133+ nelsonApp . on ( 'error' , ( error ) => {
134+ process . send ?. ( { msgtype : 'process_error' , error : error . message } ) ;
135+ } ) ;
109136
110137 // Handle socket disconnection
111138 commandio . on ( 'disconnect' , ( ) => {
@@ -120,24 +147,15 @@ function startNelsonProcess() {
120147 */
121148 function initializeSocket ( port ) {
122149 // Create Socket.IO instance
123- const commandio = socketio ( port ) ;
124-
125- // Find first external IPv4 network interface
126- const networkInterfaces = os . networkInterfaces ( ) ;
127- const ipv4 = Object . values ( networkInterfaces )
128- . flat ( )
129- . find ( ( details ) => details . family === 'IPv4' && ! details . internal ) ;
130-
131- // Throw error if no external IPv4 address found
132- if ( ! ipv4 ) {
133- throw new Error ( 'No external IPv4 address found.' ) ;
134- }
135-
136- // Create address object
137- const addr = new Address4 ( ipv4 . address ) ;
150+ const ioInstance = socketio ( port ) ;
151+ const addressInfo = resolveNetworkAddress ( ) ;
152+ const addressString =
153+ addressInfo . family === 'IPv6'
154+ ? `http://[${ addressInfo . address . correctForm ( ) } ]:${ port } `
155+ : `http://${ addressInfo . address . address } :${ port } ` ;
138156 return {
139- io : commandio ,
140- address : `http:// ${ addr . address } : ${ port } ` ,
157+ io : ioInstance ,
158+ address : addressString ,
141159 } ;
142160 }
143161 //=============================================================================
@@ -153,15 +171,45 @@ function startNelsonProcess() {
153171 : CONFIGURATION . NELSON_APPLICATION ;
154172
155173 // Get corresponding parameters
156- const parameters = CONFIGURATION . USE_DOCKER
174+ const baseParameters = CONFIGURATION . USE_DOCKER
157175 ? CONFIGURATION . DOCKER_PARAMETERS
158176 : CONFIGURATION . NELSON_APPLICATION_PARAMETERS ;
159177
160- // Append command address to parameters
161- parameters . push ( commandAddress ) ;
178+ const parameters = [ ...( baseParameters ?? [ ] ) , commandAddress ] ;
162179 return { app, parameters } ;
163180 }
164181 //=============================================================================
182+ /**
183+ * Resolve network address for the socket connection
184+ * Favors IPv4 over IPv6, and external over internal addresses
185+ * @returns {Object } Resolved address and family (IPv4/IPv6)
186+ */
187+ function resolveNetworkAddress ( ) {
188+ const interfaces = Object . values ( os . networkInterfaces ( ) )
189+ . flat ( )
190+ . filter ( Boolean ) ;
191+ const candidate =
192+ interfaces . find ( ( details ) => details . family === 'IPv4' && ! details . internal ) ??
193+ interfaces . find ( ( details ) => details . family === 'IPv6' && ! details . internal ) ??
194+ interfaces . find ( ( details ) => details . family === 'IPv4' ) ??
195+ interfaces . find ( ( details ) => details . family === 'IPv6' ) ;
196+
197+ if ( ! candidate || typeof candidate . address !== 'string' ) {
198+ throw new Error ( 'No network address available.' ) ;
199+ }
200+
201+ const addressValue = candidate . address . includes ( '%' )
202+ ? candidate . address . split ( '%' ) [ 0 ]
203+ : candidate . address ;
204+
205+ const address =
206+ candidate . family === 'IPv6'
207+ ? new Address6 ( addressValue )
208+ : new Address4 ( addressValue ) ;
209+
210+ return { address, family : candidate . family } ;
211+ }
212+ //=============================================================================
165213}
166214//=============================================================================
167215/**
0 commit comments