-
Notifications
You must be signed in to change notification settings - Fork 27
/
device.js
169 lines (137 loc) · 5.19 KB
/
device.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
const Promise = require('bluebird')
const path = require('path')
const fs = require('fs')
const child = require('child_process')
const broadcast = require('../common/broadcast')
const barcelona = require('./barcelona')
/**
This module probes cpu, memory, dmi info (or barcelona equivalent), software release revision, source code revision (commit), when system inits.
These information are probed once and results are cached.
@module Device
@requires Barcelona
@fires DeviceUpdate
*/
/**
Fired when device probe finished during system inits.
@event DeviceProbeDone
@global
*/
// K combinator
const K = x => y => x
const dminames = [
'bios-vendor', 'bios-version', 'bios-release-date',
'system-manufacturer', 'system-product-name',
'system-version', 'system-serial-number', 'system-uuid',
'baseboard-manufacturer', 'baseboard-product-name',
'baseboard-version', 'baseboard-serial-number',
'baseboard-asset-tag',
'chassis-manufacturer', 'chassis-type', 'chassis-version',
'chassis-serial-number', 'chassis-asset-tag',
'processor-family', 'processor-manufacturer',
'processor-version', 'processor-frequency'
]
// this function change string format 'processor-family' to js style 'processorFamily'
const camelCase = text =>
text.split(/[_\- ()]/)
.map((w, idx) => idx === 0
? w.charAt(0).toLowerCase() + w.slice(1)
: w.charAt(0).toUpperCase() + w.slice(1))
.join('')
// parse
const parseSingleSectionOutput = stdout =>
stdout.toString().split('\n') // split to lines
.map(l => l.trim()).filter(l => l.length) // trim and remove empty line
.map(l => l.split(':').map(w => w.trim())) // split to word array (kv)
.filter(arr => arr.length === 2 && arr[0].length) // filter out non-kv
.reduce((obj, arr) => K(obj)(obj[camelCase(arr[0])] = arr[1]), {}) // merge into one object
// parse
const parseMultiSectionOutput = stdout =>
stdout.toString().split('\n\n') // split to sections
.map(sect => sect.trim()) // trim
.filter(sect => sect.length) // remove last empty
.map(sect =>
sect.split('\n') // process each section
.map(l => l.trim()).filter(l => l.length) // trim and remove empty line
.map(l => l.split(':').map(w => w.trim())) // split to word array (kv)
.filter(arr => arr.length === 2 && arr[0].length) // filter out non-kv
.reduce((obj, arr) => K(obj)(obj[camelCase(arr[0])] = arr[1]), {})) // merge into one object
const probeProcAsync = async (path, multi) => {
let stdout = await child.execAsync(`cat /proc/${path}`)
return multi
? parseMultiSectionOutput(stdout)
: parseSingleSectionOutput(stdout)
}
// return undefined if not barcelona
const probeWS215iAsync = async () => {
try {
await fs.statAsync('/proc/BOARD_io')
let arr = await Promise.all([
child.execAsync('dd if=/dev/mtd0ro bs=1 skip=1697760 count=11'),
child.execAsync('dd if=/dev/mtd0ro bs=1 skip=1697664 count=20'),
child.execAsync('dd if=/dev/mtd0ro bs=1 skip=1660976 count=6 | xxd -p')
])
return {
serial: arr[0].toString(),
p2p: arr[1].toString(),
mac: arr[2].trim().match(/.{2}/g).join(':')
}
} catch (e) {}
}
// callback version is much easier than that of async version with bluebird promise reflection
const dmiDecode = cb => {
let count = dminames.length, dmidecode = {}
const end = () => (!--count) && cb(null, dmidecode)
dminames.forEach(name =>
child.exec(`dmidecode -s ${name}`, (err, stdout) =>
end(!err && stdout.length &&
(dmidecode[camelCase(name)] = stdout.toString().split('\n')[0].trim()))))
}
// return undefined for barcelona
const dmiDecodeAsync = async () => {
try {
await fs.statAsync('/proc/BOARD_io')
return
} catch (e) {}
return await Promise.promisify(dmiDecode)()
}
// return null if not in production deployment
const probeReleaseAsync = async () => {
if (process.cwd() === '/wisnuc/appifi') {
try {
return JSON.parse(await fs.readFileAsync('/wisnuc/appifi/.release.json'))
} catch(e) {}
}
return null
}
// return null if not in production deployment
const probeRevisionAsync = async () => {
if (process.cwd() === '/wisnuc/appifi') {
try {
return (await fs.readFileAsync('/wisnuc/appifi/.revision')).toString().trim()
} catch (e) {}
}
return null
}
let device = null
let promises = [
probeProcAsync('cpuinfo', true),
probeProcAsync('meminfo', false),
probeWS215iAsync(),
dmiDecodeAsync(),
probeReleaseAsync(),
probeRevisionAsync()
]
Promise
.all(promises)
.then(arr => {
device = {
cpuInfo: arr[0],
memInfo: arr[1],
ws215i: arr[2],
dmidecode: arr[3],
release: arr[4],
commit: arr[5] // for historical reason, this is named commit
}
broadcast.emit('DeviceUpdate', null, device)
})
module.exports = () => device