Advertisement
Guest User

Untitled

a guest
Aug 26th, 2016
124
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 22.16 KB | None | 0 0
  1. local bin = require 'struct'
  2. local bit = require 'bit'
  3. local nixio = require 'nixio'
  4. local ffi = require 'ffi'
  5.  
  6. local fopen = nixio.open
  7. local hash = nixio.crypto.hash
  8. local stpack, stunpack = bin.pack, bin.unpack
  9. local bor, band, bxor, lshift, rshift = bit.bor, bit.band, bit.bxor, bit.lshift, bit.rshift
  10. local byte, sub = string.byte, string.sub
  11. local pow = math.pow
  12.  
  13. local IOC = {
  14. SIZEBITS = 14,
  15. DIRBITS = 2,
  16. NONE = 0,
  17. WRITE = 1,
  18. READ = 2,
  19. }
  20.  
  21. IOC.READWRITE = IOC.READ + IOC.WRITE
  22.  
  23. IOC.NRBITS = 8
  24. IOC.TYPEBITS = 8
  25.  
  26. IOC.NRSHIFT = 0
  27. IOC.TYPESHIFT = IOC.NRSHIFT + IOC.NRBITS
  28. IOC.SIZESHIFT = IOC.TYPESHIFT + IOC.TYPEBITS
  29. IOC.DIRSHIFT = IOC.SIZESHIFT + IOC.SIZEBITS
  30.  
  31. local function _IOC(dir, ch, nr, size)
  32. return bor(lshift(dir, IOC.DIRSHIFT),
  33. lshift(byte(ch), IOC.TYPESHIFT),
  34. lshift(nr, IOC.NRSHIFT),
  35. lshift(size, IOC.SIZESHIFT))
  36. end
  37.  
  38. local _IO = function(ch, nr) return _IOC(IOC.NONE, ch, nr, 0) end
  39. local _IOR = function(ch, nr, tp) return _IOC(IOC.READ, ch, nr, tp) end
  40. local _IOW = function(ch, nr, tp) return _IOC(IOC.WRITE, ch, nr, tp) end
  41. local _IOWR = function(ch, nr, tp) return _IOC(IOC.READWRITE, ch, nr, tp) end
  42.  
  43. local sizeof_ipmi_msg = 8
  44. local sizeof_ipmi_recv = 16 + sizeof_ipmi_msg
  45. local sizeof_ipmi_req = 12 + sizeof_ipmi_msg
  46. local sizeof_ipmi_cmdspec = 4
  47. local sizeof_ipmi_system_interface_addr = 8
  48. local IPMICTL_RECEIVE_MSG_TRUNC = _IOWR('i', 11, sizeof_ipmi_recv)
  49. local IPMICTL_RECEIVE_MSG = _IOWR('i', 12, sizeof_ipmi_recv)
  50. local IPMICTL_SEND_COMMAND = _IOR('i', 13, sizeof_ipmi_req)
  51. local IPMICTL_REGISTER_FOR_CMD = _IOR('i', 14, sizeof_ipmi_cmdspec)
  52. local IPMICTL_UNREGISTER_FOR_CMD = _IOR('i', 15, sizeof_ipmi_cmdspec)
  53. local IPMICTL_SET_GETS_EVENTS_CMD = _IOR('i', 16, 4)
  54. local IPMICTL_SET_MY_ADDRESS_CMD = _IOR('i', 17, 4)
  55. local IPMICTL_GET_MY_ADDRESS_CMD = _IOR('i', 18, 4)
  56. local IPMICTL_SET_MY_LUN_CMD = _IOR('i', 19, 4)
  57. local IPMICTL_GET_MY_LUN_CMD = _IOR('i', 20, 4)
  58.  
  59. local IPMI_BMC_CHANNEL = 0xf
  60. local IPMI_SYSTEM_INTERFACE_ADDR_TYPE = 0x0c
  61. local IPMI_IPMB_ADDR_TYPE = 0x01
  62. local IPMI_IPMB_BROADCAST_ADDR_TYPE = 0x41
  63. local IPMI_RESPONSE_RECV_TYPE = 1
  64. local IPMI_ASYNC_EVENT_RECV_TYPE = 2
  65. local IPMI_CMD_RECV_TYPE = 3
  66.  
  67. ffi.cdef[[
  68. int fileno(void *fp);
  69. int ioctl(int d, unsigned int request, ...);
  70.  
  71. struct ipmi_msg {
  72. uint8_t netfn;
  73. uint8_t cmd;
  74. uint16_t data_len;
  75. uint8_t *data;
  76. };
  77.  
  78. struct ipmi_req {
  79. uint8_t *addr;
  80. uint32_t addr_len;
  81. long msgid;
  82. struct ipmi_msg msg;
  83. };
  84.  
  85. struct ipmi_recv {
  86. int recv_type;
  87. uint8_t *addr;
  88. uint32_t addr_len;
  89. long msgid;
  90. struct ipmi_msg msg;
  91. };
  92.  
  93. struct ipmi_cmdspec {
  94. uint8_t netfn;
  95. uint8_t cmd;
  96. };
  97.  
  98. struct ipmi_system_interface_addr {
  99. int32_t addr_type;
  100. int16_t channel;
  101. uint8_t lun;
  102. };
  103.  
  104. struct ipmi_ipmb_addr {
  105. int32_t addr_type;
  106. int16_t channel;
  107. uint8_t slave_addr;
  108. uint8_t lun;
  109. };
  110. ]]
  111.  
  112. local C = ffi.C
  113.  
  114. local _ioctl = function(fd, ...) return C.ioctl(fd:getfd(), ...) end
  115.  
  116. function _checksum(data)
  117. local i
  118. local csum = 0
  119. for i = 1, #data do
  120. csum = csum + byte(data, i)
  121. end
  122. csum = bxor(csum, 0xff) + 1
  123. return band(csum, 0xff)
  124. end
  125.  
  126. --
  127. -- Lan interface
  128. --
  129. local _L = {
  130. PAYLOADS = {
  131. IPMI = 0x00,
  132. SOL = 0x01,
  133. RMCPPLUSOPENREQ = 0x10,
  134. RMCPPLUSOPENRESPONSE = 0x11,
  135. RAKP1 = 0x12,
  136. RAKP2 = 0x13,
  137. RAKP3 = 0x14,
  138. RAKP4 = 0x15
  139. },
  140.  
  141. RMCP_CODES = {
  142. [1] = 'Insufficient resources to create new session \
  143. (wait for existing sessions to timeout)',
  144. [2] = 'Invalid Session ID',
  145. [3] = 'Invalid payload type',
  146. [4] = 'Invalid authentication algorithm',
  147. }
  148. }
  149.  
  150. local L_mt = { __index = _L }
  151.  
  152. function _L.new(self, sock, user, passwd, cmds, authtype)
  153. local t = {
  154. _sock = sock,
  155. _user = user,
  156. _passwd = passwd,
  157. _kg = passwd,
  158. _cmds = cmds or {},
  159. _sdr_cmds = {},
  160. _sdr_cached = false,
  161. _reqauth = authtype or 2,
  162. }
  163.  
  164. local obj = setmetatable(t, L_mt)
  165. obj:_initsession()
  166. return obj
  167. end
  168.  
  169. function _L._initsession(self)
  170. self._cycles = 0
  171. self._logged = false
  172. self._stopped = true
  173. self._localsid = stunpack('>I', 'MAYC')-1
  174. self._privlevel = 4 -- admin access
  175. self._confalgo = 0
  176. self._aeskey = false
  177. self._integrityalgo = 0
  178. self._k1 = false
  179. self._k2 = false
  180. self._rmcptag = 1
  181. self._sessionid = string.rep('\x00', 4)
  182. self._authtype = 0
  183. self._lastpayload = false
  184. self._seqlun = 0
  185. self._sequencenumber = 0
  186. self._ipmiversion = 0x15
  187. self._ipmi15only = true
  188. self._ver = 0
  189. self._Lfg = -1
  190. self._prod = -1
  191. self._builtin_dr = false
  192. self._rqaddr = 0x81 -- per IPMI talbe 5-4, software ids in the ipmi spec may
  193. -- be 0x81 through 0x8d. We'll stick with 0x81 for now,
  194. -- do not forsee a reason to adjust
  195. self._cmdidx = 0
  196. self._send = _L._get_channel_auth_cap -- _L._presence_ping
  197. self._recv = false
  198. self._oldpayload = false
  199. end
  200.  
  201. function _L._ipmi15authcode(self, payload, checkremotecode)
  202. if self._authcode == 0 then
  203. return ''
  204. end
  205.  
  206. local password = self._passwd
  207. local padneeded = 16 - #password
  208. if padneeded < 0 then
  209. print('Password too long for ipmi 1.5')
  210. return nil
  211. end
  212. password = password .. string.rep('\x00', padneeded)
  213. local seqbytes
  214. if checkremotecode then
  215. --
  216. else
  217. seqbytes = stpack('<I', self._sequencenumber)
  218. end
  219. local md5 = hash('md5')
  220. md5:update(password)
  221. md5:update(self._sessionid)
  222. md5:update(payload)
  223. md5:update(seqbytes)
  224. md5:update(password)
  225. local hex, bin = md5:final()
  226. return bin
  227. end
  228.  
  229. function _L._make_ipmi_payload(self, netfn, command, ...)
  230. -- local seqinc = 7 -- IPMI spec forbids gaps bigger than 7 in seq number
  231.  
  232. local reqbody
  233. local header = stpack('BB', 0x20, lshift(netfn, 2))
  234. local argc = select('#', ...)
  235. if argc > 0 and type(select(1, ...)) == 'string' then
  236. reqbody = stpack('BBBc0', self._rqaddr, self._seqlun, command, ...)
  237. else
  238. reqbody = stpack('BBB'..string.rep('B', argc), self._rqaddr, self._seqlun, command, ...)
  239. end
  240.  
  241. return stpack('c0Bc0B', header, _checksum(header), reqbody, _checksum(reqbody))
  242. end
  243.  
  244. function _L._send_payload(self, netfn, command, ...)
  245. local ipmipayload = self:_make_ipmi_payload(netfn, command, ...)
  246. local payload_type = _L.PAYLOADS.IPMI
  247. if self._integrityalgo ~= 0 then
  248. payload_type = bor(payload_type, 0x40)
  249. end
  250. if self._confalgo ~= 0 then
  251. payload_type = bor(payload_type, 0x80)
  252. end
  253.  
  254. return self:_pack_payload(ipmipayload, payload_type)
  255. end
  256.  
  257. function _L._pack_payload(self, payload, payload_type)
  258. if not payload then
  259. payload = self._lastpayload
  260. end
  261. if not payload_type then
  262. payload_type = self._last_payload_type
  263. end
  264. local message = stpack('BBBB', 0x6, 0, 0xff, 0x07) -- constant RMCP header for IPMI
  265. local baretype = band(payload_type, 0x3f)
  266. self._lastpayload = payload
  267. self._last_payload_type = payload_type
  268. message = message .. stpack('B', self._authtype)
  269. if self._ipmiversion == 0x20 then
  270. --
  271. end
  272.  
  273. message = message .. stpack('<I', self._sequencenumber)
  274. if self._ipmiversion == 0x15 then
  275. message = message..self._sessionid
  276. if self._authtype == 2 then -- MD5
  277. message = message .. stpack('c0', self:_ipmi15authcode(payload))
  278. elseif self._authtype == 4 then -- PASSWORD
  279. message = message .. self._passwd .. string.rep('\x00', 16 - #self._passwd)
  280. end
  281. message = stpack('c0Bc0', message, #payload, payload)
  282. local tl = 34 + #message
  283. if tl == 56 or tl == 84 or tl == 112 or tl == 128 or tl == 156 then
  284. message = message .. '\x00' -- legacy pad as mandated by ipmi spec
  285. end
  286. elseif self._ipmiversion == 0x20 then
  287. --
  288. end
  289.  
  290. if self._sequencenumber ~= 0 then
  291. self._sequencenumber = self._sequencenumber + 1
  292. end
  293.  
  294. return self._sock:send(message)
  295. end
  296.  
  297. function _L.send(self)
  298. if self._send then
  299. return self:_send()
  300. else
  301. return 0
  302. end
  303. end
  304.  
  305. function _L.recv(self)
  306. local data = self._sock:recv(1024)
  307. if #data == 0 then return false end
  308.  
  309. local payload
  310. local is_asf = false
  311. if byte(data, 1) == 0x06 and byte(data, 3) == 0xff and byte(data, 4) == 0x06 then
  312. payload = sub(data, 6)
  313. is_asf = true
  314. elseif not (byte(data, 1) == 0x06 and byte(data, 3) == 0xff and byte(data, 4) == 0x07) then
  315. -- not valid IPMI
  316. print('Not valid IPMI')
  317. return false
  318. end
  319.  
  320. if not is_asf and (byte(data, 5) == 0x00 or byte(data, 5) == 0x02 or byte(data, 5) == 0x04) then
  321. -- IPMI v1.5
  322. local seqnumber = stunpack('<I', sub(data, 6, 9))
  323. if byte(data, 5) ~= self._authtype then
  324. -- logout
  325. -- BMC responded with mismatch authtype
  326. print('BMC responded with mismatch authtype')
  327. return false
  328. end
  329.  
  330. if self._sessionid ~= sub(data, 10, 13) and self._sessionid ~= '\x00\x00\x00\x00' then
  331. self:_initsession()
  332. return true
  333. end
  334.  
  335. local authcode
  336. if byte(data, 5) == 0x02 or byte(data, 5) == 0x04 then
  337. authcode = sub(data, 14, 29)
  338. local sz = byte(data, 30)+30
  339. payload = sub(data, 31, sz)
  340. else
  341. local sz = byte(data, 14)+14
  342. payload = sub(data, 15, sz)
  343. end
  344. -- TODO: check _ipmi15authcode
  345. end
  346.  
  347. self._seqlun = self._seqlun + 4
  348. self._seqlun = band(self._seqlun, 0xff)
  349.  
  350. -- unordr
  351.  
  352. if not self._recv then return false end
  353.  
  354. -- unset receive function
  355. local fn = self._recv
  356. self._recv = false
  357. return fn(self, payload)
  358. end
  359.  
  360. function _L.logout(self)
  361. self._recv = false
  362. self._send = false
  363. if not self._logged then return 0 end
  364. self._logged = false
  365. self._stopped = true
  366. self._sdr_cached = false
  367. return self:_send_payload(0x6, 0x3c, self._sessionid)
  368. end
  369.  
  370. function _L._got_logout(self, response)
  371. self._recv = false
  372. self._send = false
  373. self._logged = false
  374. self._stopped = true
  375. return true
  376. end
  377.  
  378. function _L._get_channel_auth_cap(self)
  379. self._recv = _L._got_channel_auth_cap
  380. if self._ipmi15only then
  381. return self:_send_payload(0x6, 0x38, 0x0e, self._privlevel)
  382. else
  383. return self:_send_payload(0x6, 0x38, 0x8e, self._privlevel)
  384. end
  385. end
  386.  
  387. function _L._got_channel_auth_cap(self, response)
  388. self._recv = false
  389.  
  390. if byte(response, 7) == 0xcc then
  391. self._ipmi15only = true
  392. self._send = _L._get_channel_auth_cap
  393. return true
  394. end
  395.  
  396. -- TODO: check IPMI error for (netfn, command)
  397.  
  398. self._currentchannel = byte(response, 8)
  399. if #response < 11 then
  400. print('Too short reponse')
  401. return false
  402. end
  403.  
  404. if band(byte(response, 9), 0x80) == 0x80 and band(byte(response, 11), 0x02) == 0x02 then
  405. -- ipmi 2.0 support
  406. self._ipmiversion = 0x20
  407. end
  408.  
  409. if self._ipmiversion == 0x15 then
  410. if band(byte(response, 9), 0x04) == 0 then
  411. print('MD5 is required but not enabled/available on target BMC')
  412. return false
  413. end
  414. self._send = _L._get_session_challenge
  415. elseif self._ipmiversion == 0x20 then
  416. self._send = _L._open_rmcpplus_request
  417. else
  418. return false
  419. end
  420.  
  421. return true
  422. end
  423.  
  424. function _L._get_session_challenge(self)
  425. local padneeded = 16 - #self._user
  426.  
  427. if padneeded < 0 then
  428. print('Username too long for IPMI')
  429. return false
  430. end
  431.  
  432. self._recv = _L._got_session_challenge
  433. return self:_send_payload(0x6, 0x39, stpack('B', self._reqauth)..self._user..string.rep('\x00', padneeded))
  434. end
  435.  
  436. function _L._got_session_challenge(self, response)
  437. self._sessionid = sub(response, 8, 11)
  438. self._authtype = self._reqauth
  439. self._challenge = sub(response, 12, #response-1)
  440.  
  441. self._recv = false
  442. self._send = _L._activate_session
  443.  
  444. return true
  445. end
  446.  
  447. function _L._activate_session(self)
  448. self._recv = _L._activated_session
  449. -- TODO(jbjohnso): this always requests admin level (1.5)
  450. return self:_send_payload(0x6, 0x3a, stpack('BB', self._authtype, 0x04)..self._challenge..'\x01\x00\x00\x00')
  451. end
  452.  
  453. function _L._activated_session(self, data)
  454. self._logontries = 5
  455. if byte(data, 7) ~= 0 then
  456. print('Session activate: '..tostring(byte(data, 7)))
  457. -- disconnect
  458. return false
  459. end
  460.  
  461. self._sessionid = sub(data, 9, 12)
  462. self._sequencenumber = stunpack('<I', sub(data, 13, 16))
  463. self._recv = false
  464. self._send = _L._req_priv_level
  465.  
  466. return true
  467. end
  468.  
  469. function _L._req_priv_level(self)
  470. self._recv = _L._got_priv_level
  471. return self:_send_payload(0x6, 0x3b, self._privlevel)
  472. end
  473.  
  474. function _L._got_priv_level(self, response)
  475. if (byte(response, 7) == '\x80' or byte(response, 7) == '\x81') and self._privlevel == 4 then
  476. print('degrade privlevel')
  477. self._logged = true
  478. -- self:_logout()
  479. self:_initsession()
  480. self._privlevel = 3
  481. return true
  482. end
  483.  
  484. return true
  485. end
  486.  
  487. function _L._get_product_id(self)
  488. self._recv = _L._got_product_id
  489. return self:_send_payload(0x6, 0x1)
  490. end
  491.  
  492. function _L._got_product_id(self, response)
  493. self._ver = stunpack('>H', sub(response, 10, 11))
  494. self._mfg = stunpack('<I', sub(response, 14, 16) .. '\x00')
  495. self._prod = stunpack('<H', sub(response, 17, 18))
  496. self._builtin_sdr = band(byte(response, 9), 0x80) == 0x80 and band(byte(response, 13), 0x03) == 0x01
  497. print(self._ver, self._mfg, self._prod, self._builtin_sdr)
  498.  
  499. if self._builtin_sdr then
  500. -- Func Rec Cnt Rsvd
  501. self._sdr = { 0x04, 0x21, 0x00, 0x00 }
  502. else
  503. self._sdr = { 0x0a, 0x23, 0x00, 0x00 }
  504. end
  505.  
  506. if not self._sdr_cached then
  507. self._send = _L._get_sdr_info
  508. else
  509. self._logged = true
  510. self._send = false
  511. end
  512.  
  513. return true
  514. end
  515.  
  516. function _L._get_sdr_info(self)
  517. self._recv = _L._got_sdr_info
  518. return self:_send_payload(self._sdr[1], 0x20)
  519. end
  520.  
  521. function _L._got_sdr_info(self, repo)
  522. self._sdr[3] = stunpack('<H', sub(repo, 9, 10))
  523. self._send = _L._get_sdr_reserve
  524. return true
  525. end
  526.  
  527. function _L._get_sdr_reserve(self)
  528. self._recv = _L._got_sdr_reserve
  529. return self:_send_payload(self._sdr[1], 0x22)
  530. end
  531.  
  532. function _L._got_sdr_reserve(self, response)
  533. if byte(response, 7) ~= 0 then
  534. self._logged = true
  535. self._send = false
  536. return true
  537. end
  538. self._sdr[4] = stunpack('<H', sub(response, 8, 9))
  539. self._sdr_recid = 0
  540. self._sdr_idx = 0
  541. self._send = _L._get_sdr_header
  542.  
  543. return true
  544. end
  545.  
  546. function _L._get_sdr_header(self)
  547. self._recv = _L._got_sdr_header
  548. local payload = stpack('<H<HBB', self._sdr[4], self._sdr_recid, 0, 5)
  549. return self:_send_payload(self._sdr[1], self._sdr[2], payload)
  550. end
  551.  
  552. function _L._got_sdr_header(self, header)
  553. if byte(header, 7) ~= 0 or #header < 14 then
  554. --
  555. -- TODO: should we retry several times before give up?
  556. --
  557. self._send = false
  558. self._logged = true
  559. return true
  560. end
  561.  
  562. self._sdr_nextid = stunpack('<H', sub(header, 8, 9))
  563. if byte(header, 13) ~= 0x01 then -- SDR_RECORD_TYPE_FULL_SENSOR
  564. _L._next_sdr_or_ready(self)
  565. return true
  566. end
  567.  
  568. self._sdr_len = byte(header, 14)
  569. self._send = _L._get_sdr_record
  570.  
  571. return true
  572. end
  573.  
  574. function _L._get_sdr_record(self)
  575. self._recv = _L._got_sdr_record
  576. local payload = stpack('<H<HBB', self._sdr[4], self._sdr_recid, 5, self._sdr_len)
  577. return self:_send_payload(self._sdr[1], self._sdr[2], payload)
  578. end
  579.  
  580. function _L._got_sdr_record(self, record)
  581. local b18 = byte(record, 18)
  582. if #record < 55 or (b18 ~= 0x01 and b18 ~= 0x04 and b18 ~= 0x08) then
  583. _L._next_sdr_or_ready(self)
  584. return true
  585. end
  586. local size = band(byte(record, 52), 0x1f)
  587. local name = sub(record, 53, 52+size):upper()
  588.  
  589. --
  590. -- TODO: filter sensors by list
  591. --
  592.  
  593. local tos32 = function(val, bits)
  594. if band(val, lshift(bits-1, 1)) ~= 0 then
  595. return bor(-band(val, lshift(bits-1, 1)), val)
  596. else
  597. return val
  598. end
  599. end
  600.  
  601. local mtol = stunpack('>H', sub(record, 29, 30))
  602. local bacc = stunpack('>I', sub(record, 31, 34))
  603.  
  604. table.insert(self._cmds, {
  605. -- callback
  606. _L._cmd_got_sensor_reading,
  607. -- netfn, cmd
  608. 0x4, 0x2d,
  609. -- number
  610. byte(record, 12),
  611. -- name
  612. name,
  613. -- unit
  614. rshift(byte(record, 25), 6),
  615. -- linear
  616. band(byte(record, 28), 0x7f),
  617. -- __TO_M
  618. tos32(bor(rshift(band(mtol, 0xff00), 8),
  619. (lshift(band(mtol, 0xc0), 2))), 10),
  620. -- __TO_B
  621. tos32(bor(rshift(band(bacc, 0xff000000), 24),
  622. (rshift(band(bacc, 0xc00000), 14))), 10),
  623. -- __TO_R_EXP
  624. tos32(rshift(band(bacc, 0xf0), 4), 4),
  625. -- __TO_B_EXP
  626. tos32(band(bacc, 0xf), 4)
  627. })
  628.  
  629. _L._next_sdr_or_ready(self)
  630. return true
  631. end
  632.  
  633. function _L._next_sdr_or_ready(self)
  634. local ready = false
  635. if not self._sdr_cached then
  636. self._sdr_idx = self._sdr_idx + 1
  637. self._sdr_recid = self._sdr_nextid
  638. ready = self._sdr_idx == self._sdr[3]
  639. else
  640. ready = true
  641. end
  642. if ready then
  643. self._sdr_idx = 0
  644. self._sdr_cached = true
  645. self._logged = true
  646. self._send = false
  647. print('READY!')
  648. else
  649. self._send = _L._get_sdr_header
  650. end
  651. end
  652.  
  653. function _L._cmd_got_sensor_reading(self, resp)
  654. if #resp < 9 then
  655. return true
  656. end
  657. if not (byte(resp, 7) == 0 and band(byte(resp, 9), 0x20) ~= 0x20 and band(byte(resp, 9), 0x40) == 0x40) then
  658. print('RESULT: ', self._cmds[self._cmdidx+1][5], 'na')
  659. return true
  660. end
  661.  
  662. local name, t,l,m,b,k2,k1 = unpack(self._cmds[self._cmdidx + 1], 5)
  663. local val = byte(resp, 8)
  664. if t == 1 then
  665. if band(val, 0x80) == 0x80 then val = val + 1 end
  666. end
  667. if t > 0 then
  668. -- make int8_t from uint8_t
  669. val = stunpack('b', stpack('B', val))
  670. end
  671. if t > 2 then
  672. -- Ooops! This isn't an analog sensor
  673. return true
  674. end
  675.  
  676. local result = ((m * val) + (b * pow(10, k1))) * pow(10, k2)
  677. print('RESULT: ', name, result)
  678.  
  679. return true
  680. end
  681.  
  682. function _L._process_next_cmd(self)
  683. if #self._cmds == 0 then
  684. self._stopped = true
  685. return 0
  686. end
  687. self._recv = _L._got_next_cmd
  688. local cmd = self._cmds[self._cmdidx + 1]
  689. if type(cmd[4]) == 'table' then
  690. return self:_send_payload(cmd[2], cmd[3], unpack(cmd[4] or {}))
  691. elseif cmd[4] then
  692. return self:_send_payload(cmd[2], cmd[3], cmd[4])
  693. else
  694. return self:_send_payload(cmd[2], cmd[3])
  695. end
  696. end
  697.  
  698. function _L._got_next_cmd(self, response)
  699. self._cmds[self._cmdidx + 1][1](self, response)
  700. self._cmdidx = (self._cmdidx + 1) % #self._cmds
  701. if self._cmdidx == 0 then self._stopped = 0 end
  702. end
  703.  
  704. function _L.process_commands(self)
  705. if not self._logged or not self._stopped then return 0 end
  706.  
  707. self._stopped = false
  708. self._send = self._process_next_cmd
  709. return self:send()
  710. end
  711.  
  712. function _L.command(self, cb, netfn, cmd, ...)
  713. self._recv = cb
  714. return self:_send_payload(netfn, cmd, ...)
  715. end
  716.  
  717. --
  718. -- OpenIPMI interface
  719. --
  720. local _O = {
  721. send = _L.send,
  722. process_commands = _L.process_commands,
  723. command = _L.command,
  724. }
  725.  
  726. local O_mt = { __index = _O }
  727.  
  728. function _O.new(self, devnum, cmds)
  729. local t = {
  730. n = devnum,
  731. f = fopen('/dev/ipmi'..tonumber(devnum)),
  732. req = ffi.new('struct ipmi_req'),
  733. rsp = ffi.new('struct ipmi_recv'),
  734. bmc_addr = ffi.new('struct ipmi_system_interface_addr[1]'),
  735. ipmb_addr = ffi.new('struct ipmi_ipmb_addr[1]'),
  736. _cmds = cmds or { },
  737. _cmdidx = 0,
  738. _sdr_cmds = { },
  739. _sdr_cached = false,
  740. _stopped = true,
  741. _logged = false,
  742. _send = _L._get_product_id,
  743. _recv = false,
  744. }
  745. if not t.f then return end
  746.  
  747. getmetatable(t.f).getfd = function(self) return tonumber(tostring(self):sub(12)) end
  748.  
  749. local i = ffi.new('int[1]', 0)
  750. if _ioctl(t.f, IPMICTL_SET_GETS_EVENTS_CMD, i) < 0 then
  751. return
  752. end
  753.  
  754. t.bmc_addr[0].addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE
  755. t.bmc_addr[0].channel = IPMI_BMC_CHANNEL
  756. t.ipmb_addr[0].addr_type = IPMI_IPMB_ADDR_TYPE
  757.  
  758. t.rsp.addr = ffi.new('int8_t[32]')
  759. t.rsp.msg.data = ffi.new('int8_t[256]')
  760.  
  761. t.req.msgid = 1
  762.  
  763. return setmetatable(t, O_mt)
  764. end
  765.  
  766. function _O._send_payload(self, netfn, command, ...)
  767. local reqbody
  768. local argc = select('#', ...)
  769. if argc > 0 and type(select(1, ...)) == 'string' then
  770. reqbody = stpack('c0', ...)
  771. else
  772. reqbody = stpack(string.rep('B', argc), ...)
  773. end
  774.  
  775. self.req.addr = ffi.cast('char *', self.bmc_addr)
  776. self.req.addr_len = sizeof_ipmi_system_interface_addr
  777.  
  778. self.req.msgid = self.req.msgid + 1
  779.  
  780. self.req.msg.data = ffi.cast('char *', reqbody)
  781. self.req.msg.data_len = #reqbody
  782. self.req.msg.netfn = netfn
  783. self.req.msg.cmd = command
  784.  
  785. -- print('>>>>', netfn, command, nixio.bin.hexlify(reqbody))
  786. local ret = _ioctl(self.f, IPMICTL_SEND_COMMAND, self.req)
  787. return ret
  788. end
  789.  
  790. function _O.recv(self)
  791. self.rsp.addr_len = 32
  792. self.rsp.msg.data_len = 256
  793.  
  794. if _ioctl(self.f, IPMICTL_RECEIVE_MSG_TRUNC, self.rsp) < 0 then return false end
  795.  
  796. if not self._recv then return false end
  797.  
  798. local payload = ffi.string(self.rsp.msg.data, self.rsp.msg.data_len)
  799.  
  800. -- unset receive function
  801. local fn = self._recv
  802. self._recv = false
  803. -- print('<<<<<', nixio.bin.hexlify(payload))
  804. return fn(self, string.rep('\x00', 6)..payload)
  805. end
  806.  
  807. function _O._process_next_cmd(self)
  808. if #self._cmds == 0 then
  809. self._stopped = true
  810. return 0
  811. end
  812. self._recv = _L._got_next_cmd
  813. local cmd = self._cmds[self._cmdidx + 1]
  814. if type(cmd[4]) == 'table' then
  815. return self:_send_payload(cmd[2], cmd[3], unpack(cmd[4] or {}))
  816. elseif cmd[4] then
  817. return self:_send_payload(cmd[2], cmd[3], cmd[4])
  818. else
  819. return self:_send_payload(cmd[2], cmd[3])
  820. end
  821. end
  822.  
  823. return { lan = _L, open = _O }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement