UNPKG

wmic

Version:

Wrapper around the WMIC Windows command interface.

282 lines (225 loc) 6.56 kB
"use strict"; /* wmic calls must always be serialised in windows, hence the use of async.queue */ var MAX_WORKER_COUNT = 100; var execFile = require('child_process').execFile, exec = require('child_process').exec, async = require('async'), fs = require('fs'), iconv = require('iconv-lite'); /** * Need to split a command line string taking into account strings - that is, don't * split spaces within a string. So that 'P1 P2 "Other Param" P4' is split into 4 param strings * with param 3 = "Other Param" (not including quotes). **/ function splitter(cmd) { cmd = cmd.trim(); var acc = [], inString = false, cur = "", l = cmd.length; for (var i = 0 ; i < l ; i++ ){ var ch = cmd.charAt(i); switch(ch) { case '"': inString = !inString; if (!inString) { if (cur.length > 0) { acc.push(cur); cur = ""; } } break; case ' ': if (inString) { cur += ' '; } else { if (cur.length > 0) { acc.push(cur); cur = ""; } } break; default: cur += ch; break; } } if (cur.length > 0) acc.push(cur); return acc; }; function parse_list(data){ var list = []; var blocks = data.split(/\n\n|\n,?\r/g).filter(function(block) { return block.length > 2; }); blocks.forEach(function(block) { var obj = {}; var lines = block.split(/\n+|\r+/).filter(function(line) { return line.indexOf('=') !== -1 }); lines.forEach(function(line) { var kv = line.replace(/^,/, '').split("="); obj[kv[0]] = kv[1]; }) if (Object.keys(obj).length > 0) list.push(obj); }) return list; } function parse_values(out){ var arr = [], data = buildDataArray(out), keys = data[0]; data.forEach(function(k, i){ if(k != keys){ var obj = {}; k.forEach(function(l, j){ obj[keys[j]] = l }) arr.push(obj); } }); return arr; } function buildDataArray(rawInput){ var lines = rawInput.toString().trim().split('\n'), data = [], keys = [], linePattern = /(\S*?\s\s+)/g, match; while ((match = linePattern.exec(lines[0])) !== null) { if (match.index === linePattern.lastIndex) { linePattern.lastIndex++; } var key = {}; key.string = match[0].trim(); key.startPoint = lines[0].indexOf(key.string); key.keyLength = match[0].length; keys.push(key); } lines.forEach(function(line, index){ var lineData = []; keys.forEach(function(key, jndex){ lineData.push(line.substr(key.startPoint, key.keyLength).trim()); }) data.push(lineData); }) return data; } function get_encoding(stdout) { var codePage = stdout.replace(/\n.*$/s, '').replace(/\D/g, ''); return codePage; } exports.get_encoding = get_encoding; /** * Run the wmic command provided. * * The resulting output string has an additional pid property added so, one may get the process * details. This seems the easiest way of doing so given the run is in a queue. **/ var run = exports.run = function run(cmd, cb) { queue.push(cmd, cb); }; // The encoding is cached in this variable, so the CHCP command is executed only once. var consoleEncoding; var queue = async.queue(function(cmd, cb) { var opts = { env: process.env, cwd: process.env.TEMP }; if (opts.env.PATH.indexOf('system32') === -1) { opts.env.PATH += ';' + process.env.WINDIR + "\\system32"; opts.env.PATH += ';' + process.env.WINDIR + "\\system32\\wbem"; } var pid; async.parallel([ function(cb) { if (consoleEncoding) { cb(null, consoleEncoding); } else { exec('chcp', function(err, stdout) { if (err) { cb(err); return; } var codePage = get_encoding(stdout); consoleEncoding = codePage && codePage !== '65001' ? 'cp' + codePage : 'utf8'; cb(null, consoleEncoding); }); } }, function(cb) { var wm = execFile('wmic', splitter(cmd), opts), stdout = [], stderr = []; pid = wm.pid; wm.on('error', function(e) { if (e.code == 'ENOENT') e.message = 'Unable to find wmic command in path.'; cb(e); }) wm.stdout.on('data', function(d) { // console.log('Got out: ' + d.toString()) stdout.push(d); }); wm.stderr.on('data', function(e) { // console.log('Got error: ' + e.toString()) stderr.push(e); }); wm.on('exit', function(code) { // remove weird temp file generated by wmic fs.unlink('TempWmicBatchFile.bat', function() { /* noop */ }); setImmediate(function() { cb(null, [stdout, stderr]); }) }); wm.stdin.end(); } ], function(err, results) { if (!err) { var encoding = results[0]; var stdoutStr = stringifyBufferArray(results[1][0], encoding); var stderrStr = stringifyBufferArray(results[1][1], encoding); if (stderrStr) { err = new Error(stderrStr); } cb(err, stdoutStr, pid); } else { cb(err, '', pid); } } ); function stringifyBufferArray(array, encoding) { return array.map(function(buffer) { return iconv.decode(buffer, encoding); }).join(',').replace(/^,/, '').replace(/,\s+$/, '').trim(); } }, MAX_WORKER_COUNT); exports.get_value = function(section, value, condition, cb){ var cond = condition ? ' where "' + condition + '" ' : ''; var cmd = section + cond + ' get ' + value + ' /value'; run(cmd, function(err, out){ if (err) return cb(err); var str = out.match(/=(.*)/); if (str) cb(null, str[1].trim()); else cb(new Error("Wmic: Couldn't get " + value + " in " + section)); }) } exports.get_values = function(section, keys, condition, cb){ var cond = condition ? ' where "' + condition + '" ' : ''; var cmd = section + cond + ' get ' + keys; run(cmd, function(err, out){ if (err) return cb(err); cb(null, parse_values(out)); }); }; /** * Calls back an array of objects for the given command. * * This only works for alias commands with a LIST clause. **/ exports.get_list = function(cmd, callback) { run(cmd + ' list full', function(err, data) { if (err) return callback(err); callback(null, parse_list(data)); }); };