├── .gitignore ├── LICENSE ├── README.md ├── bin └── serial-number ├── index.js ├── package.json └── setup.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | npm-debug.log -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The ISC License 2 | 3 | Copyright (c) 2013-2015 Elan Shanker 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 15 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Serial Number 2 | ============= 3 | 4 | A simple Node.js module for accessing the serial number (a.k.a. Dell Service 5 | Tag, asset tag) of the local machine. Supports Linux, Mac (OS X), Windows, and 6 | FreeBSD. On Amazon EC2 instances, it will return the instance-id. 7 | 8 | 9 | Installation 10 | ------------ 11 | `npm install serial-number` 12 | 13 | And/or install globally for a `serial-number` shell command: 14 | `[sudo] npm install -g serial-number` 15 | 16 | 17 | Usage 18 | ----- 19 | The serial number value is retrieved from the system asynchronously and passed 20 | to a callback. 21 | 22 | ```javascript 23 | var serialNumber = require('serial-number'); 24 | 25 | serialNumber(function (err, value) { 26 | console.log(value); 27 | }); 28 | ``` 29 | 30 | If the serial number turns out to be invalid (common on VMs), the system's UUID 31 | value will be provided as a fallback. To instead try to get the UUID on the 32 | first attempt, set the `preferUUID` flag: 33 | 34 | ```javascript 35 | serialNumber.preferUUID = true; 36 | ``` 37 | 38 | To prefix the system command with `sudo` use the `useSudo` method: 39 | 40 | ```javascript 41 | serialNumber.useSudo(function (err, value) { 42 | console.log(value); 43 | }); 44 | ``` 45 | 46 | For the CLI command it's just 47 | 48 | ```sh 49 | $ serial-number 50 | A12B3C4DE5FG 51 | ``` 52 | 53 | The CLI provides a couple options: 54 | 55 | * `--uuid`: Equivalent to the `preferUUID` setting as above. 56 | * `--cmdprefix `: Sets a string to be prefixed ahead of the shell 57 | command to be run. Can be used to specify a path to the `dmidecode` binary on 58 | \*nix systems if it won't be found in the environment `$PATH` 59 | 60 | ```sh 61 | $ serial-number --uuid --cmdprefix "/usr/sbin/" 62 | 1234AABB-C5DE-678F-G9HI-J01K2LM34N5A 63 | ``` 64 | 65 | License 66 | ------- 67 | [ISC](https://raw.github.com/es128/serial-number/master/LICENSE) 68 | -------------------------------------------------------------------------------- /bin/serial-number: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | var path = require('path'); 6 | var fs = require('fs'); 7 | 8 | var serialNumber = require('../'); 9 | 10 | var cmdprefix = ''; 11 | process.argv.forEach(function(arg, index, arr) { 12 | if (arg === '--uuid') { 13 | serialNumber.preferUUID = true; 14 | } else if (arg === '--cmdprefix') { 15 | cmdprefix = arr[index + 1]; 16 | } 17 | }); 18 | 19 | serialNumber(function (err, val) { 20 | if (err) throw err; 21 | console.log(val); 22 | }, cmdprefix); 23 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var http = require('http'); 5 | var exec = require('child_process').exec; 6 | 7 | var serialNumber = function (cb, cmdPrefix) { 8 | var delimiter = ': '; 9 | var uselessSerials = [ 10 | 'To be filled by O.E.M.', 11 | ] 12 | 13 | var fromCache = function (error, stdout) { 14 | fs.readFile(__dirname + '/cache', function (fsErr, data) { 15 | if (data) {data = data.toString().trim();} 16 | if (fsErr || !data || data.length < 2) { 17 | attemptEC2(function() { 18 | stdoutHandler(error, stdout, true); 19 | }); 20 | } else { 21 | cb(null, data); 22 | } 23 | }); 24 | }; 25 | 26 | var stdoutHandler = function (error, stdout, bypassCache) { 27 | if (error && !bypassCache) { 28 | fromCache(error, stdout); 29 | } else { 30 | cb(error, parseResult(stdout)); 31 | } 32 | }; 33 | 34 | var parseResult = function (input) { 35 | var result = input.slice(input.indexOf(delimiter) + 2).trim(); 36 | 37 | var isResultUseless = uselessSerials.some(function(val) { 38 | return val === result; 39 | }); 40 | 41 | if (isResultUseless) { 42 | return ''; 43 | } 44 | 45 | return result; 46 | }; 47 | 48 | var attemptEC2 = function (failCb) { 49 | var data = ''; 50 | var failHandler = function () { 51 | failCb(); 52 | failCb = function () {}; 53 | }; 54 | var request = http.get( 55 | 'http://169.254.169.254/latest/meta-data/instance-id', 56 | function (res) { 57 | res.on('data', function (chunk) { 58 | data += chunk; 59 | }).on('end', function () { 60 | if (data.length > 2) { 61 | cb(null, data.trim()); 62 | } else { 63 | failHandler(); 64 | } 65 | }); 66 | } 67 | ); 68 | request.on('error', failHandler).setTimeout(1000, failHandler); 69 | }; 70 | 71 | cmdPrefix = cmdPrefix || ''; 72 | var vals = ['Serial', 'UUID']; 73 | var cmd; 74 | 75 | switch (process.platform) { 76 | 77 | case 'win32': 78 | delimiter = '\r\n'; 79 | vals[0] = 'IdentifyingNumber'; 80 | cmd = 'wmic csproduct get '; 81 | break; 82 | 83 | case 'darwin': 84 | cmd = 'system_profiler SPHardwareDataType | grep '; 85 | break; 86 | 87 | case 'linux': 88 | if (process.arch === 'arm') { 89 | vals[1] = 'Serial'; 90 | cmd = 'cat /proc/cpuinfo | grep '; 91 | 92 | } else { 93 | cmd = 'dmidecode -t system | grep '; 94 | } 95 | break; 96 | 97 | case 'freebsd': 98 | cmd = 'dmidecode -t system | grep '; 99 | break; 100 | } 101 | 102 | if (!cmd) return cb(new Error('Cannot provide serial number for ' + process.platform)); 103 | 104 | if (serialNumber.preferUUID) vals.reverse(); 105 | 106 | exec(cmdPrefix + cmd + vals[0], function (error, stdout) { 107 | if (error || parseResult(stdout).length > 1) { 108 | stdoutHandler(error, stdout); 109 | } else { 110 | exec(cmdPrefix + cmd + vals[1], stdoutHandler); 111 | } 112 | }); 113 | }; 114 | 115 | serialNumber.preferUUID = false; 116 | 117 | module.exports = exports = serialNumber; 118 | 119 | exports.useSudo = function (cb) { 120 | serialNumber(cb, 'sudo '); 121 | }; 122 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serial-number", 3 | "version": "1.3.0", 4 | "description": "Reads the machine's serial number (a.k.a. service/asset tag) or Amazon EC2 instance-id", 5 | "author": { 6 | "name": "Elan Shanker", 7 | "url": "http://github.com/es128" 8 | }, 9 | "license": "ISC", 10 | "homepage": "https://github.com/es128/serial-number", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/es128/serial-number.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/es128/serial-number/issues" 17 | }, 18 | "bin": { 19 | "serial-number": "bin/serial-number" 20 | }, 21 | "scripts": { 22 | "postinstall": "node setup.js" 23 | }, 24 | "keywords": [ 25 | "serial", 26 | "number", 27 | "serial-number", 28 | "asset", 29 | "service", 30 | "tag", 31 | "machine", 32 | "hardware", 33 | "dell", 34 | "amazon", 35 | "aws", 36 | "ec2", 37 | "instance-id", 38 | "uuid" 39 | ], 40 | "dependencies": {} 41 | } 42 | -------------------------------------------------------------------------------- /setup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var serialNumber = require('./index'); 4 | 5 | var fail = function (err) { 6 | console.error('Could not read serial number:', err); 7 | }; 8 | 9 | serialNumber(function (err) { 10 | if (process.platform !== 'win32' && err && err.toString().match(/Permission denied/i)) { 11 | [ 12 | '\x1B[7m' + // inverse style 13 | 'Your system requires root/administrative priviledge to access the serial number.' + 14 | '\x1B[27m', 15 | 16 | '\x1B[31m' + // red 17 | 'Attempting to run command with `sudo` and cache your serial for future use.' + 18 | '\x1B[39m' 19 | ].forEach(function (msg) {console.info(msg);}); 20 | serialNumber.useSudo(function (err, val) { 21 | if (err) {return fail(err);} 22 | require('fs').writeFile(__dirname + '/cache', val, function (err) { 23 | if (err) { 24 | console.error('Could not write serial number cache file:', err); 25 | } else { 26 | // green 27 | console.info('\x1B[32m' + 'Successfully cached serial number' + '\x1B[39m'); 28 | } 29 | }); 30 | }); 31 | } else if (err) { 32 | fail(err); 33 | } 34 | }); 35 | --------------------------------------------------------------------------------