ffho-debug 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. #!/usr/bin/lua
  2. local debugdata = ''
  3. PATH_DBG_REPORT='/tmp/debug-report.txt'
  4. local nixio = require('nixio'), require('nixio.util')
  5. local fs = require 'nixio.fs'
  6. local json = require('luci.jsonc')
  7. local uci = require('simple-uci').cursor()
  8. local pretty_hostname = require 'pretty_hostname'
  9. local site = require 'gluon.site'
  10. local sysconfig = require 'gluon.sysconfig'
  11. local platform = require 'gluon.platform'
  12. local util = require 'gluon.util'
  13. -- some usefull functions
  14. local hostname = pretty_hostname.get(uci)
  15. local primary_mac = sysconfig.primary_mac
  16. local model = platform.get_model()
  17. local version = util.trim(fs.readfile('/lib/gluon/gluon-version'))
  18. local release = util.trim(fs.readfile('/lib/gluon/release'))
  19. local contact = uci:get_first('gluon-node-info', 'owner', 'contact', 'none')
  20. local autoupdater = uci:get('autoupdater', 'settings', 'branch')
  21. if not uci:get_bool('autoupdater', 'settings', 'enabled') then
  22. autoupdater = 'disabled (' .. autoupdater .. ')'
  23. end
  24. local addresses = ''
  25. for line in io.lines('/proc/net/if_inet6') do
  26. local matches = { line:match('^' .. string.rep('(%x%x%x%x)', 8) .. string.rep(' %x%x', 4) .. '%s+([^%s]+)$') }
  27. if matches[9] == 'br-client' then
  28. addresses = addresses .. ' ' .. string.format('%s:%s:%s:%s:%s:%s:%s:%s', unpack(matches)) .. '\n'
  29. end
  30. end
  31. local data = io.open('/proc/meminfo'):read('*a')
  32. local fields = {}
  33. for k, v in data:gmatch('([^\n:]+):%s*(%d+) kB') do
  34. fields[k] = tonumber(v)
  35. end
  36. local pubkey = util.trim(io.popen('/etc/init.d/fastd show_key mesh_vpn'):read('*a'))
  37. if pubkey == '' then pubkey = 'none' end
  38. local function location()
  39. local text = 'none'
  40. local locationid = uci:get_first('gluon-node-info', 'location')
  41. if locationid then
  42. local location = uci:get_all('gluon-node-info', locationid)
  43. if uci:get_bool('gluon-node-info', locationid, 'share_location') and location.latitude and location.longitude then
  44. text = location.latitude .. ', ' .. location.longitude
  45. end
  46. end
  47. return text
  48. end
  49. local function ip_proto(address)
  50. if address:match("%.") then
  51. return "IPv4"
  52. end
  53. if address:match(":") then
  54. return "IPv6"
  55. end
  56. return "???"
  57. end
  58. -- wrapper for calling systemcommands
  59. local function cmd(_command)
  60. local f = io.popen(_command)
  61. local l = f:read("*a")
  62. f:close()
  63. return "# " .. _command .. ": \n" .. l .. "\n"
  64. end
  65. -- read contents of a given file
  66. local function readFile(_file)
  67. local f = io.open(_file, "r")
  68. if f~=nil then
  69. local l = f:read("*a")
  70. f:close()
  71. return "--- Content of file " .. _file .. " ---\n" .. l .. "\n\n"
  72. else
  73. return ""
  74. end
  75. end
  76. -- Should we enter local mode?
  77. localMode = false
  78. if arg[1] == '-l' then
  79. localMode = true
  80. end
  81. -- search for existing debug report
  82. local oldReport = nixio.open(PATH_DBG_REPORT, "r")
  83. if oldReport==nil then
  84. -- no existing debugreport, let's generate a new one
  85. -- inform the User ;)
  86. print('-- Hello, Mr. Dillinger. Thank you for coming back early.')
  87. print('-- Sit right there; make yourself comfortable. Remember the time we spent play chess together?')
  88. -- first of all, collect some generic information about the system
  89. debugdata = debugdata .. "---- BEGIN SYSTEM INFORMATION ----\n"
  90. debugdata = debugdata .. "Hostname: " .. hostname .. "\n"
  91. debugdata = debugdata .. "Community: " .. site.site_name() .. "\n"
  92. debugdata = debugdata .. "Model: " .. model .. "\n"
  93. debugdata = debugdata .. "Firmware: " .. release .. " / " .. version .. "\n"
  94. debugdata = debugdata .. "MAC: " .. primary_mac .. "\n"
  95. debugdata = debugdata .. "Contact: " .. contact .. "\n"
  96. debugdata = debugdata .. "Uptime: " .. util.trim(io.popen("uptime | sed 's/^ \+//'"):read('*a')) .. '\n'
  97. debugdata = debugdata .. "Autoupdater: " .. autoupdater .. "\n"
  98. debugdata = debugdata .. "Location: " .. location() .. "\n"
  99. debugdata = debugdata .. "IPs: " .. util.trim(addresses) .. "\n"
  100. debugdata = debugdata .. 'Pubkey: ' .. pubkey .. '\n'
  101. debugdata = debugdata .. "Memory: " .. string.format("%.1f %% used, %.1f %% free\n",(fields.MemTotal-fields.MemFree)/fields.MemTotal*100,fields.MemFree/fields.MemTotal*100)
  102. debugdata = debugdata .. cmd("ps w")
  103. debugdata = debugdata .. "---- END SYSTEM INFORMATION ----\n\n"
  104. -- show uci variables
  105. debugdata = debugdata .. "---- BEGIN UCI VARIABLES ----\n"
  106. debugdata = debugdata .. cmd("uci show | grep -v '\.key' | grep -v '\.secret'")
  107. debugdata = debugdata .. "---- END UCI VARIABLES ----\n\n"
  108. -- show cron jobs
  109. debugdata = debugdata .. "---- BEGIN CRON JOBS ----\n"
  110. debugdata = debugdata .. cmd("grep -r \"\" /usr/lib/micron.d/ | sed -e \"s/:/:\\n/\"")
  111. debugdata = debugdata .. "---- END CRON JOBS ----\n\n"
  112. -- now get some information about the network status
  113. debugdata = debugdata .. "---- BEGIN IP AND ROUTUNG INFORMATION ----\n"
  114. debugdata = debugdata .. cmd("ip addr show")
  115. debugdata = debugdata .. cmd("ip route show")
  116. debugdata = debugdata .. cmd("ip -6 route show")
  117. debugdata = debugdata .. "---- BEGIN IP AND ROUTUNG INFORMATION ----\n\n"
  118. -- get wireless status
  119. debugdata = debugdata .. "---- BEGIN WIRELESS INFORMATION ----\n"
  120. local interfaces = io.popen("iw dev | grep Interface | cut -d' ' -f2"):read('*a')
  121. for ifname in interfaces:gmatch('%S+') do
  122. debugdata = debugdata .. cmd("iwinfo " .. ifname .. " info 2>&1")
  123. if ifname:match('ibss') or ifname:match('mesh') then
  124. debugdata = debugdata .. cmd("iwinfo " .. ifname .. " assoclist 2>&1")
  125. end
  126. end
  127. debugdata = debugdata .. "---- END WIRELESS INFORMATION ----\n\n"
  128. -- get batman status
  129. debugdata = debugdata .. "---- BEGIN BATMAN STATUS ----\n"
  130. debugdata = debugdata .. cmd("batctl gwl")
  131. debugdata = debugdata .. cmd("batctl tl")
  132. debugdata = debugdata .. "---- END BATMAN STATUS ----\n\n"
  133. -- get fastd status
  134. debugdata = debugdata .. "---- BEGIN FASTD STATUS ----\n"
  135. if string.len(io.popen("ip -f inet address show dev br-wan | grep global"):read('*a')) >= 2 then
  136. debugdata = debugdata .. "IPv4 configured\n"
  137. else
  138. debugdata = debugdata .. "IPv4 not configured\n"
  139. end
  140. if string.len(io.popen("ip -f inet6 address show dev br-wan | grep global"):read('*a')) >= 2 then
  141. debugdata = debugdata .. "IPv6 configured\n"
  142. else
  143. debugdata = debugdata .. "IPv6 not configured\n"
  144. end
  145. local stat, fastd_status = pcall(
  146. function()
  147. local fastd_sock = nixio.socket('unix', 'stream')
  148. assert(fastd_sock:connect('/var/run/fastd.mesh_vpn.socket'))
  149. local decoder = json.new()
  150. local sink = decoder:sink()
  151. while true do
  152. local chunk = fastd_sock:read(2048)
  153. if not chunk or chunk:len() == 0 then break end
  154. sink(chunk)
  155. end
  156. return assert(decoder:get())
  157. end
  158. )
  159. if stat then
  160. debugdata = debugdata .. string.format("fastd running for %.3f seconds\n", fastd_status.uptime/1000)
  161. local peers = 0
  162. local connections = 0
  163. for key, peer in pairs(fastd_status.peers) do
  164. peers = peers+1
  165. if peer.connection then
  166. connections = connections+1
  167. end
  168. end
  169. debugdata = debugdata .. string.format("There are %i peers configured, of which %i are connected:\n", peers, connections)
  170. for key, peer in pairs(fastd_status.peers) do
  171. debugdata = debugdata .. peer.name .. ": "
  172. if peer.connection then
  173. debugdata = debugdata .. string.format("connected for %.3f seconds via %s\n", peer.connection.established/1000, ip_proto(peer.address))
  174. else
  175. debugdata = debugdata .. "not connected\n"
  176. end
  177. end
  178. else
  179. debugdata = debugdata .. "fastd not running\n"
  180. end
  181. debugdata = debugdata .. "---- END FASTD STATUS ----\n\n"
  182. -- get log
  183. debugdata = debugdata .. "---- BEGIN LOGREAD ----\n"
  184. debugdata = debugdata .. cmd("logread")
  185. debugdata = debugdata .. "---- END LOGREAD ----\n\n"
  186. debugdata = debugdata .. "---- BEGIN DMESG KERNEL LOG ----\n"
  187. debugdata = debugdata .. cmd("dmesg")
  188. debugdata = debugdata .. "---- END DMESG KERNEL LOG ----\n\n"
  189. else
  190. print('Orphaned debug-report file found.')
  191. print('-- You wouldn\'t want me to dig up Flynn\'s file and read it up on a VDT at The Times, would you?')
  192. debugdata = oldReport:readall()
  193. oldReport:close()
  194. end
  195. -- if local mode is requested print the report, otherwise send it to the admin team
  196. if localMode then
  197. print('Omitting to send the report data to a report-server')
  198. print('-- Sark! All of my functions are now yours. Take them!:')
  199. print(debugdata)
  200. nixio.fs.unlink(PATH_DBG_REPORT)
  201. else
  202. print('-- My User has information that could... that could make this a free system again!')
  203. local sent = 0
  204. local reportname = nil
  205. local port = site.debugserver.port()
  206. for _, host in ipairs(site.debugserver.host()) do
  207. print('Trying to deliver debug-report to: ' .. host)
  208. local sock = nixio.connect(host, port, "inet6", "stream")
  209. if sock then
  210. sock:setopt('socket', 'sndtimeo', 30.0)
  211. sock:setopt('socket', 'rcvtimeo', 30.0)
  212. sent = sock:writeall(debugdata)
  213. if sent == debugdata:len() then
  214. -- half-side close to indicate the end of our transmission
  215. sock:shutdown('wr')
  216. print('Transmission succeeded. Waiting for report-name.')
  217. reportname = sock:readall(256)
  218. end
  219. sock:close()
  220. if reportname ~= nil then break end
  221. end
  222. end
  223. if reportname ~= nil then
  224. print('\nYour report has been stored at the debug-server with the name: ' .. reportname)
  225. print('-- With the information I can access, I can run things 900 to 1200 times better than any human.')
  226. else
  227. print('Sorry, I couldn\'t send the report.')
  228. print('-- If you are a User, then everything you\'ve done so far has been according to a plan, right?')
  229. print('-- I will try it again the next time you run me. See you soon ...')
  230. local f = nixio.open(PATH_DBG_REPORT, 'w')
  231. f:writeall(debugdata)
  232. f:close()
  233. end
  234. end
  235. print("-- You've almost reached your decision gate, and I cannot spare you any more time. End of Line.")
  236. os.exit(0)