diff --git a/modules/util-descriptors.js b/modules/util-descriptors.js index a77e72b..ddf3174 100644 --- a/modules/util-descriptors.js +++ b/modules/util-descriptors.js @@ -16,18 +16,28 @@ limitations under the License. const SYNCHRONIZE = 0x00100000; +// +// util-descriptors is a helper module that will enable enumeration of all open descriptors, as well as a means to close them +// + function invalid() { throw ('Not supported on ' + process.platform); } +// +// Returns an array containing all the open descriptors for the current process +// function getOpenDescriptors() { switch (process.platform) { case "freebsd": + // + // BSD will use the system utility procstat to fetch the list of descriptors + // var child = require('child_process').execFile('/bin/sh', ['sh']); child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); }); child.stderr.on('data', function (c) { }); @@ -61,6 +71,9 @@ function getOpenDescriptors() } break; case "linux": + // + // Linux we will just rely on procfs to find the descriptors for our PID + // var child = require('child_process').execFile('/bin/sh', ['sh']); child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); }); child.stderr.on('data', function (c) { }); @@ -92,6 +105,9 @@ function getOpenDescriptors() return ([]); } } +// +// This function will enumerate the specified array of descriptors, and close each one +// function closeDescriptors(fdArray) { var fd = null; @@ -102,10 +118,14 @@ function closeDescriptors(fdArray) fd = fdArray.pop(); if (fd > 2) { - this.libc.close(fd); + this.libc.close(fd); // use glibc to close the descriptor } } } + +// +// execv helper function, which will close all open descriptors immediately before invoking execv() +// function _execv(exePath, argarr) { if (this.libc == null) @@ -119,11 +139,16 @@ function _execv(exePath, argarr) var args = require('_GenericMarshal').CreateVariable((1 + argarr.length) * require('_GenericMarshal').PointerSize); for (i = 0; i < argarr.length; ++i) { + // convert the JS array into a native array to be passed to execv var arg = require('_GenericMarshal').CreateVariable(argarr[i]); tmp.push(arg); arg.pointerBuffer().copy(args.toBuffer(), i * require('_GenericMarshal').PointerSize); } + // + // Fetch the list of all open descriptors, then close all of them. We need to do this, becuase + // execv() is going to inherit all the descriptors, so they will probably leak if the new process doesn't know what to do with them + // var fds = this.getOpenDescriptors(); this.closeDescriptors(fds); @@ -131,15 +156,22 @@ function _execv(exePath, argarr) throw('exec error'); } +// +// This function returns the native marshaler for glibc, specifically for 'execv' and 'close' +// function getLibc() { - var libs = require('monitor-info').getLibInfo('libc'); + var libs = require('monitor-info').getLibInfo('libc'); // This will fetch the location of all the libc modules on the platform. var libc = null; while (libs.length > 0) { try { + // + // We need to enumerate each libc module, and try to load it, becuase it is common for a linux distribution + // to include modules for several different architectures. So only the correct one will load. We need to find it. + // libc = require('_GenericMarshal').CreateNativeProxy(libs.pop().path); libc.CreateMethod('execv'); libc.CreateMethod('close'); @@ -155,16 +187,24 @@ function getLibc() return (libc); } +// +// Windows helper function to fetch a HANDLE to the specified process +// function win_getProcessHandle(pid) { try { if(!this.kernel32) { + // + // Reference to OpenProcess() can be found at: + // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess + // this.kernel32 = require('_GenericMarshal').CreateNativeProxy('kernel32.dll'); - this.kernel32.CreateMethod('OpenProcess'); + this.kernel32.CreateMethod('OpenProcess'); } + // This will return a HANDLE to the specified prcoess return (this.kernel32.OpenProcess(SYNCHRONIZE, 0, pid)); } catch(e) @@ -177,9 +217,11 @@ switch (process.platform) { case 'linux': case 'freebsd': + // Only Linux and FreeBSD support finding the list of open descriptors module.exports = { getOpenDescriptors: getOpenDescriptors, closeDescriptors: closeDescriptors, _execv: _execv, libc: getLibc() }; break; default: + // For other platforms, we will return an error module.exports = { getOpenDescriptors: invalid, closeDescriptors: invalid }; break; } diff --git a/modules/util-language.js b/modules/util-language.js index 912dd71..c7c4254 100644 --- a/modules/util-language.js +++ b/modules/util-language.js @@ -14,9 +14,20 @@ See the License for the specific language governing permissions and limitations under the License. */ +// +// util-language is a helper module to fetch the currently configured Language Locale of the Operating System +// +// +// This is a windows helper function to convert LCID to language name +// function toLang(val) { + // + // Windows Language codes can be found at: + // https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/6c085406-a698-4e12-9d4d-c3b0ee3dbc4a + // + var ret; switch (val) { @@ -959,10 +970,15 @@ function toLang(val) } return (ret); } + +// +// Try to determine the current language locale +// function getCurrent() { if(process.platform == 'win32') { + // On windows wi will use WMI via WMIC to get the LCID. var child = require('child_process').execFile(process.env['windir'] + '\\system32\\wbem\\wmic.exe', ['wmic', 'os', 'get', 'oslanguage','/FORMAT:LIST']); child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); }); child.stderr.str = ''; child.stderr.on('data', function (c) { this.str += c.toString(); }); @@ -975,6 +991,7 @@ function getCurrent() tokens = lines[i].split('='); if(tokens[0]=='OSLanguage') { + // Convert LCID to language string return (toLang(tokens[1])); } } @@ -983,12 +1000,14 @@ function getCurrent() if(process.env['LANG']) { + // If 'LANG" is defined in the environment variable, we can just return that return (process.env['LANG'].split('.')[0]); } else { if (process.platform == 'darwin') { + // On macOS we can use the system utility 'osascript' to fetch the current locale of the system var child = require('child_process').execFile('/bin/sh', ['sh']); child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); }); child.stderr.str = ''; child.stderr.on('data', function (c) { this.str += c.toString(); }); @@ -1000,6 +1019,7 @@ function getCurrent() { try { + // On Linux/BSD, we are goign to fetch the environment variables of the Display Manager process, and see what locale was set for it var uid = require('user-sessions').gdmUid; var child = require('child_process').execFile('/bin/sh', ['sh']); child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); }); @@ -1019,7 +1039,7 @@ function getCurrent() } } - +// This property will fetch the current locale the first time, and cache the results var obj = {}; Object.defineProperty(obj, 'current', { get: function () @@ -1039,6 +1059,9 @@ module.exports = obj; if (process.platform == 'win32') { + // + // On Windows, we will set a property to fetch/cache the wmicXslPath + // Object.defineProperty(module.exports, 'wmicXslPath', { get: function () diff --git a/modules/util-service-check.js b/modules/util-service-check.js index 1c9ae71..d39bb6e 100644 --- a/modules/util-service-check.js +++ b/modules/util-service-check.js @@ -15,21 +15,40 @@ limitations under the License. */ +// +// This is a windows helper that will try to determine the service name for the currently running service +// + +// +// Will return the name of the currently running service if it can be determined, null otherwise +// function win_serviceCheck() { var s; var reg = require('win-registry'); var path; var values = reg.QueryKey(reg.HKEY.LocalMachine, 'SOFTWARE\\Open Source'); + + // + // The MeshAgent will normally add a registry entry into the above registry path, at installation time + // + if (values.subkeys) { for (var i in values.subkeys) { try { + // + // We are enumerating all the Mesh Agents listed in the registry above, and check with the + // windows service manager to see if the PID matches the PID of the current process + // s = require('service-manager').manager.getService(values.subkeys[i]); if(s.isMe()) { + // + // This service is us, so we can return the results + // s.close(); return (values.subkeys[i]); } @@ -48,10 +67,16 @@ function win_serviceCheck() values = reg.QueryKey(reg.HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Services'); if (values.subkeys) { + // + // We couldn't find a match in the registry where the Mesh Agent normally saves information about installation, + // so we're going to just enumerate all the windows services, and try to manually brute force it + // + for(var i in values.subkeys) { try { + // We're going to look at the exe path for each enumerated service path = reg.QueryKey(reg.HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Services\\' + values.subkeys[i], 'ImagePath'); } catch(xx) @@ -65,13 +90,16 @@ function win_serviceCheck() if (path.startsWith('"')) { path = path.substring(1); } if(path == process.execPath) { + // + // If the service's exe path matches the exe path of the current process, we'll check the PID to see if it is indeed us + // try { s = require('service-manager').manager.getService(values.subkeys[i]); if(s.isMe()) { s.close(); - return (values.subkeys[i]); + return (values.subkeys[i]); // It is a match! } s.close(); } @@ -82,7 +110,7 @@ function win_serviceCheck() } } } - return (null); + return (null); // We couldn't find the right service } switch(process.platform)