2013-10-25 19:22:45 +02:00
2014-06-29 00:35:33 +02:00
module . exports = function ( RED ) {
"use strict" ;
2020-04-03 17:07:41 +02:00
var spawn = require ( "child_process" ) . spawn ;
var plat = require ( "os" ) . platform ( ) ;
2020-11-20 19:07:35 +01:00
function doPing ( node , host , arrayMode ) {
2020-04-03 17:07:41 +02:00
const defTimeout = 5000 ;
2021-03-12 13:54:13 +01:00
var ex , ex6 , hostOptions , commandLineOptions ;
2020-07-01 12:16:20 +02:00
if ( typeof host === "string" ) {
2020-04-03 17:07:41 +02:00
hostOptions = {
host : host ,
timeout : defTimeout
}
} else {
hostOptions = host ;
hostOptions . timeout = isNaN ( parseInt ( hostOptions . timeout ) ) ? defTimeout : parseInt ( hostOptions . timeout ) ;
}
//clamp timeout between 1 and 30 sec
hostOptions . timeout = hostOptions . timeout < 1000 ? 1000 : hostOptions . timeout ;
hostOptions . timeout = hostOptions . timeout > 30000 ? 30000 : hostOptions . timeout ;
var timeoutS = Math . round ( hostOptions . timeout / 1000 ) ; //whole numbers only
var msg = { payload : false , topic : hostOptions . host } ;
//only include the extra msg object if operating in advance/array mode.
2020-07-01 12:16:20 +02:00
if ( arrayMode ) {
2020-04-03 17:07:41 +02:00
msg . ping = hostOptions
}
2021-03-12 13:54:13 +01:00
//User Selected Protocol
2020-11-20 19:07:35 +01:00
if ( plat == "linux" || plat == "android" ) {
2021-03-12 13:54:13 +01:00
if ( node . protocol === "IPv4" ) {
commandLineOptions = [ "-n" , "-4" , "-w" , timeoutS , "-c" , "1" ] ; //IPv4
} else if ( node . protocol === "IPv6" ) {
commandLineOptions = [ "-n" , "-6" , "-w" , timeoutS , "-c" , "1" ] ; //IPv6
} else {
commandLineOptions = [ "-n" , "-w" , timeoutS , "-c" , "1" ] ; //Automatic
}
2020-11-20 19:07:35 +01:00
} else if ( plat . match ( /^win/ ) ) {
2021-03-12 13:54:13 +01:00
if ( node . protocol === "IPv4" ) {
commandLineOptions = [ "-n" , "1" , "-4" , "-w" , hostOptions . timeout ] ; //IPv4
} else if ( node . protocol === "IPv6" ) {
commandLineOptions = [ "-n" , "1" , "-6" , "-w" , hostOptions . timeout ] ; //IPv6
} else {
commandLineOptions = [ "-n" , "1" , "-w" , hostOptions . timeout ] ; //Automatic
}
2020-11-20 19:07:35 +01:00
} else if ( plat == "darwin" || plat == "freebsd" ) {
2021-03-12 13:54:13 +01:00
if ( node . protocol === "IPv4" ) {
commandLineOptions = [ "-n" , "-4" , "-t" , timeoutS , "-c" , "1" ] ; //IPv4
} else if ( node . protocol === "IPv6" ) {
commandLineOptions = [ "-n" , "-6" , "-t" , timeoutS , "-c" , "1" ] ; //IPv6
} else {
commandLineOptions = [ "-n" , "-t" , timeoutS , "-c" , "1" ] ; //Automatic
}
2020-11-20 19:07:35 +01:00
} else {
node . error ( "Sorry - your platform - " + plat + " - is not recognised." , msg ) ;
2020-04-03 17:07:41 +02:00
return ; //dont pass go - just return!
}
//spawn with timeout in case of os issue
2020-11-20 19:07:35 +01:00
ex = spawn ( "ping" , [ ... commandLineOptions , hostOptions . host ] ) ;
2020-04-03 17:07:41 +02:00
//monitor every spawned process & SIGINT if too long
var spawnTout = setTimeout ( ( ) => {
node . log ( ` ping - Host ' ${ hostOptions . host } ' process timeout - sending SIGINT ` )
2020-07-01 12:16:20 +02:00
try {
2020-11-20 19:07:35 +01:00
if ( ex && ex . pid ) { ex . kill ( "SIGINT" ) ; }
}
catch ( e ) { console . warn ( e ) ; }
2020-04-03 17:07:41 +02:00
} , hostOptions . timeout + 1000 ) ; //add 1s for grace
var res = false ;
var line = "" ;
var fail = false ;
//var regex = /from.*time.(.*)ms/;
var regex = /=.*[<|=]([0-9]*).*TTL|ttl..*=([0-9\.]*)/ ;
2021-03-12 13:54:13 +01:00
var tryPing6 = false ;
//catch error msg from ping
ex . stderr . setEncoding ( 'utf8' ) ;
ex . stderr . on ( "data" , function ( data ) {
if ( ! data . includes ( 'Usage' ) ) { // !data: only get the error and not how to use the ping command
if ( data . includes ( 'invalid' ) && data . includes ( '6' ) ) { //if IPv6 not supported in version of ping try ping6
tryPing6 = true ;
//node.error(data, msg); // used for testing output of -6 on ping command. Keep this line untill out of beta testing please. Contact---> https://discourse.nodered.org/u/meeki007
} else if ( data . includes ( 'Network is unreachable' ) ) {
node . error ( data + " Please check that your service provider or network device has IPv6 enabled" , msg ) ;
} else {
node . error ( data , msg ) ;
}
}
} ) ;
2020-11-20 19:07:35 +01:00
ex . stdout . on ( "data" , function ( data ) {
line += data . toString ( ) ;
} ) ;
2020-04-03 17:07:41 +02:00
ex . on ( "exit" , function ( err ) {
clearTimeout ( spawnTout ) ;
} ) ;
ex . on ( "error" , function ( err ) {
fail = true ;
if ( err . code === "ENOENT" ) {
node . error ( err . code + " ping command not found" , msg ) ;
}
else if ( err . code === "EACCES" ) {
node . error ( err . code + " can't run ping command" , msg ) ;
}
else {
node . error ( err . code , msg ) ;
}
} ) ;
ex . on ( "close" , function ( code ) {
2021-03-12 13:54:13 +01:00
if ( tryPing6 === false ) {
if ( fail ) { fail = false ; return ; }
var m = regex . exec ( line ) || "" ;
if ( m !== "" ) {
if ( m [ 1 ] ) { res = Number ( m [ 1 ] ) ; }
if ( m [ 2 ] ) { res = Number ( m [ 2 ] ) ; }
}
if ( code === 0 ) { msg . payload = res }
try { node . send ( msg ) ; }
catch ( e ) { console . warn ( e ) }
} else {
//fallback to ping6 for OS's that have not updated/out of date
if ( plat == "linux" || plat == "android" ) {
commandLineOptions = [ "-n" , "-w" , timeoutS , "-c" , "1" ] ;
} else if ( plat == "darwin" || plat == "freebsd" ) {
commandLineOptions = [ "-n" , "-c" , "1" ] //NOTE: -X / timeout does not work on mac OSX and most freebsd systems
} else {
node . error ( "Sorry IPv6 on your platform - " + plat + " - is not supported." , msg ) ;
}
//spawn with timeout in case of os issue
ex6 = spawn ( "ping6" , [ ... commandLineOptions , hostOptions . host ] ) ;
//monitor every spawned process & SIGINT if too long
var spawnTout = setTimeout ( ( ) => {
node . log ( ` ping6 - Host ' ${ hostOptions . host } ' process timeout - sending SIGINT ` )
try {
if ( ex6 && ex6 . pid ) { ex6 . kill ( "SIGINT" ) ; }
}
catch ( e ) { console . warn ( e ) ; }
} , hostOptions . timeout + 1000 ) ; //add 1s for grace
//catch error msg from ping6
ex6 . stderr . setEncoding ( 'utf8' ) ;
ex6 . stderr . on ( "data" , function ( data ) {
if ( ! data . includes ( 'Usage' ) ) { // !data: only get the error and not how to use the ping6 command
if ( data . includes ( 'Network is unreachable' ) ) {
node . error ( data + " Please check that your service provider or network device has IPv6 enabled" , msg ) ;
} else {
node . error ( data , msg ) ;
}
}
} ) ;
ex6 . stdout . on ( "data" , function ( data ) {
line += data . toString ( ) ;
} ) ;
ex6 . on ( "exit" , function ( err ) {
clearTimeout ( spawnTout ) ;
} ) ;
ex6 . on ( "error" , function ( err ) {
fail = true ;
if ( err . code === "ENOENT" ) {
node . error ( err . code + " ping6 command not found" , msg ) ;
}
else if ( err . code === "EACCES" ) {
node . error ( err . code + " can't run ping6 command" , msg ) ;
}
else {
node . error ( err . code , msg ) ;
}
} ) ;
ex6 . on ( "close" , function ( code ) {
if ( fail ) { fail = false ; return ; }
var m = regex . exec ( line ) || "" ;
if ( m !== "" ) {
if ( m [ 1 ] ) { res = Number ( m [ 1 ] ) ; }
if ( m [ 2 ] ) { res = Number ( m [ 2 ] ) ; }
}
if ( code === 0 ) { msg . payload = res }
try { node . send ( msg ) ; }
catch ( e ) { console . warn ( e ) }
} ) ;
2020-04-03 17:07:41 +02:00
}
2021-03-12 13:54:13 +01:00
2020-04-03 17:07:41 +02:00
} ) ;
2021-03-12 13:54:13 +01:00
2020-04-03 17:07:41 +02:00
}
2013-10-04 09:24:09 +02:00
2014-06-29 00:35:33 +02:00
function PingNode ( n ) {
RED . nodes . createNode ( this , n ) ;
2021-03-12 13:54:13 +01:00
this . protocol = n . protocol || 'Automatic' ;
2020-04-03 17:07:41 +02:00
this . mode = n . mode ;
2014-06-29 00:35:33 +02:00
this . host = n . host ;
this . timer = n . timer * 1000 ;
var node = this ;
2013-10-04 09:24:09 +02:00
2020-04-03 17:07:41 +02:00
function generatePingList ( str ) {
return ( str + "" ) . split ( "," ) . map ( ( e ) => ( e + "" ) . trim ( ) ) . filter ( ( e ) => e != "" ) ;
}
2020-11-20 19:07:35 +01:00
function clearPingInterval ( ) {
2020-04-03 17:07:41 +02:00
if ( node . tout ) { clearInterval ( node . tout ) ; }
}
2020-07-01 12:16:20 +02:00
if ( node . mode === "triggered" ) {
2020-04-03 17:07:41 +02:00
clearPingInterval ( ) ;
2020-07-01 12:16:20 +02:00
} else if ( node . timer ) {
2020-04-03 17:07:41 +02:00
node . tout = setInterval ( function ( ) {
let pingables = generatePingList ( node . host ) ;
for ( let index = 0 ; index < pingables . length ; index ++ ) {
const element = pingables [ index ] ;
2020-07-01 12:16:20 +02:00
if ( element ) { doPing ( node , element , false ) ; }
2018-12-05 14:46:08 +01:00
}
2020-04-03 17:07:41 +02:00
} , node . timer ) ;
}
this . on ( "input" , function ( msg ) {
let node = this ;
let payload = node . host || msg . payload ;
2020-07-01 12:16:20 +02:00
if ( typeof payload == "string" ) {
2020-04-03 17:07:41 +02:00
let pingables = generatePingList ( payload )
for ( let index = 0 ; index < pingables . length ; index ++ ) {
const element = pingables [ index ] ;
2020-07-01 12:16:20 +02:00
if ( element ) { doPing ( node , element , false ) ; }
2018-12-05 14:46:08 +01:00
}
2020-04-03 17:07:41 +02:00
} else if ( Array . isArray ( payload ) ) {
for ( let index = 0 ; index < payload . length ; index ++ ) {
const element = payload [ index ] ;
2020-07-01 12:16:20 +02:00
if ( element ) { doPing ( node , element , true ) ; }
2016-02-13 12:15:02 +01:00
}
2020-04-03 17:07:41 +02:00
}
} ) ;
2013-10-04 09:24:09 +02:00
2014-06-29 00:35:33 +02:00
this . on ( "close" , function ( ) {
2020-04-03 17:07:41 +02:00
clearPingInterval ( ) ;
2014-06-29 00:35:33 +02:00
} ) ;
}
RED . nodes . registerType ( "ping" , PingNode ) ;
2021-03-12 13:54:13 +01:00
}