parent
c1db89e657
commit
36430257fd
|
@ -0,0 +1,13 @@
|
|||
module github.com/status-im/status-go/extkeys
|
||||
|
||||
go 1.13
|
||||
|
||||
replace github.com/ethereum/go-ethereum v1.9.5 => github.com/status-im/go-ethereum v1.9.5-status.7
|
||||
|
||||
require (
|
||||
github.com/btcsuite/btcd v0.20.1-beta
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d
|
||||
github.com/ethereum/go-ethereum v1.9.5
|
||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c
|
||||
golang.org/x/text v0.3.2
|
||||
)
|
|
@ -0,0 +1,102 @@
|
|||
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
||||
github.com/Azure/azure-pipeline-go v0.0.0-20180607212504-7571e8eb0876/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
|
||||
github.com/Azure/azure-storage-blob-go v0.0.0-20180712005634-eaae161d9d5e/go.mod h1:x2mtS6O3mnMEZOJp7d7oldh8IvatBrMfReiyQ+cKgKY=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/allegro/bigcache v0.0.0-20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ=
|
||||
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20170201225849-2268707a8f08/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/docker/docker v0.0.0-20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/elastic/gosigar v0.0.0-20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs=
|
||||
github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gizak/termui v0.0.0-20170117222342-991cd3d38091/go.mod h1:PkJoWUt/zacQKysNfQtcw1RW+eK2SxkieVBtl+4ovLA=
|
||||
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||
github.com/go-stack/stack v1.5.4/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag=
|
||||
github.com/influxdata/influxdb v0.0.0-20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
|
||||
github.com/jackpal/go-nat-pmp v0.0.0-20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/julienschmidt/httprouter v0.0.0-20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/karalabe/hid v0.0.0-20181128192157-d815e0c1a2e2/go.mod h1:YvbcH+3Wo6XPs9nkgTY3u19KXLauXW+J5nB7hEHuX0A=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e/go.mod h1:nty42YY5QByNC5MM7q/nj938VbgPU7avs45z6NClpxI=
|
||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.0-20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/naoina/toml v0.0.0-20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
||||
github.com/nsf/termbox-go v0.0.0-20170211012700-3540b76b9c77/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170128050532-febf2d34b54a/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opentracing/opentracing-go v0.0.0-20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
|
||||
github.com/peterh/liner v0.0.0-20170902204657-a37ad3984311/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||
github.com/pkg/errors v0.0.0-20171216070316-e881fd58d78e/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/prometheus v0.0.0-20170814170113-3101606756c5/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s=
|
||||
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
|
||||
github.com/robertkrimen/otto v0.0.0-20170205013659-6a77b7cbc37d/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY=
|
||||
github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ=
|
||||
github.com/status-im/go-ethereum v1.9.5-status.7 h1:DKH1GiF52LwaZaw6YDBliFEgm/JDsbIT+hn7ph6X94Q=
|
||||
github.com/status-im/go-ethereum v1.9.5-status.7/go.mod h1:YyH5DKB6+z+Vaya7eIm67pnuPZ1oiUMbbsZW41ktN0g=
|
||||
github.com/status-im/status-go/extkeys v1.0.0/go.mod h1:GdqJbrcpkNm5ZsSCpp+PdMxnXx+OcRBdm3PI0rs1FpU=
|
||||
github.com/stretchr/testify v0.0.0-20170809224252-890a5c3458b4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/syndtr/goleveldb v0.0.0-20181128100959-b001fa50d6b2/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
||||
github.com/uber/jaeger-client-go v0.0.0-20180607151842-f7e0d4744fa6/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
|
||||
github.com/uber/jaeger-lib v0.0.0-20180615202729-a51202d6f4a7/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba h1:9bFeDpN3gTqNanMVqNcoR/pJQuP5uroC3t1D7eXozTE=
|
||||
golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4=
|
||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405 h1:829vOVxxusYHC+IqBtkX5mbKtsY9fheQiQn0MZRVLfQ=
|
||||
gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20180302121509-abf0ba0be5d5/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
|
||||
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
4
go.mod
4
go.mod
|
@ -12,8 +12,6 @@ replace github.com/nfnt/resize => github.com/status-im/resize v0.0.0-20201215164
|
|||
|
||||
require (
|
||||
github.com/beevik/ntp v0.2.0
|
||||
github.com/btcsuite/btcd v0.22.0-beta
|
||||
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce
|
||||
github.com/cenkalti/backoff/v3 v3.2.2
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/deckarep/golang-set v1.7.1
|
||||
|
@ -54,6 +52,7 @@ require (
|
|||
github.com/status-im/markdown v0.0.0-20201022101546-c0cbdd5763bf
|
||||
github.com/status-im/migrate/v4 v4.6.2-status.2
|
||||
github.com/status-im/rendezvous v1.3.2
|
||||
github.com/status-im/status-go/extkeys v1.1.2
|
||||
github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954
|
||||
|
@ -64,7 +63,6 @@ require (
|
|||
go.uber.org/zap v1.15.0
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
|
||||
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb
|
||||
golang.org/x/text v0.3.6
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
||||
gopkg.in/go-playground/validator.v9 v9.31.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
|
|
1
go.sum
1
go.sum
|
@ -1311,6 +1311,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# Directories
|
||||
/.vagrant
|
||||
/.idea
|
||||
/build
|
||||
|
||||
# Files
|
||||
.DS_Store
|
||||
/*.iml
|
||||
*.h
|
||||
|
||||
# Editor swap files
|
||||
*.swp
|
||||
*.swo
|
||||
*.swn
|
||||
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
*.pyc
|
||||
*.swp
|
||||
|
||||
# Example binaries
|
||||
examples/df/df
|
||||
examples/df/df.exe
|
||||
examples/free/free
|
||||
examples/free/free.exe
|
||||
examples/ps/ps
|
||||
examples/ps/ps.exe
|
||||
examples/ss/ss
|
||||
examples/ss/ss.exe
|
||||
examples/uptime/uptime
|
||||
examples/uptime/uptime.exe
|
||||
|
||||
# Test Data
|
||||
cgroup/testdata/*
|
||||
!cgroup/testdata/*.zip
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
# elastic/gosigar Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
### Fixed
|
||||
|
||||
### Changed
|
||||
|
||||
### Deprecated
|
||||
|
||||
## [0.14.1]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix unsupported devices for filesystem. [#159](https://github.com/elastic/gosigar/pull/159)
|
||||
|
||||
## [0.14.0]
|
||||
|
||||
### Addded
|
||||
|
||||
- Add darwin ARM support. [#152](https://github.com/elastic/gosigar/pull/152)
|
||||
|
||||
## [0.13.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Add method of overriding the /sys/fs/cgroup hierarchy, for reading cgroup metrics inside Docker [#148](https://github.com/elastic/gosigar/pull/148)
|
||||
|
||||
## [0.12.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Add `Cached` data to Memory [#145](https://github.com/elastic/gosigar/pull/145)
|
||||
- Add `SysTypeName` support on Windows [#146](https://github.com/elastic/gosigar/pull/146)
|
||||
|
||||
## [0.11.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Added support for AIX. [#133](https://github.com/elastic/gosigar/pull/133)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed the `ss` example by replacing the Logrus package with the stdlib `log` package. [#123](https://github.com/elastic/gosigar/issues/123) [#136](https://github.com/elastic/gosigar/pull/136)
|
||||
- Replaced `bytePtrToString` and cleaned up darwin code. [#138](https://github.com/elastic/gosigar/issues/138) [#141](https://github.com/elastic/gosigar/pull/141)
|
||||
|
||||
## [0.10.5]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed uptime calculation under Windows. [#126](https://github.com/elastic/gosigar/pull/126)
|
||||
- Fixed compilation issue for darwin/386. [#128](https://github.com/elastic/gosigar/pull/128)
|
||||
|
||||
### Changed
|
||||
|
||||
- Load DLLs only from Windows system directory. [#132](https://github.com/elastic/gosigar/pull/132)
|
||||
|
||||
## [0.10.4]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed a crash when splitting command-line arguments under Windows. [#124](https://github.com/elastic/gosigar/pull/124)
|
||||
|
||||
## [0.10.3]
|
||||
|
||||
### Fixed
|
||||
- ProcState.Get() doesn't fail under Windows when it cannot obtain process ownership information. [#121](https://github.com/elastic/gosigar/pull/121)
|
||||
|
||||
## [0.10.2]
|
||||
|
||||
### Fixed
|
||||
- Fix memory leak when getting process arguments. [#119](https://github.com/elastic/gosigar/pull/119)
|
||||
|
||||
## [0.10.1]
|
||||
|
||||
### Fixed
|
||||
- Replaced the WMI queries with win32 apis due to high CPU usage. [#116](https://github.com/elastic/gosigar/pull/116)
|
||||
|
||||
## [0.10.0]
|
||||
|
||||
### Added
|
||||
- List filesystems on Windows that have an access path but not an assigned letter. [#112](https://github.com/elastic/gosigar/pull/112)
|
||||
|
||||
### Fixed
|
||||
- Added missing runtime import for FreeBSD. [#104](https://github.com/elastic/gosigar/pull/104)
|
||||
- Handle nil command line in Windows processes. [#110](https://github.com/elastic/gosigar/pull/110)
|
||||
|
||||
## [0.9.0]
|
||||
|
||||
### Added
|
||||
- Added support for huge TLB pages on Linux [#97](https://github.com/elastic/gosigar/pull/97)
|
||||
- Added support for big endian platform [#100](https://github.com/elastic/gosigar/pull/100)
|
||||
|
||||
### Fixed
|
||||
- Add missing method for OpenBSD [#99](https://github.com/elastic/gosigar/pull/99)
|
||||
|
||||
## [0.8.0]
|
||||
|
||||
### Added
|
||||
- Added partial `getrusage` support for Windows to retrieve system CPU time and user CPU time. [#95](https://github.com/elastic/gosigar/pull/95)
|
||||
- Added full `getrusage` support for Unix. [#95](https://github.com/elastic/gosigar/pull/95)
|
||||
|
||||
## [0.7.0]
|
||||
|
||||
### Added
|
||||
- Added method stubs for process handling for operating system that are not supported
|
||||
by gosigar. All methods return `ErrNotImplemented` on such systems. [#88](https://github.com/elastic/gosigar/pull/88)
|
||||
|
||||
### Fixed
|
||||
- Fix freebsd build by using the common version of Get(pid). [#91](https://github.com/elastic/gosigar/pull/91)
|
||||
|
||||
### Changed
|
||||
- Fixed issues in cgroup package by adding missing error checks and closing
|
||||
file handles. [#92](https://github.com/elastic/gosigar/pull/92)
|
||||
|
||||
## [0.6.0]
|
||||
|
||||
### Added
|
||||
- Added method stubs to enable compilation for operating systems that are not
|
||||
supported by gosigar. All methods return `ErrNotImplemented` on these unsupported
|
||||
operating systems. [#83](https://github.com/elastic/gosigar/pull/83)
|
||||
- FreeBSD returns `ErrNotImplemented` for `ProcTime.Get`. [#83](https://github.com/elastic/gosigar/pull/83)
|
||||
|
||||
### Changed
|
||||
- OpenBSD returns `ErrNotImplemented` for `ProcTime.Get` instead of `nil`. [#83](https://github.com/elastic/gosigar/pull/83)
|
||||
- Fixed incorrect `Mem.Used` calculation under linux. [#82](https://github.com/elastic/gosigar/pull/82)
|
||||
- Fixed `ProcState` on Linux and FreeBSD when process names contain parentheses. [#81](https://github.com/elastic/gosigar/pull/81)
|
||||
|
||||
### Removed
|
||||
- Remove NetBSD build from sigar_unix.go as it is not supported by gosigar. [#83](https://github.com/elastic/gosigar/pull/83)
|
||||
|
||||
## [0.5.0]
|
||||
|
||||
### Changed
|
||||
- Fixed Trim environment variables when comparing values in the test suite. [#79](https://github.com/elastic/gosigar/pull/79)
|
||||
- Make `kern_procargs` more robust under darwin when we cannot retrieve
|
||||
all the information about a process. [#78](https://github.com/elastic/gosigar/pull/78)
|
||||
|
||||
## [0.4.0]
|
||||
|
||||
### Changed
|
||||
- Fixed Windows issue that caused a hang during `init()` if WMI wasn't ready. [#74](https://github.com/elastic/gosigar/pull/74)
|
||||
|
||||
## [0.3.0]
|
||||
|
||||
### Added
|
||||
- Read `MemAvailable` value for kernel 3.14+ [#71](https://github.com/elastic/gosigar/pull/71)
|
||||
|
||||
## [0.2.0]
|
||||
|
||||
### Added
|
||||
- Added `ErrCgroupsMissing` to indicate that /proc/cgroups is missing which is
|
||||
an indicator that cgroups were disabled at compile time. [#64](https://github.com/elastic/gosigar/pull/64)
|
||||
|
||||
### Changed
|
||||
- Changed `cgroup.SupportedSubsystems()` to honor the "enabled" column in the
|
||||
/proc/cgroups file. [#64](https://github.com/elastic/gosigar/pull/64)
|
||||
|
||||
## [0.1.0]
|
||||
|
||||
### Added
|
||||
- Added `CpuList` implementation for Windows that returns CPU timing information
|
||||
on a per CPU basis. [#55](https://github.com/elastic/gosigar/pull/55)
|
||||
- Added `Uptime` implementation for Windows. [#55](https://github.com/elastic/gosigar/pull/55)
|
||||
- Added `Swap` implementation for Windows based on page file metrics. [#55](https://github.com/elastic/gosigar/pull/55)
|
||||
- Added support to `github.com/gosigar/sys/windows` for querying and enabling
|
||||
privileges in a process token.
|
||||
- Added utility code for interfacing with linux NETLINK_INET_DIAG. [#60](https://github.com/elastic/gosigar/pull/60)
|
||||
- Added `ProcEnv` for getting a process's environment variables. [#61](https://github.com/elastic/gosigar/pull/61)
|
||||
|
||||
### Changed
|
||||
- Changed several `OpenProcess` calls on Windows to request the lowest possible
|
||||
access privileges. [#50](https://github.com/elastic/gosigar/pull/50)
|
||||
- Removed cgo usage from Windows code.
|
||||
- Added OS version checks to `ProcArgs.Get` on Windows because the
|
||||
`Win32_Process` WMI query is not available prior to Windows vista. On XP and
|
||||
Windows 2003, this method returns `ErrNotImplemented`. [#55](https://github.com/elastic/gosigar/pull/55)
|
||||
|
||||
### Fixed
|
||||
- Fixed value of `Mem.ActualFree` and `Mem.ActualUsed` on Windows. [#49](https://github.com/elastic/gosigar/pull/49)
|
||||
- Fixed `ProcTime.StartTime` on Windows to report value in milliseconds since
|
||||
Unix epoch. [#51](https://github.com/elastic/gosigar/pull/51)
|
||||
- Fixed `ProcStatus.PPID` value is wrong on Windows. [#55](https://github.com/elastic/gosigar/pull/55)
|
||||
- Fixed `ProcStatus.Username` error on Windows XP [#56](https://github.com/elastic/gosigar/pull/56)
|
||||
|
||||
[Unreleased]: https://github.com/elastic/gosigar/compare/v0.14.1...HEAD
|
||||
[0.14.1]: https://github.com/elastic/gosigar/releases/tag/v0.14.1
|
||||
[0.14.0]: https://github.com/elastic/gosigar/releases/tag/v0.14.0
|
||||
[0.13.0]: https://github.com/elastic/gosigar/releases/tag/v0.13.0
|
||||
[0.12.0]: https://github.com/elastic/gosigar/releases/tag/v0.12.0
|
||||
[0.11.0]: https://github.com/elastic/gosigar/releases/tag/v0.11.0
|
||||
[0.10.5]: https://github.com/elastic/gosigar/releases/tag/v0.10.5
|
||||
[0.10.4]: https://github.com/elastic/gosigar/releases/tag/v0.10.4
|
||||
[0.10.3]: https://github.com/elastic/gosigar/releases/tag/v0.10.3
|
||||
[0.10.2]: https://github.com/elastic/gosigar/releases/tag/v0.10.2
|
||||
[0.10.1]: https://github.com/elastic/gosigar/releases/tag/v0.10.1
|
||||
[0.10.0]: https://github.com/elastic/gosigar/releases/tag/v0.10.0
|
||||
[0.9.0]: https://github.com/elastic/gosigar/releases/tag/v0.9.0
|
||||
[0.8.0]: https://github.com/elastic/gosigar/releases/tag/v0.8.0
|
||||
[0.7.0]: https://github.com/elastic/gosigar/releases/tag/v0.7.0
|
||||
[0.6.0]: https://github.com/elastic/gosigar/releases/tag/v0.6.0
|
||||
[0.5.0]: https://github.com/elastic/gosigar/releases/tag/v0.5.0
|
||||
[0.4.0]: https://github.com/elastic/gosigar/releases/tag/v0.4.0
|
||||
[0.3.0]: https://github.com/elastic/gosigar/releases/tag/v0.3.0
|
||||
[0.2.0]: https://github.com/elastic/gosigar/releases/tag/v0.2.0
|
||||
[0.1.0]: https://github.com/elastic/gosigar/releases/tag/v0.1.0
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,9 @@
|
|||
Copyright (c) [2009-2011] VMware, Inc. All Rights Reserved.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
You may not use this product except in compliance with the License.
|
||||
|
||||
This product includes a number of subcomponents with
|
||||
separate copyright notices and license terms. Your use of these
|
||||
subcomponents is subject to the terms and conditions of the
|
||||
subcomponent's license, as noted in the LICENSE file.
|
|
@ -0,0 +1,59 @@
|
|||
# Go sigar [![Build Status](https://beats-ci.elastic.co/job/Beats/job/gosigar/job/master/badge/icon)](https://beats-ci.elastic.co/job/Beats/job/gosigar/job/master/)
|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
Go sigar is a golang implementation of the
|
||||
[sigar API](https://github.com/hyperic/sigar). The Go version of
|
||||
sigar has a very similar interface, but is being written from scratch
|
||||
in pure go/cgo, rather than cgo bindings for libsigar.
|
||||
|
||||
## Test drive
|
||||
|
||||
$ go get github.com/elastic/gosigar
|
||||
$ cd $GOPATH/src/github.com/elastic/gosigar/examples/ps
|
||||
$ go build
|
||||
$ ./ps
|
||||
|
||||
## Supported platforms
|
||||
|
||||
The features vary by operating system.
|
||||
|
||||
| Feature | Linux | Darwin | Windows | OpenBSD | FreeBSD | AIX |
|
||||
|-----------------|:-----:|:------:|:-------:|:-------:|:-------:|:-------:|
|
||||
| Cpu | X | X | X | X | X | X |
|
||||
| CpuList | X | X | | X | X | X |
|
||||
| FDUsage | X | | | | X | |
|
||||
| FileSystemList | X | X | X | X | X | X |
|
||||
| FileSystemUsage | X | X | X | X | X | X |
|
||||
| HugeTLBPages | X | | | | | |
|
||||
| LoadAverage | X | X | | X | X | X |
|
||||
| Mem | X | X | X | X | X | X |
|
||||
| ProcArgs | X | X | X | | X | X |
|
||||
| ProcEnv | X | X | | | X | X |
|
||||
| ProcExe | X | X | | | X | X |
|
||||
| ProcFDUsage | X | | | | X | |
|
||||
| ProcList | X | X | X | | X | X |
|
||||
| ProcMem | X | X | X | | X | X |
|
||||
| ProcState | X | X | X | | X | X |
|
||||
| ProcTime | X | X | X | | X | X |
|
||||
| Rusage | X | | X | | | X |
|
||||
| Swap | X | X | | X | X | X |
|
||||
| Uptime | X | X | | X | X | X |
|
||||
|
||||
## OS Specific Notes
|
||||
|
||||
### FreeBSD
|
||||
|
||||
Mount both `linprocfs` and `procfs` for compatability. Consider adding these
|
||||
mounts to your `/etc/fstab` file so they are mounted automatically at boot.
|
||||
|
||||
```
|
||||
sudo mount -t procfs proc /proc
|
||||
sudo mkdir -p /compat/linux/proc
|
||||
sudo mount -t linprocfs /dev/null /compat/linux/proc
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Apache 2.0
|
|
@ -0,0 +1,25 @@
|
|||
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
|
||||
VAGRANTFILE_API_VERSION = "2"
|
||||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
config.vm.box = "hashicorp/precise64"
|
||||
config.vm.provision "shell", inline: "mkdir -p /home/vagrant/go"
|
||||
config.vm.synced_folder ".", "/home/vagrant/go/src/github.com/cloudfoundry/gosigar"
|
||||
config.vm.provision "shell", inline: "chown -R vagrant:vagrant /home/vagrant/go"
|
||||
install_go = <<-BASH
|
||||
set -e
|
||||
|
||||
if [ ! -d "/usr/local/go" ]; then
|
||||
cd /tmp && wget https://storage.googleapis.com/golang/go1.3.3.linux-amd64.tar.gz
|
||||
cd /usr/local
|
||||
tar xvzf /tmp/go1.3.3.linux-amd64.tar.gz
|
||||
echo 'export GOPATH=/home/vagrant/go; export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin' >> /home/vagrant/.bashrc
|
||||
fi
|
||||
export GOPATH=/home/vagrant/go
|
||||
export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin
|
||||
/usr/local/go/bin/go get -u github.com/onsi/ginkgo/ginkgo
|
||||
/usr/local/go/bin/go get -u github.com/onsi/gomega;
|
||||
BASH
|
||||
config.vm.provision "shell", inline: 'apt-get install -y git-core'
|
||||
config.vm.provision "shell", inline: install_go
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
# Enable coverage report message for diff on commit
|
||||
coverage:
|
||||
status:
|
||||
project: off
|
||||
patch:
|
||||
default:
|
||||
# basic
|
||||
target: auto
|
||||
threshold: null
|
||||
base: auto
|
||||
# advanced
|
||||
branches: null
|
||||
if_no_uploads: error
|
||||
if_not_found: success
|
||||
if_ci_failed: error
|
||||
only_pulls: false
|
||||
flags: null
|
||||
paths: null
|
||||
|
||||
# Disable comments on Pull Requests
|
||||
comment: false
|
|
@ -0,0 +1,89 @@
|
|||
package gosigar
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type ConcreteSigar struct{}
|
||||
|
||||
func (c *ConcreteSigar) CollectCpuStats(collectionInterval time.Duration) (<-chan Cpu, chan<- struct{}) {
|
||||
// samplesCh is buffered to 1 value to immediately return first CPU sample
|
||||
samplesCh := make(chan Cpu, 1)
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
var cpuUsage Cpu
|
||||
|
||||
// Immediately provide non-delta value.
|
||||
// samplesCh is buffered to 1 value, so it will not block.
|
||||
cpuUsage.Get()
|
||||
samplesCh <- cpuUsage
|
||||
|
||||
ticker := time.NewTicker(collectionInterval)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
previousCpuUsage := cpuUsage
|
||||
|
||||
cpuUsage.Get()
|
||||
|
||||
select {
|
||||
case samplesCh <- cpuUsage.Delta(previousCpuUsage):
|
||||
default:
|
||||
// Include default to avoid channel blocking
|
||||
}
|
||||
|
||||
case <-stopCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return samplesCh, stopCh
|
||||
}
|
||||
|
||||
func (c *ConcreteSigar) GetLoadAverage() (LoadAverage, error) {
|
||||
l := LoadAverage{}
|
||||
err := l.Get()
|
||||
return l, err
|
||||
}
|
||||
|
||||
func (c *ConcreteSigar) GetMem() (Mem, error) {
|
||||
m := Mem{}
|
||||
err := m.Get()
|
||||
return m, err
|
||||
}
|
||||
|
||||
func (c *ConcreteSigar) GetSwap() (Swap, error) {
|
||||
s := Swap{}
|
||||
err := s.Get()
|
||||
return s, err
|
||||
}
|
||||
|
||||
func (c *ConcreteSigar) GetHugeTLBPages() (HugeTLBPages, error) {
|
||||
p := HugeTLBPages{}
|
||||
err := p.Get()
|
||||
return p, err
|
||||
}
|
||||
|
||||
func (c *ConcreteSigar) GetFileSystemUsage(path string) (FileSystemUsage, error) {
|
||||
f := FileSystemUsage{}
|
||||
err := f.Get(path)
|
||||
return f, err
|
||||
}
|
||||
|
||||
func (c *ConcreteSigar) GetFDUsage() (FDUsage, error) {
|
||||
fd := FDUsage{}
|
||||
err := fd.Get()
|
||||
return fd, err
|
||||
}
|
||||
|
||||
// GetRusage return the resource usage of the process
|
||||
// Possible params: 0 = RUSAGE_SELF, 1 = RUSAGE_CHILDREN, 2 = RUSAGE_THREAD
|
||||
func (c *ConcreteSigar) GetRusage(who int) (Rusage, error) {
|
||||
r := Rusage{}
|
||||
err := r.Get(who)
|
||||
return r, err
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
module github.com/elastic/gosigar
|
||||
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/stretchr/testify v1.4.0
|
||||
golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0
|
||||
)
|
|
@ -0,0 +1,15 @@
|
|||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0 h1:8H8QZJ30plJyIVj60H3lr8TZGIq2Fh3Cyrs/ZNg1foU=
|
||||
golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
@ -0,0 +1,564 @@
|
|||
// +build aix
|
||||
|
||||
package gosigar
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -L/usr/lib -lperfstat
|
||||
|
||||
#include <libperfstat.h>
|
||||
#include <procinfo.h>
|
||||
#include <unistd.h>
|
||||
#include <utmp.h>
|
||||
#include <sys/mntctl.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/vmount.h>
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var system struct {
|
||||
ticks uint64
|
||||
btime uint64
|
||||
pagesize uint64
|
||||
}
|
||||
|
||||
func init() {
|
||||
// sysconf(_SC_CLK_TCK) returns the number of ticks by second.
|
||||
system.ticks = uint64(C.sysconf(C._SC_CLK_TCK))
|
||||
system.pagesize = uint64(os.Getpagesize())
|
||||
}
|
||||
|
||||
// utmp can't be used by "encoding/binary" if generated by cgo,
|
||||
// some pads will be explicitly missing.
|
||||
type utmp struct {
|
||||
User [256]uint8
|
||||
ID [14]uint8
|
||||
Line [64]uint8
|
||||
XPad1 int16
|
||||
Pid int32
|
||||
Type int16
|
||||
XPad2 int16
|
||||
Time int64
|
||||
Termination int16
|
||||
Exit int16
|
||||
Host [256]uint8
|
||||
XdblWordPad int32
|
||||
XreservedA [2]int32
|
||||
XreservedV [6]int32
|
||||
}
|
||||
|
||||
func bootTime() (uint64, error) {
|
||||
if system.btime != 0 {
|
||||
return system.btime, nil
|
||||
}
|
||||
|
||||
// Get boot time from /etc/utmp
|
||||
file, err := os.Open("/etc/utmp")
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error while opening /etc/utmp: %s", err)
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
for {
|
||||
var utmp utmp
|
||||
if err := binary.Read(file, binary.BigEndian, &utmp); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if utmp.Type == C.BOOT_TIME {
|
||||
system.btime = uint64(utmp.Time)
|
||||
break
|
||||
}
|
||||
}
|
||||
return system.btime, nil
|
||||
}
|
||||
|
||||
func tick2msec(val uint64) uint64 {
|
||||
return val * 1000 / system.ticks
|
||||
}
|
||||
|
||||
// Get returns the list of file systems
|
||||
func (self *FileSystemList) Get() error {
|
||||
var size C.int
|
||||
_, err := C.mntctl(C.MCTL_QUERY, C.sizeof_int, (*C.char)(unsafe.Pointer(&size)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while retrieving file system number: %s", err)
|
||||
}
|
||||
|
||||
buf := make([]byte, size)
|
||||
num, err := C.mntctl(C.MCTL_QUERY, C.ulong(size), (*C.char)(&buf[0]))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while retrieving file system list: %s", err)
|
||||
}
|
||||
|
||||
// Vmount structure has a fixed size area for common data (type,
|
||||
// offsets, etc) and another area with variable length data (devname,
|
||||
// options, etc). These data can be accessed based on the offsets
|
||||
// stored in an array inside the fixed part. They can be retrieve
|
||||
// using index given by C define.
|
||||
vmt2data := func(buf []byte, ent *C.struct_vmount, idx int, baseOff int) []byte {
|
||||
off := int(ent.vmt_data[idx].vmt_off)
|
||||
size := int(ent.vmt_data[idx].vmt_size)
|
||||
return buf[baseOff+off : baseOff+off+size]
|
||||
}
|
||||
|
||||
entOff := 0
|
||||
|
||||
fslist := make([]FileSystem, num)
|
||||
for i := 0; i < int(num); i++ {
|
||||
ent := (*C.struct_vmount)(unsafe.Pointer(&buf[entOff]))
|
||||
fs := &fslist[i]
|
||||
|
||||
// Correspondances taken for /etc/vfs
|
||||
switch ent.vmt_gfstype {
|
||||
case C.MNT_AIX:
|
||||
fs.SysTypeName = "jfs2"
|
||||
case C.MNT_NAMEFS:
|
||||
fs.SysTypeName = "namefs"
|
||||
case C.MNT_NFS:
|
||||
fs.SysTypeName = "nfs"
|
||||
case C.MNT_JFS:
|
||||
fs.SysTypeName = "jfs"
|
||||
case C.MNT_CDROM:
|
||||
fs.SysTypeName = "cdrom"
|
||||
case C.MNT_PROCFS:
|
||||
fs.SysTypeName = "proc"
|
||||
case C.MNT_SFS:
|
||||
fs.SysTypeName = "sfs"
|
||||
case C.MNT_CACHEFS:
|
||||
fs.SysTypeName = "cachefs"
|
||||
case C.MNT_NFS3:
|
||||
fs.SysTypeName = "nfs3"
|
||||
case C.MNT_AUTOFS:
|
||||
fs.SysTypeName = "autofs"
|
||||
case C.MNT_POOLFS:
|
||||
fs.SysTypeName = "poolfs"
|
||||
case C.MNT_UDF:
|
||||
fs.SysTypeName = "udfs"
|
||||
case C.MNT_NFS4:
|
||||
fs.SysTypeName = "nfs4"
|
||||
case C.MNT_CIFS:
|
||||
fs.SysTypeName = "cifs"
|
||||
case C.MNT_PMEMFS:
|
||||
fs.SysTypeName = "pmemfs"
|
||||
case C.MNT_AHAFS:
|
||||
fs.SysTypeName = "ahafs"
|
||||
case C.MNT_STNFS:
|
||||
fs.SysTypeName = "stnfs"
|
||||
default:
|
||||
if ent.vmt_flags&C.MNT_REMOTE != 0 {
|
||||
fs.SysTypeName = "network"
|
||||
} else {
|
||||
fs.SysTypeName = "none"
|
||||
}
|
||||
}
|
||||
|
||||
fs.DirName = convertBytesToString(vmt2data(buf, ent, C.VMT_STUB, entOff))
|
||||
fs.Options = convertBytesToString(vmt2data(buf, ent, C.VMT_ARGS, entOff))
|
||||
devname := convertBytesToString(vmt2data(buf, ent, C.VMT_OBJECT, entOff))
|
||||
if ent.vmt_flags&C.MNT_REMOTE != 0 {
|
||||
hostname := convertBytesToString(vmt2data(buf, ent, C.VMT_OBJECT, entOff))
|
||||
fs.DevName = hostname + ":" + devname
|
||||
} else {
|
||||
fs.DevName = devname
|
||||
}
|
||||
|
||||
entOff += int(ent.vmt_length)
|
||||
}
|
||||
|
||||
self.List = fslist
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns the CPU load average
|
||||
func (self *LoadAverage) Get() error {
|
||||
cpudata := C.perfstat_cpu_total_t{}
|
||||
|
||||
if _, err := C.perfstat_cpu_total(nil, &cpudata, C.sizeof_perfstat_cpu_total_t, 1); err != nil {
|
||||
return fmt.Errorf("perfstat_cpu_total: %s", err)
|
||||
}
|
||||
|
||||
// from libperfstat.h:
|
||||
// "To calculate the load average, divide the numbers by (1<<SBITS).
|
||||
// SBITS is defined in <sys/proc.h>."
|
||||
fixedToFloat64 := func(x uint64) float64 {
|
||||
return float64(x) / (1 << C.SBITS)
|
||||
}
|
||||
self.One = fixedToFloat64(uint64(cpudata.loadavg[0]))
|
||||
self.Five = fixedToFloat64(uint64(cpudata.loadavg[1]))
|
||||
self.Fifteen = fixedToFloat64(uint64(cpudata.loadavg[2]))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns the system uptime
|
||||
func (self *Uptime) Get() error {
|
||||
btime, err := bootTime()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uptime := time.Now().Sub(time.Unix(int64(btime), 0))
|
||||
self.Length = uptime.Seconds()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns the current system memory
|
||||
func (self *Mem) Get() error {
|
||||
meminfo := C.perfstat_memory_total_t{}
|
||||
_, err := C.perfstat_memory_total(nil, &meminfo, C.sizeof_perfstat_memory_total_t, 1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("perfstat_memory_total: %s", err)
|
||||
}
|
||||
|
||||
self.Total = uint64(meminfo.real_total) * system.pagesize
|
||||
self.Free = uint64(meminfo.real_free) * system.pagesize
|
||||
|
||||
kern := uint64(meminfo.numperm) * system.pagesize // number of pages in file cache
|
||||
|
||||
self.Used = self.Total - self.Free
|
||||
self.ActualFree = self.Free + kern
|
||||
self.ActualUsed = self.Used - kern
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns the current system swap memory
|
||||
func (self *Swap) Get() error {
|
||||
ps := C.perfstat_pagingspace_t{}
|
||||
id := C.perfstat_id_t{}
|
||||
|
||||
id.name[0] = 0
|
||||
|
||||
for {
|
||||
// errno can be set during perfstat_pagingspace's call even
|
||||
// if it succeeds. Thus, only check it when the result is -1.
|
||||
if r, err := C.perfstat_pagingspace(&id, &ps, C.sizeof_perfstat_pagingspace_t, 1); r == -1 && err != nil {
|
||||
return fmt.Errorf("perfstat_memory_total: %s", err)
|
||||
}
|
||||
|
||||
if ps.active != 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
// convert MB sizes to bytes
|
||||
self.Total += uint64(ps.mb_size) * 1024 * 1024
|
||||
self.Used += uint64(ps.mb_used) * 1024 * 1024
|
||||
|
||||
if id.name[0] == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
self.Free = self.Total - self.Used
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns information about a CPU
|
||||
func (self *Cpu) Get() error {
|
||||
cpudata := C.perfstat_cpu_total_t{}
|
||||
|
||||
if _, err := C.perfstat_cpu_total(nil, &cpudata, C.sizeof_perfstat_cpu_total_t, 1); err != nil {
|
||||
return fmt.Errorf("perfstat_cpu_total: %s", err)
|
||||
}
|
||||
|
||||
self.User = tick2msec(uint64(cpudata.user))
|
||||
self.Sys = tick2msec(uint64(cpudata.sys))
|
||||
self.Idle = tick2msec(uint64(cpudata.idle))
|
||||
self.Wait = tick2msec(uint64(cpudata.wait))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns the list of CPU used by the system
|
||||
func (self *CpuList) Get() error {
|
||||
cpudata := C.perfstat_cpu_t{}
|
||||
id := C.perfstat_id_t{}
|
||||
id.name[0] = 0
|
||||
|
||||
// Retrieve the number of cpu using perfstat_cpu
|
||||
capacity, err := C.perfstat_cpu(nil, nil, C.sizeof_perfstat_cpu_t, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while retrieving CPU number: %s", err)
|
||||
}
|
||||
list := make([]Cpu, 0, capacity)
|
||||
|
||||
for {
|
||||
if _, err := C.perfstat_cpu(&id, &cpudata, C.sizeof_perfstat_cpu_t, 1); err != nil {
|
||||
return fmt.Errorf("perfstat_cpu: %s", err)
|
||||
}
|
||||
|
||||
cpu := Cpu{}
|
||||
cpu.User = tick2msec(uint64(cpudata.user))
|
||||
cpu.Sys = tick2msec(uint64(cpudata.sys))
|
||||
cpu.Idle = tick2msec(uint64(cpudata.idle))
|
||||
cpu.Wait = tick2msec(uint64(cpudata.wait))
|
||||
|
||||
list = append(list, cpu)
|
||||
|
||||
if id.name[0] == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
self.List = list
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns the list of all active processes
|
||||
func (self *ProcList) Get() error {
|
||||
info := C.struct_procsinfo64{}
|
||||
pid := C.pid_t(0)
|
||||
|
||||
var list []int
|
||||
|
||||
for {
|
||||
// getprocs first argument is a void*
|
||||
num, err := C.getprocs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, nil, 0, &pid, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
list = append(list, int(info.pi_pid))
|
||||
|
||||
if num == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
self.List = list
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns information about a process
|
||||
func (self *ProcState) Get(pid int) error {
|
||||
info := C.struct_procsinfo64{}
|
||||
cpid := C.pid_t(pid)
|
||||
|
||||
num, err := C.getprocs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, nil, 0, &cpid, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if num != 1 {
|
||||
return syscall.ESRCH
|
||||
}
|
||||
|
||||
self.Name = C.GoString(&info.pi_comm[0])
|
||||
self.Ppid = int(info.pi_ppid)
|
||||
self.Pgid = int(info.pi_pgrp)
|
||||
self.Nice = int(info.pi_nice)
|
||||
self.Tty = int(info.pi_ttyd)
|
||||
self.Priority = int(info.pi_pri)
|
||||
|
||||
switch info.pi_state {
|
||||
case C.SACTIVE:
|
||||
self.State = RunStateRun
|
||||
case C.SIDL:
|
||||
self.State = RunStateIdle
|
||||
case C.SSTOP:
|
||||
self.State = RunStateStop
|
||||
case C.SZOMB:
|
||||
self.State = RunStateZombie
|
||||
case C.SSWAP:
|
||||
self.State = RunStateSleep
|
||||
default:
|
||||
self.State = RunStateUnknown
|
||||
}
|
||||
|
||||
// Get process username. Fallback to UID if username is not available.
|
||||
uid := strconv.Itoa(int(info.pi_uid))
|
||||
userID, err := user.LookupId(uid)
|
||||
if err == nil && userID.Username != "" {
|
||||
self.Username = userID.Username
|
||||
} else {
|
||||
self.Username = uid
|
||||
}
|
||||
|
||||
thrinfo := C.struct_thrdsinfo64{}
|
||||
tid := C.tid_t(0)
|
||||
|
||||
if _, err := C.getthrds(cpid, unsafe.Pointer(&thrinfo), C.sizeof_struct_thrdsinfo64, &tid, 1); err != nil {
|
||||
self.Processor = int(thrinfo.ti_affinity)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//Get returns the current memory usage of a process
|
||||
func (self *ProcMem) Get(pid int) error {
|
||||
info := C.struct_procsinfo64{}
|
||||
cpid := C.pid_t(pid)
|
||||
|
||||
num, err := C.getprocs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, nil, 0, &cpid, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if num != 1 {
|
||||
return syscall.ESRCH
|
||||
}
|
||||
|
||||
self.Size = uint64(info.pi_size) * system.pagesize
|
||||
self.Share = uint64(info.pi_sdsize) * system.pagesize
|
||||
self.Resident = uint64(info.pi_drss+info.pi_trss) * system.pagesize
|
||||
|
||||
self.MinorFaults = uint64(info.pi_minflt)
|
||||
self.MajorFaults = uint64(info.pi_majflt)
|
||||
self.PageFaults = self.MinorFaults + self.MajorFaults
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns a process uptime
|
||||
func (self *ProcTime) Get(pid int) error {
|
||||
info := C.struct_procsinfo64{}
|
||||
cpid := C.pid_t(pid)
|
||||
|
||||
num, err := C.getprocs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, nil, 0, &cpid, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if num != 1 {
|
||||
return syscall.ESRCH
|
||||
}
|
||||
|
||||
self.StartTime = uint64(info.pi_start) * 1000
|
||||
self.User = uint64(info.pi_utime) * 1000
|
||||
self.Sys = uint64(info.pi_stime) * 1000
|
||||
self.Total = self.User + self.Sys
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns arguments of a process
|
||||
func (self *ProcArgs) Get(pid int) error {
|
||||
/* If buffer is not large enough, args are truncated */
|
||||
buf := make([]byte, 8192)
|
||||
info := C.struct_procsinfo64{}
|
||||
info.pi_pid = C.pid_t(pid)
|
||||
|
||||
if _, err := C.getargs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, (*C.char)(&buf[0]), 8192); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bbuf := bytes.NewBuffer(buf)
|
||||
|
||||
var args []string
|
||||
|
||||
for {
|
||||
arg, err := bbuf.ReadBytes(0)
|
||||
if err == io.EOF || arg[0] == 0 {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args = append(args, string(chop(arg)))
|
||||
}
|
||||
|
||||
self.List = args
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns the environment of a process
|
||||
func (self *ProcEnv) Get(pid int) error {
|
||||
if self.Vars == nil {
|
||||
self.Vars = map[string]string{}
|
||||
}
|
||||
|
||||
/* If buffer is not large enough, args are truncated */
|
||||
buf := make([]byte, 8192)
|
||||
info := C.struct_procsinfo64{}
|
||||
info.pi_pid = C.pid_t(pid)
|
||||
|
||||
if _, err := C.getevars(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, (*C.char)(&buf[0]), 8192); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bbuf := bytes.NewBuffer(buf)
|
||||
|
||||
delim := []byte{61} // "="
|
||||
|
||||
for {
|
||||
line, err := bbuf.ReadBytes(0)
|
||||
if err == io.EOF || line[0] == 0 {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pair := bytes.SplitN(chop(line), delim, 2)
|
||||
if len(pair) != 2 {
|
||||
return fmt.Errorf("Error reading process environment for PID: %d", pid)
|
||||
}
|
||||
self.Vars[string(pair[0])] = string(pair[1])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns the path of the process executable
|
||||
func (self *ProcExe) Get(pid int) error {
|
||||
/* If buffer is not large enough, args are truncated */
|
||||
buf := make([]byte, 8192)
|
||||
info := C.struct_procsinfo64{}
|
||||
info.pi_pid = C.pid_t(pid)
|
||||
|
||||
if _, err := C.getargs(unsafe.Pointer(&info), C.sizeof_struct_procsinfo64, (*C.char)(&buf[0]), 8192); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bbuf := bytes.NewBuffer(buf)
|
||||
|
||||
// retrieve the first argument
|
||||
cmd, err := bbuf.ReadBytes(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.Name = string(chop(cmd))
|
||||
|
||||
cwd, err := os.Readlink("/proc/" + strconv.Itoa(pid) + "/cwd")
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
self.Cwd = cwd
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns process filesystem usage. Not implimented on AIX.
|
||||
func (*ProcFDUsage) Get(_ int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
// Get returns filesytem usage. Not implimented on AIX.
|
||||
func (*FDUsage) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
// Get returns huge pages info. Not implimented on AIX.
|
||||
func (*HugeTLBPages) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
|
@ -0,0 +1,501 @@
|
|||
// Copyright (c) 2012 VMware, Inc.
|
||||
|
||||
package gosigar
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_host.h>
|
||||
#include <mach/host_info.h>
|
||||
#include <libproc.h>
|
||||
#include <mach/processor_info.h>
|
||||
#include <mach/vm_map.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Get fetches LoadAverage data
|
||||
func (self *LoadAverage) Get() error {
|
||||
avg := []C.double{0, 0, 0}
|
||||
|
||||
C.getloadavg(&avg[0], C.int(len(avg)))
|
||||
|
||||
self.One = float64(avg[0])
|
||||
self.Five = float64(avg[1])
|
||||
self.Fifteen = float64(avg[2])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get fetches memory data
|
||||
func (self *Mem) Get() error {
|
||||
var vmstat C.vm_statistics_data_t
|
||||
|
||||
if err := sysctlbyname("hw.memsize", &self.Total); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := vmInfo(&vmstat); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
kern := uint64(vmstat.inactive_count) << 12
|
||||
self.Free = uint64(vmstat.free_count) << 12
|
||||
|
||||
self.Used = self.Total - self.Free
|
||||
self.ActualFree = self.Free + kern
|
||||
self.ActualUsed = self.Used - kern
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type xswUsage struct {
|
||||
Total, Avail, Used uint64
|
||||
}
|
||||
|
||||
// Get fetches swap data
|
||||
func (self *Swap) Get() error {
|
||||
swUsage := xswUsage{}
|
||||
|
||||
if err := sysctlbyname("vm.swapusage", &swUsage); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Total = swUsage.Total
|
||||
self.Used = swUsage.Used
|
||||
self.Free = swUsage.Avail
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get fetches hugepages data
|
||||
func (self *HugeTLBPages) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
// Get fetches CPU data
|
||||
func (self *Cpu) Get() error {
|
||||
var count C.mach_msg_type_number_t = C.HOST_CPU_LOAD_INFO_COUNT
|
||||
var cpuload C.host_cpu_load_info_data_t
|
||||
|
||||
status := C.host_statistics(C.host_t(C.mach_host_self()),
|
||||
C.HOST_CPU_LOAD_INFO,
|
||||
C.host_info_t(unsafe.Pointer(&cpuload)),
|
||||
&count)
|
||||
|
||||
if status != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("host_statistics error=%d", status)
|
||||
}
|
||||
|
||||
self.User = uint64(cpuload.cpu_ticks[C.CPU_STATE_USER])
|
||||
self.Sys = uint64(cpuload.cpu_ticks[C.CPU_STATE_SYSTEM])
|
||||
self.Idle = uint64(cpuload.cpu_ticks[C.CPU_STATE_IDLE])
|
||||
self.Nice = uint64(cpuload.cpu_ticks[C.CPU_STATE_NICE])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get fetches CPU list data
|
||||
func (self *CpuList) Get() error {
|
||||
var count C.mach_msg_type_number_t
|
||||
var cpuload *C.processor_cpu_load_info_data_t
|
||||
var ncpu C.natural_t
|
||||
|
||||
status := C.host_processor_info(C.host_t(C.mach_host_self()),
|
||||
C.PROCESSOR_CPU_LOAD_INFO,
|
||||
&ncpu,
|
||||
(*C.processor_info_array_t)(unsafe.Pointer(&cpuload)),
|
||||
&count)
|
||||
|
||||
if status != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("host_processor_info error=%d", status)
|
||||
}
|
||||
|
||||
// jump through some cgo casting hoops and ensure we properly free
|
||||
// the memory that cpuload points to
|
||||
target := C.vm_map_t(C.mach_task_self_)
|
||||
address := C.vm_address_t(uintptr(unsafe.Pointer(cpuload)))
|
||||
defer C.vm_deallocate(target, address, C.vm_size_t(ncpu))
|
||||
|
||||
// the body of struct processor_cpu_load_info
|
||||
// aka processor_cpu_load_info_data_t
|
||||
var cpuTicks [C.CPU_STATE_MAX]uint32
|
||||
|
||||
// copy the cpuload array to a []byte buffer
|
||||
// where we can binary.Read the data
|
||||
size := int(ncpu) * binary.Size(cpuTicks)
|
||||
buf := C.GoBytes(unsafe.Pointer(cpuload), C.int(size))
|
||||
|
||||
bbuf := bytes.NewBuffer(buf)
|
||||
|
||||
self.List = make([]Cpu, 0, ncpu)
|
||||
|
||||
for i := 0; i < int(ncpu); i++ {
|
||||
cpu := Cpu{}
|
||||
|
||||
err := binary.Read(bbuf, binary.LittleEndian, &cpuTicks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cpu.User = uint64(cpuTicks[C.CPU_STATE_USER])
|
||||
cpu.Sys = uint64(cpuTicks[C.CPU_STATE_SYSTEM])
|
||||
cpu.Idle = uint64(cpuTicks[C.CPU_STATE_IDLE])
|
||||
cpu.Nice = uint64(cpuTicks[C.CPU_STATE_NICE])
|
||||
|
||||
self.List = append(self.List, cpu)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns FD usage data
|
||||
func (self *FDUsage) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
// Get returns filesystem data
|
||||
func (self *FileSystemList) Get() error {
|
||||
num, err := syscall.Getfsstat(nil, C.MNT_NOWAIT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf := make([]syscall.Statfs_t, num)
|
||||
|
||||
_, err = syscall.Getfsstat(buf, C.MNT_NOWAIT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fslist := make([]FileSystem, 0, num)
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
fs := FileSystem{}
|
||||
fs.DirName = byteListToString(buf[i].Mntonname[:])
|
||||
fs.DevName = byteListToString(buf[i].Mntfromname[:])
|
||||
fs.SysTypeName = byteListToString(buf[i].Fstypename[:])
|
||||
|
||||
fslist = append(fslist, fs)
|
||||
}
|
||||
|
||||
self.List = fslist
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Get returns a process list
|
||||
func (self *ProcList) Get() error {
|
||||
n := C.proc_listpids(C.PROC_ALL_PIDS, 0, nil, 0)
|
||||
if n <= 0 {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
buf := make([]byte, n)
|
||||
n = C.proc_listpids(C.PROC_ALL_PIDS, 0, unsafe.Pointer(&buf[0]), n)
|
||||
if n <= 0 {
|
||||
return syscall.ENOMEM
|
||||
}
|
||||
|
||||
var pid int32
|
||||
num := int(n) / binary.Size(pid)
|
||||
list := make([]int, 0, num)
|
||||
bbuf := bytes.NewBuffer(buf)
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
if err := binary.Read(bbuf, binary.LittleEndian, &pid); err != nil {
|
||||
return err
|
||||
}
|
||||
if pid == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
list = append(list, int(pid))
|
||||
}
|
||||
|
||||
self.List = list
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns process state data
|
||||
func (self *ProcState) Get(pid int) error {
|
||||
info := C.struct_proc_taskallinfo{}
|
||||
|
||||
if err := taskInfo(pid, &info); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Name = C.GoString(&info.pbsd.pbi_comm[0])
|
||||
|
||||
switch info.pbsd.pbi_status {
|
||||
case C.SIDL:
|
||||
self.State = RunStateIdle
|
||||
case C.SRUN:
|
||||
self.State = RunStateRun
|
||||
case C.SSLEEP:
|
||||
self.State = RunStateSleep
|
||||
case C.SSTOP:
|
||||
self.State = RunStateStop
|
||||
case C.SZOMB:
|
||||
self.State = RunStateZombie
|
||||
default:
|
||||
self.State = RunStateUnknown
|
||||
}
|
||||
|
||||
self.Ppid = int(info.pbsd.pbi_ppid)
|
||||
|
||||
self.Pgid = int(info.pbsd.pbi_pgid)
|
||||
|
||||
self.Tty = int(info.pbsd.e_tdev)
|
||||
|
||||
self.Priority = int(info.ptinfo.pti_priority)
|
||||
|
||||
self.Nice = int(info.pbsd.pbi_nice)
|
||||
|
||||
// Get process username. Fallback to UID if username is not available.
|
||||
uid := strconv.Itoa(int(info.pbsd.pbi_uid))
|
||||
user, err := user.LookupId(uid)
|
||||
if err == nil && user.Username != "" {
|
||||
self.Username = user.Username
|
||||
} else {
|
||||
self.Username = uid
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns process memory data
|
||||
func (self *ProcMem) Get(pid int) error {
|
||||
info := C.struct_proc_taskallinfo{}
|
||||
|
||||
if err := taskInfo(pid, &info); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Size = uint64(info.ptinfo.pti_virtual_size)
|
||||
self.Resident = uint64(info.ptinfo.pti_resident_size)
|
||||
self.PageFaults = uint64(info.ptinfo.pti_faults)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns process time data
|
||||
func (self *ProcTime) Get(pid int) error {
|
||||
info := C.struct_proc_taskallinfo{}
|
||||
|
||||
if err := taskInfo(pid, &info); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.User =
|
||||
uint64(info.ptinfo.pti_total_user) / uint64(time.Millisecond)
|
||||
|
||||
self.Sys =
|
||||
uint64(info.ptinfo.pti_total_system) / uint64(time.Millisecond)
|
||||
|
||||
self.Total = self.User + self.Sys
|
||||
|
||||
self.StartTime = (uint64(info.pbsd.pbi_start_tvsec) * 1000) +
|
||||
(uint64(info.pbsd.pbi_start_tvusec) / 1000)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns process arg data
|
||||
func (self *ProcArgs) Get(pid int) error {
|
||||
var args []string
|
||||
|
||||
argv := func(arg string) {
|
||||
args = append(args, arg)
|
||||
}
|
||||
|
||||
err := kernProcargs(pid, nil, argv, nil)
|
||||
|
||||
self.List = args
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Get returns process environment data
|
||||
func (self *ProcEnv) Get(pid int) error {
|
||||
if self.Vars == nil {
|
||||
self.Vars = map[string]string{}
|
||||
}
|
||||
|
||||
env := func(k, v string) {
|
||||
self.Vars[k] = v
|
||||
}
|
||||
|
||||
return kernProcargs(pid, nil, nil, env)
|
||||
}
|
||||
|
||||
// Get returns process exec data
|
||||
func (self *ProcExe) Get(pid int) error {
|
||||
exe := func(arg string) {
|
||||
self.Name = arg
|
||||
}
|
||||
|
||||
return kernProcargs(pid, exe, nil, nil)
|
||||
}
|
||||
|
||||
// Get returns process file usage
|
||||
func (self *ProcFDUsage) Get(pid int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
// kernProcargs is a wrapper around sysctl KERN_PROCARGS2
|
||||
// callbacks params are optional,
|
||||
// up to the caller as to which pieces of data they want
|
||||
func kernProcargs(pid int,
|
||||
exe func(string),
|
||||
argv func(string),
|
||||
env func(string, string)) error {
|
||||
|
||||
mib := []C.int{C.CTL_KERN, C.KERN_PROCARGS2, C.int(pid)}
|
||||
argmax := uintptr(C.ARG_MAX)
|
||||
buf := make([]byte, argmax)
|
||||
err := sysctl(mib, &buf[0], &argmax, nil, 0)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
bbuf := bytes.NewBuffer(buf)
|
||||
bbuf.Truncate(int(argmax))
|
||||
|
||||
var argc int32
|
||||
binary.Read(bbuf, binary.LittleEndian, &argc)
|
||||
|
||||
path, err := bbuf.ReadBytes(0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading the argv[0]: %v", err)
|
||||
}
|
||||
if exe != nil {
|
||||
exe(string(chop(path)))
|
||||
}
|
||||
|
||||
// skip trailing \0's
|
||||
for {
|
||||
c, err := bbuf.ReadByte()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error skipping nils: %v", err)
|
||||
}
|
||||
if c != 0 {
|
||||
bbuf.UnreadByte()
|
||||
break // start of argv[0]
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < int(argc); i++ {
|
||||
arg, err := bbuf.ReadBytes(0)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading args: %v", err)
|
||||
}
|
||||
if argv != nil {
|
||||
argv(string(chop(arg)))
|
||||
}
|
||||
}
|
||||
|
||||
if env == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
delim := []byte{61} // "="
|
||||
|
||||
for {
|
||||
line, err := bbuf.ReadBytes(0)
|
||||
if err == io.EOF || line[0] == 0 {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading args: %v", err)
|
||||
}
|
||||
pair := bytes.SplitN(chop(line), delim, 2)
|
||||
|
||||
if len(pair) != 2 {
|
||||
return fmt.Errorf("Error reading process information for PID: %d", pid)
|
||||
}
|
||||
|
||||
env(string(pair[0]), string(pair[1]))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// XXX copied from zsyscall_darwin_amd64.go
|
||||
func sysctl(mib []C.int, old *byte, oldlen *uintptr,
|
||||
new *byte, newlen uintptr) (err error) {
|
||||
var p0 unsafe.Pointer
|
||||
p0 = unsafe.Pointer(&mib[0])
|
||||
_, _, e1 := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(p0),
|
||||
uintptr(len(mib)),
|
||||
uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)),
|
||||
uintptr(unsafe.Pointer(new)), uintptr(newlen))
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func vmInfo(vmstat *C.vm_statistics_data_t) error {
|
||||
var count C.mach_msg_type_number_t = C.HOST_VM_INFO_COUNT
|
||||
|
||||
status := C.host_statistics(
|
||||
C.host_t(C.mach_host_self()),
|
||||
C.HOST_VM_INFO,
|
||||
C.host_info_t(unsafe.Pointer(vmstat)),
|
||||
&count)
|
||||
|
||||
if status != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("host_statistics=%d", status)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// generic Sysctl buffer unmarshalling
|
||||
func sysctlbyname(name string, data interface{}) (err error) {
|
||||
val, err := syscall.Sysctl(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf := []byte(val)
|
||||
|
||||
switch v := data.(type) {
|
||||
case *uint64:
|
||||
*v = *(*uint64)(unsafe.Pointer(&buf[0]))
|
||||
return
|
||||
}
|
||||
|
||||
bbuf := bytes.NewBuffer([]byte(val))
|
||||
return binary.Read(bbuf, binary.LittleEndian, data)
|
||||
}
|
||||
|
||||
func taskInfo(pid int, info *C.struct_proc_taskallinfo) error {
|
||||
size := C.int(unsafe.Sizeof(*info))
|
||||
ptr := unsafe.Pointer(info)
|
||||
|
||||
n := C.proc_pidinfo(C.int(pid), C.PROC_PIDTASKALLINFO, 0, ptr, size)
|
||||
if n != size {
|
||||
return fmt.Errorf("Could not read process info for pid %d", pid)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// +build !386
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (self *Uptime) Get() error {
|
||||
tv := syscall.Timeval32{}
|
||||
|
||||
if err := sysctlbyname("kern.boottime", &tv); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Length = time.Since(time.Unix(int64(tv.Sec), int64(tv.Usec)*1000)).Seconds()
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package gosigar
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (self *Uptime) Get() error {
|
||||
tv := syscall.Timeval{}
|
||||
|
||||
if err := sysctlbyname("kern.boottime", &tv); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Length = time.Since(time.Unix(int64(tv.Sec), int64(tv.Usec)*1000)).Seconds()
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
// Copyright (c) 2012 VMware, Inc.
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Go version of apr_strfsize
|
||||
func FormatSize(size uint64) string {
|
||||
ord := []string{"K", "M", "G", "T", "P", "E"}
|
||||
o := 0
|
||||
buf := new(bytes.Buffer)
|
||||
w := bufio.NewWriter(buf)
|
||||
|
||||
if size < 973 {
|
||||
fmt.Fprintf(w, "%3d ", size)
|
||||
w.Flush()
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
for {
|
||||
remain := size & 1023
|
||||
size >>= 10
|
||||
|
||||
if size >= 973 {
|
||||
o++
|
||||
continue
|
||||
}
|
||||
|
||||
if size < 9 || (size == 9 && remain < 973) {
|
||||
remain = ((remain * 5) + 256) / 512
|
||||
if remain >= 10 {
|
||||
size++
|
||||
remain = 0
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%d.%d%s", size, remain, ord[o])
|
||||
break
|
||||
}
|
||||
|
||||
if remain >= 512 {
|
||||
size++
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%3d%s", size, ord[o])
|
||||
break
|
||||
}
|
||||
|
||||
w.Flush()
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func FormatPercent(percent float64) string {
|
||||
return strconv.FormatFloat(percent, 'f', -1, 64) + "%"
|
||||
}
|
||||
|
||||
func (self *FileSystemUsage) UsePercent() float64 {
|
||||
b_used := (self.Total - self.Free) / 1024
|
||||
b_avail := self.Avail / 1024
|
||||
utotal := b_used + b_avail
|
||||
used := b_used
|
||||
|
||||
if utotal != 0 {
|
||||
u100 := used * 100
|
||||
pct := u100 / utotal
|
||||
if u100%utotal != 0 {
|
||||
pct += 1
|
||||
}
|
||||
return (float64(pct) / float64(100)) * 100.0
|
||||
}
|
||||
|
||||
return 0.0
|
||||
}
|
||||
|
||||
func (self *Uptime) Format() string {
|
||||
buf := new(bytes.Buffer)
|
||||
w := bufio.NewWriter(buf)
|
||||
uptime := uint64(self.Length)
|
||||
|
||||
days := uptime / (60 * 60 * 24)
|
||||
|
||||
if days != 0 {
|
||||
s := ""
|
||||
if days > 1 {
|
||||
s = "s"
|
||||
}
|
||||
fmt.Fprintf(w, "%d day%s, ", days, s)
|
||||
}
|
||||
|
||||
minutes := uptime / 60
|
||||
hours := minutes / 60
|
||||
hours %= 24
|
||||
minutes %= 60
|
||||
|
||||
fmt.Fprintf(w, "%2d:%02d", hours, minutes)
|
||||
|
||||
w.Flush()
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (self *ProcTime) FormatStartTime() string {
|
||||
if self.StartTime == 0 {
|
||||
return "00:00"
|
||||
}
|
||||
start := time.Unix(int64(self.StartTime)/1000, 0)
|
||||
format := "Jan02"
|
||||
if time.Since(start).Seconds() < (60 * 60 * 24) {
|
||||
format = "15:04"
|
||||
}
|
||||
return start.Format(format)
|
||||
}
|
||||
|
||||
func (self *ProcTime) FormatTotal() string {
|
||||
t := self.Total / 1000
|
||||
ss := t % 60
|
||||
t /= 60
|
||||
mm := t % 60
|
||||
t /= 60
|
||||
hh := t % 24
|
||||
return fmt.Sprintf("%02d:%02d:%02d", hh, mm, ss)
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
// Copied and modified from sigar_linux.go.
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
/*
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/ucred.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func init() {
|
||||
system.ticks = uint64(C.sysconf(C._SC_CLK_TCK))
|
||||
|
||||
Procd = "/compat/linux/proc"
|
||||
|
||||
getLinuxBootTime()
|
||||
}
|
||||
|
||||
func getMountTableFileName() string {
|
||||
return Procd + "/mtab"
|
||||
}
|
||||
|
||||
func (self *Uptime) Get() error {
|
||||
ts := C.struct_timespec{}
|
||||
|
||||
if _, err := C.clock_gettime(C.CLOCK_UPTIME, &ts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Length = float64(ts.tv_sec) + 1e-9*float64(ts.tv_nsec)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *FDUsage) Get() error {
|
||||
val := C.uint32_t(0)
|
||||
sc := C.size_t(4)
|
||||
|
||||
name := C.CString("kern.openfiles")
|
||||
_, err := C.sysctlbyname(name, unsafe.Pointer(&val), &sc, nil, 0)
|
||||
C.free(unsafe.Pointer(name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.Open = uint64(val)
|
||||
|
||||
name = C.CString("kern.maxfiles")
|
||||
_, err = C.sysctlbyname(name, unsafe.Pointer(&val), &sc, nil, 0)
|
||||
C.free(unsafe.Pointer(name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.Max = uint64(val)
|
||||
|
||||
self.Unused = self.Max - self.Open
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcFDUsage) Get(pid int) error {
|
||||
err := readFile("/proc/"+strconv.Itoa(pid)+"/rlimit", func(line string) bool {
|
||||
if strings.HasPrefix(line, "nofile") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 3 {
|
||||
self.SoftLimit, _ = strconv.ParseUint(fields[1], 10, 64)
|
||||
self.HardLimit, _ = strconv.ParseUint(fields[2], 10, 64)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// linprocfs only provides this information for this process (self).
|
||||
fds, err := ioutil.ReadDir(procFileName(pid, "fd"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.Open = uint64(len(fds))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *HugeTLBPages) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func parseCpuStat(self *Cpu, line string) error {
|
||||
fields := strings.Fields(line)
|
||||
|
||||
self.User, _ = strtoull(fields[1])
|
||||
self.Nice, _ = strtoull(fields[2])
|
||||
self.Sys, _ = strtoull(fields[3])
|
||||
self.Idle, _ = strtoull(fields[4])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Mem) Get() error {
|
||||
val := C.uint32_t(0)
|
||||
sc := C.size_t(4)
|
||||
|
||||
name := C.CString("vm.stats.vm.v_page_count")
|
||||
_, err := C.sysctlbyname(name, unsafe.Pointer(&val), &sc, nil, 0)
|
||||
C.free(unsafe.Pointer(name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pagecount := uint64(val)
|
||||
|
||||
name = C.CString("vm.stats.vm.v_page_size")
|
||||
_, err = C.sysctlbyname(name, unsafe.Pointer(&val), &sc, nil, 0)
|
||||
C.free(unsafe.Pointer(name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pagesize := uint64(val)
|
||||
|
||||
name = C.CString("vm.stats.vm.v_free_count")
|
||||
_, err = C.sysctlbyname(name, unsafe.Pointer(&val), &sc, nil, 0)
|
||||
C.free(unsafe.Pointer(name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.Free = uint64(val) * pagesize
|
||||
|
||||
name = C.CString("vm.stats.vm.v_inactive_count")
|
||||
_, err = C.sysctlbyname(name, unsafe.Pointer(&val), &sc, nil, 0)
|
||||
C.free(unsafe.Pointer(name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kern := uint64(val)
|
||||
|
||||
self.Total = uint64(pagecount * pagesize)
|
||||
|
||||
self.Used = self.Total - self.Free
|
||||
self.ActualFree = self.Free + (kern * pagesize)
|
||||
self.ActualUsed = self.Used - (kern * pagesize)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
package gosigar
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrNotImplemented is returned when a particular statistic isn't implemented on the host OS.
|
||||
type ErrNotImplemented struct {
|
||||
OS string
|
||||
}
|
||||
|
||||
func (e ErrNotImplemented) Error() string {
|
||||
return "not implemented on " + e.OS
|
||||
}
|
||||
|
||||
// IsNotImplemented returns true if the error is ErrNotImplemented
|
||||
func IsNotImplemented(err error) bool {
|
||||
switch err.(type) {
|
||||
case ErrNotImplemented, *ErrNotImplemented:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Sigar is an interface for gathering system host stats
|
||||
type Sigar interface {
|
||||
CollectCpuStats(collectionInterval time.Duration) (<-chan Cpu, chan<- struct{})
|
||||
GetLoadAverage() (LoadAverage, error)
|
||||
GetMem() (Mem, error)
|
||||
GetSwap() (Swap, error)
|
||||
GetHugeTLBPages(HugeTLBPages, error)
|
||||
GetFileSystemUsage(string) (FileSystemUsage, error)
|
||||
GetFDUsage() (FDUsage, error)
|
||||
GetRusage(who int) (Rusage, error)
|
||||
}
|
||||
|
||||
// Cpu contains CPU time stats
|
||||
type Cpu struct {
|
||||
User uint64
|
||||
Nice uint64
|
||||
Sys uint64
|
||||
Idle uint64
|
||||
Wait uint64
|
||||
Irq uint64
|
||||
SoftIrq uint64
|
||||
Stolen uint64
|
||||
}
|
||||
|
||||
// Total returns total CPU time
|
||||
func (cpu *Cpu) Total() uint64 {
|
||||
return cpu.User + cpu.Nice + cpu.Sys + cpu.Idle +
|
||||
cpu.Wait + cpu.Irq + cpu.SoftIrq + cpu.Stolen
|
||||
}
|
||||
|
||||
// Delta returns the difference between two Cpu stat objects
|
||||
func (cpu Cpu) Delta(other Cpu) Cpu {
|
||||
return Cpu{
|
||||
User: cpu.User - other.User,
|
||||
Nice: cpu.Nice - other.Nice,
|
||||
Sys: cpu.Sys - other.Sys,
|
||||
Idle: cpu.Idle - other.Idle,
|
||||
Wait: cpu.Wait - other.Wait,
|
||||
Irq: cpu.Irq - other.Irq,
|
||||
SoftIrq: cpu.SoftIrq - other.SoftIrq,
|
||||
Stolen: cpu.Stolen - other.Stolen,
|
||||
}
|
||||
}
|
||||
|
||||
// LoadAverage reports standard load averages
|
||||
type LoadAverage struct {
|
||||
One, Five, Fifteen float64
|
||||
}
|
||||
|
||||
// Uptime reports system uptime
|
||||
type Uptime struct {
|
||||
Length float64
|
||||
}
|
||||
|
||||
// Mem contains host memory stats
|
||||
type Mem struct {
|
||||
Total uint64
|
||||
Used uint64
|
||||
Free uint64
|
||||
Cached uint64
|
||||
ActualFree uint64
|
||||
ActualUsed uint64
|
||||
}
|
||||
|
||||
// Swap contains stats on swap space
|
||||
type Swap struct {
|
||||
Total uint64
|
||||
Used uint64
|
||||
Free uint64
|
||||
}
|
||||
|
||||
// HugeTLBPages contains HugePages stats
|
||||
type HugeTLBPages struct {
|
||||
Total uint64
|
||||
Free uint64
|
||||
Reserved uint64
|
||||
Surplus uint64
|
||||
DefaultSize uint64
|
||||
TotalAllocatedSize uint64
|
||||
}
|
||||
|
||||
// CpuList contains a list of CPUs on the host system
|
||||
type CpuList struct {
|
||||
List []Cpu
|
||||
}
|
||||
|
||||
// FDUsage contains stats on filesystem usage
|
||||
type FDUsage struct {
|
||||
Open uint64
|
||||
Unused uint64
|
||||
Max uint64
|
||||
}
|
||||
|
||||
// FileSystem contains basic information about a given mounted filesystem
|
||||
type FileSystem struct {
|
||||
DirName string
|
||||
DevName string
|
||||
TypeName string
|
||||
SysTypeName string
|
||||
Options string
|
||||
Flags uint32
|
||||
}
|
||||
|
||||
// FileSystemList gets a list of mounted filesystems
|
||||
type FileSystemList struct {
|
||||
List []FileSystem
|
||||
}
|
||||
|
||||
// FileSystemUsage contains basic stats for the specified filesystem
|
||||
type FileSystemUsage struct {
|
||||
Total uint64
|
||||
Used uint64
|
||||
Free uint64
|
||||
Avail uint64
|
||||
Files uint64
|
||||
FreeFiles uint64
|
||||
}
|
||||
|
||||
// ProcList contains a list of processes found on the host system
|
||||
type ProcList struct {
|
||||
List []int
|
||||
}
|
||||
|
||||
// RunState is a byte-long code used to specify the current runtime state of a process
|
||||
type RunState byte
|
||||
|
||||
const (
|
||||
// RunStateSleep corresponds to a sleep state
|
||||
RunStateSleep = 'S'
|
||||
// RunStateRun corresponds to a running state
|
||||
RunStateRun = 'R'
|
||||
// RunStateStop corresponds to a stopped state
|
||||
RunStateStop = 'T'
|
||||
// RunStateZombie marks a zombie process
|
||||
RunStateZombie = 'Z'
|
||||
// RunStateIdle corresponds to an idle state
|
||||
RunStateIdle = 'D'
|
||||
// RunStateUnknown corresponds to a process in an unknown state
|
||||
RunStateUnknown = '?'
|
||||
)
|
||||
|
||||
// ProcState contains basic metadata and process ownership info for the specified process
|
||||
type ProcState struct {
|
||||
Name string
|
||||
Username string
|
||||
State RunState
|
||||
Ppid int
|
||||
Pgid int
|
||||
Tty int
|
||||
Priority int
|
||||
Nice int
|
||||
Processor int
|
||||
}
|
||||
|
||||
// ProcMem contains memory statistics for a specified process
|
||||
type ProcMem struct {
|
||||
Size uint64
|
||||
Resident uint64
|
||||
Share uint64
|
||||
MinorFaults uint64
|
||||
MajorFaults uint64
|
||||
PageFaults uint64
|
||||
}
|
||||
|
||||
// ProcTime contains run time statistics for a specified process
|
||||
type ProcTime struct {
|
||||
StartTime uint64
|
||||
User uint64
|
||||
Sys uint64
|
||||
Total uint64
|
||||
}
|
||||
|
||||
// ProcArgs contains a list of args for a specified process
|
||||
type ProcArgs struct {
|
||||
List []string
|
||||
}
|
||||
|
||||
// ProcEnv contains a map of environment variables for specified process
|
||||
type ProcEnv struct {
|
||||
Vars map[string]string
|
||||
}
|
||||
|
||||
// ProcExe contains basic data about a specified process
|
||||
type ProcExe struct {
|
||||
Name string
|
||||
Cwd string
|
||||
Root string
|
||||
}
|
||||
|
||||
// ProcFDUsage contains data on file limits and usage
|
||||
type ProcFDUsage struct {
|
||||
Open uint64
|
||||
SoftLimit uint64
|
||||
HardLimit uint64
|
||||
}
|
||||
|
||||
// Rusage contains data on resource usage for a specified process
|
||||
type Rusage struct {
|
||||
Utime time.Duration
|
||||
Stime time.Duration
|
||||
Maxrss int64
|
||||
Ixrss int64
|
||||
Idrss int64
|
||||
Isrss int64
|
||||
Minflt int64
|
||||
Majflt int64
|
||||
Nswap int64
|
||||
Inblock int64
|
||||
Oublock int64
|
||||
Msgsnd int64
|
||||
Msgrcv int64
|
||||
Nsignals int64
|
||||
Nvcsw int64
|
||||
Nivcsw int64
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
// Copyright (c) 2012 VMware, Inc.
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
system.ticks = 100 // C.sysconf(C._SC_CLK_TCK)
|
||||
|
||||
Procd = "/proc"
|
||||
|
||||
getLinuxBootTime()
|
||||
}
|
||||
|
||||
func getMountTableFileName() string {
|
||||
return "/etc/mtab"
|
||||
}
|
||||
|
||||
// Get returns uptime data
|
||||
func (self *Uptime) Get() error {
|
||||
sysinfo := syscall.Sysinfo_t{}
|
||||
|
||||
if err := syscall.Sysinfo(&sysinfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Length = float64(sysinfo.Uptime)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns FD usage data
|
||||
func (self *FDUsage) Get() error {
|
||||
return readFile(Procd+"/sys/fs/file-nr", func(line string) bool {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 3 {
|
||||
self.Open, _ = strconv.ParseUint(fields[0], 10, 64)
|
||||
self.Unused, _ = strconv.ParseUint(fields[1], 10, 64)
|
||||
self.Max, _ = strconv.ParseUint(fields[2], 10, 64)
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
// Get returns hugepages data
|
||||
func (self *HugeTLBPages) Get() error {
|
||||
table, err := parseMeminfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Total, _ = table["HugePages_Total"]
|
||||
self.Free, _ = table["HugePages_Free"]
|
||||
self.Reserved, _ = table["HugePages_Rsvd"]
|
||||
self.Surplus, _ = table["HugePages_Surp"]
|
||||
self.DefaultSize, _ = table["Hugepagesize"]
|
||||
|
||||
if totalSize, found := table["Hugetlb"]; found {
|
||||
self.TotalAllocatedSize = totalSize
|
||||
} else {
|
||||
// If Hugetlb is not present, or huge pages of different sizes
|
||||
// are used, this figure can be unaccurate.
|
||||
// TODO (jsoriano): Extract information from /sys/kernel/mm/hugepages too
|
||||
self.TotalAllocatedSize = (self.Total - self.Free + self.Reserved) * self.DefaultSize
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns process FD usage
|
||||
func (self *ProcFDUsage) Get(pid int) error {
|
||||
err := readFile(procFileName(pid, "limits"), func(line string) bool {
|
||||
if strings.HasPrefix(line, "Max open files") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 6 {
|
||||
self.SoftLimit, _ = strconv.ParseUint(fields[3], 10, 64)
|
||||
self.HardLimit, _ = strconv.ParseUint(fields[4], 10, 64)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fds, err := ioutil.ReadDir(procFileName(pid, "fd"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.Open = uint64(len(fds))
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseCpuStat(self *Cpu, line string) error {
|
||||
fields := strings.Fields(line)
|
||||
|
||||
self.User, _ = strtoull(fields[1])
|
||||
self.Nice, _ = strtoull(fields[2])
|
||||
self.Sys, _ = strtoull(fields[3])
|
||||
self.Idle, _ = strtoull(fields[4])
|
||||
self.Wait, _ = strtoull(fields[5])
|
||||
self.Irq, _ = strtoull(fields[6])
|
||||
self.SoftIrq, _ = strtoull(fields[7])
|
||||
self.Stolen, _ = strtoull(fields[8])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns memory data
|
||||
func (self *Mem) Get() error {
|
||||
|
||||
table, err := parseMeminfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Total, _ = table["MemTotal"]
|
||||
self.Free, _ = table["MemFree"]
|
||||
buffers, _ := table["Buffers"]
|
||||
self.Cached, _ = table["Cached"]
|
||||
|
||||
if available, ok := table["MemAvailable"]; ok {
|
||||
// MemAvailable is in /proc/meminfo (kernel 3.14+)
|
||||
self.ActualFree = available
|
||||
} else {
|
||||
self.ActualFree = self.Free + buffers + self.Cached
|
||||
}
|
||||
|
||||
self.Used = self.Total - self.Free
|
||||
self.ActualUsed = self.Total - self.ActualFree
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,457 @@
|
|||
// Copyright (c) 2012 VMware, Inc.
|
||||
|
||||
// +build freebsd linux
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var system struct {
|
||||
ticks uint64
|
||||
btime uint64
|
||||
}
|
||||
|
||||
var Procd string
|
||||
|
||||
func getLinuxBootTime() {
|
||||
// grab system boot time
|
||||
readFile(Procd+"/stat", func(line string) bool {
|
||||
if strings.HasPrefix(line, "btime") {
|
||||
system.btime, _ = strtoull(line[6:])
|
||||
return false // stop reading
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (self *LoadAverage) Get() error {
|
||||
line, err := ioutil.ReadFile(Procd + "/loadavg")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
fields := strings.Fields(string(line))
|
||||
|
||||
self.One, _ = strconv.ParseFloat(fields[0], 64)
|
||||
self.Five, _ = strconv.ParseFloat(fields[1], 64)
|
||||
self.Fifteen, _ = strconv.ParseFloat(fields[2], 64)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Swap) Get() error {
|
||||
|
||||
table, err := parseMeminfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.Total, _ = table["SwapTotal"]
|
||||
self.Free, _ = table["SwapFree"]
|
||||
|
||||
self.Used = self.Total - self.Free
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Cpu) Get() error {
|
||||
return readFile(Procd+"/stat", func(line string) bool {
|
||||
if len(line) > 4 && line[0:4] == "cpu " {
|
||||
parseCpuStat(self, line)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func (self *CpuList) Get() error {
|
||||
capacity := len(self.List)
|
||||
if capacity == 0 {
|
||||
capacity = 4
|
||||
}
|
||||
list := make([]Cpu, 0, capacity)
|
||||
|
||||
err := readFile(Procd+"/stat", func(line string) bool {
|
||||
if len(line) > 3 && line[0:3] == "cpu" && line[3] != ' ' {
|
||||
cpu := Cpu{}
|
||||
parseCpuStat(&cpu, line)
|
||||
list = append(list, cpu)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
self.List = list
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *FileSystemList) Get() error {
|
||||
capacity := len(self.List)
|
||||
if capacity == 0 {
|
||||
capacity = 10
|
||||
}
|
||||
fslist := make([]FileSystem, 0, capacity)
|
||||
|
||||
err := readFile(getMountTableFileName(), func(line string) bool {
|
||||
fields := strings.Fields(line)
|
||||
|
||||
fs := FileSystem{}
|
||||
fs.DevName = fields[0]
|
||||
fs.DirName = fields[1]
|
||||
fs.SysTypeName = fields[2]
|
||||
fs.Options = fields[3]
|
||||
|
||||
fslist = append(fslist, fs)
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
self.List = fslist
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *ProcList) Get() error {
|
||||
dir, err := os.Open(Procd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
const readAllDirnames = -1 // see os.File.Readdirnames doc
|
||||
|
||||
names, err := dir.Readdirnames(readAllDirnames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
capacity := len(names)
|
||||
list := make([]int, 0, capacity)
|
||||
|
||||
for _, name := range names {
|
||||
if name[0] < '0' || name[0] > '9' {
|
||||
continue
|
||||
}
|
||||
pid, err := strconv.Atoi(name)
|
||||
if err == nil {
|
||||
list = append(list, pid)
|
||||
}
|
||||
}
|
||||
|
||||
self.List = list
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcState) Get(pid int) error {
|
||||
data, err := readProcFile(pid, "stat")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Extract the comm value with is surrounded by parentheses.
|
||||
lIdx := bytes.Index(data, []byte("("))
|
||||
rIdx := bytes.LastIndex(data, []byte(")"))
|
||||
if lIdx < 0 || rIdx < 0 || lIdx >= rIdx || rIdx+2 >= len(data) {
|
||||
return fmt.Errorf("failed to extract comm for pid %d from '%v'", pid, string(data))
|
||||
}
|
||||
self.Name = string(data[lIdx+1 : rIdx])
|
||||
|
||||
// Extract the rest of the fields that we are interested in.
|
||||
fields := bytes.Fields(data[rIdx+2:])
|
||||
if len(fields) <= 36 {
|
||||
return fmt.Errorf("expected more stat fields for pid %d from '%v'", pid, string(data))
|
||||
}
|
||||
|
||||
interests := bytes.Join([][]byte{
|
||||
fields[0], // state
|
||||
fields[1], // ppid
|
||||
fields[2], // pgrp
|
||||
fields[4], // tty_nr
|
||||
fields[15], // priority
|
||||
fields[16], // nice
|
||||
fields[36], // processor (last processor executed on)
|
||||
}, []byte(" "))
|
||||
|
||||
var state string
|
||||
_, err = fmt.Fscan(bytes.NewBuffer(interests),
|
||||
&state,
|
||||
&self.Ppid,
|
||||
&self.Pgid,
|
||||
&self.Tty,
|
||||
&self.Priority,
|
||||
&self.Nice,
|
||||
&self.Processor,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse stat fields for pid %d from '%v': %v", pid, string(data), err)
|
||||
}
|
||||
self.State = RunState(state[0])
|
||||
|
||||
// Read /proc/[pid]/status to get the uid, then lookup uid to get username.
|
||||
status, err := getProcStatus(pid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read process status for pid %d: %v", pid, err)
|
||||
}
|
||||
uids, err := getUIDs(status)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read process status for pid %d: %v", pid, err)
|
||||
}
|
||||
user, err := user.LookupId(uids[0])
|
||||
if err == nil {
|
||||
self.Username = user.Username
|
||||
} else {
|
||||
self.Username = uids[0]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcMem) Get(pid int) error {
|
||||
contents, err := readProcFile(pid, "statm")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields := strings.Fields(string(contents))
|
||||
|
||||
size, _ := strtoull(fields[0])
|
||||
self.Size = size << 12
|
||||
|
||||
rss, _ := strtoull(fields[1])
|
||||
self.Resident = rss << 12
|
||||
|
||||
share, _ := strtoull(fields[2])
|
||||
self.Share = share << 12
|
||||
|
||||
contents, err = readProcFile(pid, "stat")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields = strings.Fields(string(contents))
|
||||
|
||||
self.MinorFaults, _ = strtoull(fields[10])
|
||||
self.MajorFaults, _ = strtoull(fields[12])
|
||||
self.PageFaults = self.MinorFaults + self.MajorFaults
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcTime) Get(pid int) error {
|
||||
contents, err := readProcFile(pid, "stat")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields := strings.Fields(string(contents))
|
||||
|
||||
user, _ := strtoull(fields[13])
|
||||
sys, _ := strtoull(fields[14])
|
||||
// convert to millis
|
||||
self.User = user * (1000 / system.ticks)
|
||||
self.Sys = sys * (1000 / system.ticks)
|
||||
self.Total = self.User + self.Sys
|
||||
|
||||
// convert to millis
|
||||
self.StartTime, _ = strtoull(fields[21])
|
||||
self.StartTime /= system.ticks
|
||||
self.StartTime += system.btime
|
||||
self.StartTime *= 1000
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcArgs) Get(pid int) error {
|
||||
contents, err := readProcFile(pid, "cmdline")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bbuf := bytes.NewBuffer(contents)
|
||||
|
||||
var args []string
|
||||
|
||||
for {
|
||||
arg, err := bbuf.ReadBytes(0)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
args = append(args, string(chop(arg)))
|
||||
}
|
||||
|
||||
self.List = args
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcEnv) Get(pid int) error {
|
||||
contents, err := readProcFile(pid, "environ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if self.Vars == nil {
|
||||
self.Vars = map[string]string{}
|
||||
}
|
||||
|
||||
pairs := bytes.Split(contents, []byte{0})
|
||||
for _, kv := range pairs {
|
||||
parts := bytes.SplitN(kv, []byte{'='}, 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := string(bytes.TrimSpace(parts[0]))
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
self.Vars[key] = string(bytes.TrimSpace(parts[1]))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcExe) Get(pid int) error {
|
||||
fields := map[string]*string{
|
||||
"exe": &self.Name,
|
||||
"cwd": &self.Cwd,
|
||||
"root": &self.Root,
|
||||
}
|
||||
|
||||
for name, field := range fields {
|
||||
val, err := os.Readlink(procFileName(pid, name))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*field = val
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseMeminfo() (map[string]uint64, error) {
|
||||
table := map[string]uint64{}
|
||||
|
||||
err := readFile(Procd+"/meminfo", func(line string) bool {
|
||||
fields := strings.Split(line, ":")
|
||||
|
||||
if len(fields) != 2 {
|
||||
return true // skip on errors
|
||||
}
|
||||
|
||||
valueUnit := strings.Fields(fields[1])
|
||||
value, err := strtoull(valueUnit[0])
|
||||
if err != nil {
|
||||
return true // skip on errors
|
||||
}
|
||||
|
||||
if len(valueUnit) > 1 && valueUnit[1] == "kB" {
|
||||
value *= 1024
|
||||
}
|
||||
table[fields[0]] = value
|
||||
|
||||
return true
|
||||
})
|
||||
return table, err
|
||||
}
|
||||
|
||||
func readFile(file string, handler func(string) bool) error {
|
||||
contents, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(bytes.NewBuffer(contents))
|
||||
|
||||
for {
|
||||
line, _, err := reader.ReadLine()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if !handler(string(line)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func strtoull(val string) (uint64, error) {
|
||||
return strconv.ParseUint(val, 10, 64)
|
||||
}
|
||||
|
||||
func procFileName(pid int, name string) string {
|
||||
return Procd + "/" + strconv.Itoa(pid) + "/" + name
|
||||
}
|
||||
|
||||
func readProcFile(pid int, name string) (content []byte, err error) {
|
||||
path := procFileName(pid, name)
|
||||
|
||||
// Panics have been reported when reading proc files, let's recover and
|
||||
// report the path if this happens
|
||||
// See https://github.com/elastic/beats/issues/6692
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
content = nil
|
||||
err = fmt.Errorf("recovered panic when reading proc file '%s': %v", path, r)
|
||||
}
|
||||
}()
|
||||
contents, err := ioutil.ReadFile(path)
|
||||
|
||||
if err != nil {
|
||||
if perr, ok := err.(*os.PathError); ok {
|
||||
if perr.Err == syscall.ENOENT {
|
||||
return nil, syscall.ESRCH
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return contents, err
|
||||
}
|
||||
|
||||
// getProcStatus reads /proc/[pid]/status which contains process status
|
||||
// information in human readable form.
|
||||
func getProcStatus(pid int) (map[string]string, error) {
|
||||
status := make(map[string]string, 42)
|
||||
path := filepath.Join(Procd, strconv.Itoa(pid), "status")
|
||||
err := readFile(path, func(line string) bool {
|
||||
fields := strings.SplitN(line, ":", 2)
|
||||
if len(fields) == 2 {
|
||||
status[fields[0]] = strings.TrimSpace(fields[1])
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
return status, err
|
||||
}
|
||||
|
||||
// getUIDs reads the "Uid" value from status and splits it into four values --
|
||||
// real, effective, saved set, and file system UIDs.
|
||||
func getUIDs(status map[string]string) ([]string, error) {
|
||||
uidLine, ok := status["Uid"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Uid not found in proc status")
|
||||
}
|
||||
|
||||
uidStrs := strings.Fields(uidLine)
|
||||
if len(uidStrs) != 4 {
|
||||
return nil, fmt.Errorf("Uid line ('%s') did not contain four values", uidLine)
|
||||
}
|
||||
|
||||
return uidStrs, nil
|
||||
}
|
|
@ -0,0 +1,426 @@
|
|||
// Copyright (c) 2016 Jasper Lievisse Adriaanse <j@jasper.la>.
|
||||
|
||||
// +build openbsd
|
||||
|
||||
package gosigar
|
||||
|
||||
/*
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/sched.h>
|
||||
#include <sys/swap.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
//import "github.com/davecgh/go-spew/spew"
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Uvmexp struct {
|
||||
pagesize uint32
|
||||
pagemask uint32
|
||||
pageshift uint32
|
||||
npages uint32
|
||||
free uint32
|
||||
active uint32
|
||||
inactive uint32
|
||||
paging uint32
|
||||
wired uint32
|
||||
zeropages uint32
|
||||
reserve_pagedaemon uint32
|
||||
reserve_kernel uint32
|
||||
anonpages uint32
|
||||
vnodepages uint32
|
||||
vtextpages uint32
|
||||
freemin uint32
|
||||
freetarg uint32
|
||||
inactarg uint32
|
||||
wiredmax uint32
|
||||
anonmin uint32
|
||||
vtextmin uint32
|
||||
vnodemin uint32
|
||||
anonminpct uint32
|
||||
vtextmi uint32
|
||||
npct uint32
|
||||
vnodeminpct uint32
|
||||
nswapdev uint32
|
||||
swpages uint32
|
||||
swpginuse uint32
|
||||
swpgonly uint32
|
||||
nswget uint32
|
||||
nanon uint32
|
||||
nanonneeded uint32
|
||||
nfreeanon uint32
|
||||
faults uint32
|
||||
traps uint32
|
||||
intrs uint32
|
||||
swtch uint32
|
||||
softs uint32
|
||||
syscalls uint32
|
||||
pageins uint32
|
||||
obsolete_swapins uint32
|
||||
obsolete_swapouts uint32
|
||||
pgswapin uint32
|
||||
pgswapout uint32
|
||||
forks uint32
|
||||
forks_ppwait uint32
|
||||
forks_sharevm uint32
|
||||
pga_zerohit uint32
|
||||
pga_zeromiss uint32
|
||||
zeroaborts uint32
|
||||
fltnoram uint32
|
||||
fltnoanon uint32
|
||||
fltpgwait uint32
|
||||
fltpgrele uint32
|
||||
fltrelck uint32
|
||||
fltrelckok uint32
|
||||
fltanget uint32
|
||||
fltanretry uint32
|
||||
fltamcopy uint32
|
||||
fltnamap uint32
|
||||
fltnomap uint32
|
||||
fltlget uint32
|
||||
fltget uint32
|
||||
flt_anon uint32
|
||||
flt_acow uint32
|
||||
flt_obj uint32
|
||||
flt_prcopy uint32
|
||||
flt_przero uint32
|
||||
pdwoke uint32
|
||||
pdrevs uint32
|
||||
pdswout uint32
|
||||
pdfreed uint32
|
||||
pdscans uint32
|
||||
pdanscan uint32
|
||||
pdobscan uint32
|
||||
pdreact uint32
|
||||
pdbusy uint32
|
||||
pdpageouts uint32
|
||||
pdpending uint32
|
||||
pddeact uint32
|
||||
pdreanon uint32
|
||||
pdrevnode uint32
|
||||
pdrevtext uint32
|
||||
fpswtch uint32
|
||||
kmapent uint32
|
||||
}
|
||||
|
||||
type Bcachestats struct {
|
||||
numbufs uint64
|
||||
numbufpages uint64
|
||||
numdirtypages uint64
|
||||
numcleanpages uint64
|
||||
pendingwrites uint64
|
||||
pendingreads uint64
|
||||
numwrites uint64
|
||||
numreads uint64
|
||||
cachehits uint64
|
||||
busymapped uint64
|
||||
dmapages uint64
|
||||
highpages uint64
|
||||
delwribufs uint64
|
||||
kvaslots uint64
|
||||
kvaslots_avail uint64
|
||||
}
|
||||
|
||||
type Swapent struct {
|
||||
se_dev C.dev_t
|
||||
se_flags int32
|
||||
se_nblks int32
|
||||
se_inuse int32
|
||||
se_priority int32
|
||||
sw_path []byte
|
||||
}
|
||||
|
||||
func (self *FileSystemList) Get() error {
|
||||
num, err := syscall.Getfsstat(nil, C.MNT_NOWAIT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf := make([]syscall.Statfs_t, num)
|
||||
|
||||
_, err = syscall.Getfsstat(buf, C.MNT_NOWAIT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fslist := make([]FileSystem, 0, num)
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
fs := FileSystem{}
|
||||
|
||||
fs.DirName = byteListToString(buf[i].F_mntonname[:])
|
||||
fs.DevName = byteListToString(buf[i].F_mntfromname[:])
|
||||
fs.SysTypeName = byteListToString(buf[i].F_fstypename[:])
|
||||
|
||||
fslist = append(fslist, fs)
|
||||
}
|
||||
|
||||
self.List = fslist
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *FileSystemUsage) Get(path string) error {
|
||||
stat := syscall.Statfs_t{}
|
||||
err := syscall.Statfs(path, &stat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Total = uint64(stat.F_blocks) * uint64(stat.F_bsize)
|
||||
self.Free = uint64(stat.F_bfree) * uint64(stat.F_bsize)
|
||||
self.Avail = uint64(stat.F_bavail) * uint64(stat.F_bsize)
|
||||
self.Used = self.Total - self.Free
|
||||
self.Files = stat.F_files
|
||||
self.FreeFiles = stat.F_ffree
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *FDUsage) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *LoadAverage) Get() error {
|
||||
avg := []C.double{0, 0, 0}
|
||||
|
||||
C.getloadavg(&avg[0], C.int(len(avg)))
|
||||
|
||||
self.One = float64(avg[0])
|
||||
self.Five = float64(avg[1])
|
||||
self.Fifteen = float64(avg[2])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Uptime) Get() error {
|
||||
tv := syscall.Timeval{}
|
||||
mib := [2]int32{C.CTL_KERN, C.KERN_BOOTTIME}
|
||||
|
||||
n := uintptr(0)
|
||||
// First we determine how much memory we'll need to pass later on (via `n`)
|
||||
_, _, errno := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Now perform the actual sysctl(3) call, storing the result in tv
|
||||
_, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, uintptr(unsafe.Pointer(&tv)), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.Length = time.Since(time.Unix(int64(tv.Sec), int64(tv.Usec)*1000)).Seconds()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Mem) Get() error {
|
||||
n := uintptr(0)
|
||||
|
||||
var uvmexp Uvmexp
|
||||
mib := [2]int32{C.CTL_VM, C.VM_UVMEXP}
|
||||
n = uintptr(0)
|
||||
// First we determine how much memory we'll need to pass later on (via `n`)
|
||||
_, _, errno := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, uintptr(unsafe.Pointer(&uvmexp)), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var bcachestats Bcachestats
|
||||
mib3 := [3]int32{C.CTL_VFS, C.VFS_GENERIC, C.VFS_BCACHESTAT}
|
||||
n = uintptr(0)
|
||||
_, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib3[0])), 3, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
_, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib3[0])), 3, uintptr(unsafe.Pointer(&bcachestats)), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.Total = uint64(uvmexp.npages) << uvmexp.pageshift
|
||||
self.Used = uint64(uvmexp.npages-uvmexp.free) << uvmexp.pageshift
|
||||
self.Free = uint64(uvmexp.free) << uvmexp.pageshift
|
||||
|
||||
self.ActualFree = self.Free + (uint64(bcachestats.numbufpages) << uvmexp.pageshift)
|
||||
self.ActualUsed = self.Used - (uint64(bcachestats.numbufpages) << uvmexp.pageshift)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Swap) Get() error {
|
||||
nswap := C.swapctl(C.SWAP_NSWAP, unsafe.Pointer(uintptr(0)), 0)
|
||||
|
||||
// If there are no swap devices, nothing to do here.
|
||||
if nswap == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
swdev := make([]Swapent, nswap)
|
||||
|
||||
rnswap := C.swapctl(C.SWAP_STATS, unsafe.Pointer(&swdev[0]), nswap)
|
||||
if rnswap == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < int(nswap); i++ {
|
||||
if swdev[i].se_flags&C.SWF_ENABLE == 2 {
|
||||
self.Used = self.Used + uint64(swdev[i].se_inuse/(1024/C.DEV_BSIZE))
|
||||
self.Total = self.Total + uint64(swdev[i].se_nblks/(1024/C.DEV_BSIZE))
|
||||
}
|
||||
}
|
||||
|
||||
self.Free = self.Total - self.Used
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *HugeTLBPages) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *Cpu) Get() error {
|
||||
load := [C.CPUSTATES]C.long{C.CP_USER, C.CP_NICE, C.CP_SYS, C.CP_INTR, C.CP_IDLE}
|
||||
|
||||
mib := [2]int32{C.CTL_KERN, C.KERN_CPTIME}
|
||||
n := uintptr(0)
|
||||
// First we determine how much memory we'll need to pass later on (via `n`)
|
||||
_, _, errno := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, uintptr(unsafe.Pointer(&load)), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.User = uint64(load[0])
|
||||
self.Nice = uint64(load[1])
|
||||
self.Sys = uint64(load[2])
|
||||
self.Irq = uint64(load[3])
|
||||
self.Idle = uint64(load[4])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *CpuList) Get() error {
|
||||
mib := [2]int32{C.CTL_HW, C.HW_NCPU}
|
||||
var ncpu int
|
||||
|
||||
n := uintptr(0)
|
||||
// First we determine how much memory we'll need to pass later on (via `n`)
|
||||
_, _, errno := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Now perform the actual sysctl(3) call, storing the result in ncpu
|
||||
_, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, uintptr(unsafe.Pointer(&ncpu)), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
load := [C.CPUSTATES]C.long{C.CP_USER, C.CP_NICE, C.CP_SYS, C.CP_INTR, C.CP_IDLE}
|
||||
|
||||
self.List = make([]Cpu, ncpu)
|
||||
for curcpu := range self.List {
|
||||
sysctlCptime(ncpu, curcpu, &load)
|
||||
fillCpu(&self.List[curcpu], load)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcList) Get() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcArgs) Get(pid int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcEnv) Get(pid int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *ProcState) Get(pid int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcMem) Get(pid int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcTime) Get(pid int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *ProcExe) Get(pid int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcFDUsage) Get(pid int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *Rusage) Get(pid int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func fillCpu(cpu *Cpu, load [C.CPUSTATES]C.long) {
|
||||
cpu.User = uint64(load[0])
|
||||
cpu.Nice = uint64(load[1])
|
||||
cpu.Sys = uint64(load[2])
|
||||
cpu.Irq = uint64(load[3])
|
||||
cpu.Idle = uint64(load[4])
|
||||
}
|
||||
|
||||
func sysctlCptime(ncpu int, curcpu int, load *[C.CPUSTATES]C.long) error {
|
||||
var mib []int32
|
||||
|
||||
// Use the correct mib based on the number of CPUs and fill out the
|
||||
// current CPU number in case of SMP. (0 indexed cf. self.List)
|
||||
if ncpu == 0 {
|
||||
mib = []int32{C.CTL_KERN, C.KERN_CPTIME}
|
||||
} else {
|
||||
mib = []int32{C.CTL_KERN, C.KERN_CPTIME2, int32(curcpu)}
|
||||
}
|
||||
|
||||
len := len(mib)
|
||||
|
||||
n := uintptr(0)
|
||||
// First we determine how much memory we'll need to pass later on (via `n`)
|
||||
_, _, errno := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), uintptr(len), 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), uintptr(len), uintptr(unsafe.Pointer(load)), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errno != 0 || n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
// +build !aix,!darwin,!freebsd,!linux,!openbsd,!windows
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func (c *Cpu) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (l *LoadAverage) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (m *Mem) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (s *Swap) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (s *HugeTLBPages) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (f *FDUsage) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (p *ProcTime) Get(int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *FileSystemUsage) Get(path string) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *CpuList) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (p *ProcState) Get(int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (p *ProcExe) Get(int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (p *ProcMem) Get(int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (p *ProcFDUsage) Get(int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (p *ProcEnv) Get(int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (p *ProcList) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (p *ProcArgs) Get(int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *Rusage) Get(int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright (c) 2012 VMware, Inc.
|
||||
|
||||
// +build aix darwin freebsd linux
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func (self *FileSystemUsage) Get(path string) error {
|
||||
stat := syscall.Statfs_t{}
|
||||
err := syscall.Statfs(path, &stat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Total = uint64(stat.Blocks) * uint64(stat.Bsize)
|
||||
self.Free = uint64(stat.Bfree) * uint64(stat.Bsize)
|
||||
self.Avail = uint64(stat.Bavail) * uint64(stat.Bsize)
|
||||
self.Used = self.Total - self.Free
|
||||
self.Files = stat.Files
|
||||
self.FreeFiles = uint64(stat.Ffree)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Rusage) Get(who int) error {
|
||||
ru, err := getResourceUsage(who)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uTime := convertRtimeToDur(ru.Utime)
|
||||
sTime := convertRtimeToDur(ru.Stime)
|
||||
|
||||
r.Utime = uTime
|
||||
r.Stime = sTime
|
||||
r.Maxrss = int64(ru.Maxrss)
|
||||
r.Ixrss = int64(ru.Ixrss)
|
||||
r.Idrss = int64(ru.Idrss)
|
||||
r.Isrss = int64(ru.Isrss)
|
||||
r.Minflt = int64(ru.Minflt)
|
||||
r.Majflt = int64(ru.Majflt)
|
||||
r.Nswap = int64(ru.Nswap)
|
||||
r.Inblock = int64(ru.Inblock)
|
||||
r.Oublock = int64(ru.Oublock)
|
||||
r.Msgsnd = int64(ru.Msgsnd)
|
||||
r.Msgrcv = int64(ru.Msgrcv)
|
||||
r.Nsignals = int64(ru.Nsignals)
|
||||
r.Nvcsw = int64(ru.Nvcsw)
|
||||
r.Nivcsw = int64(ru.Nivcsw)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getResourceUsage(who int) (unix.Rusage, error) {
|
||||
r := unix.Rusage{}
|
||||
err := unix.Getrusage(who, &r)
|
||||
|
||||
return r, err
|
||||
}
|
||||
|
||||
func convertRtimeToDur(t unix.Timeval) time.Duration {
|
||||
return time.Duration(t.Nano())
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) 2012 VMware, Inc.
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// byteListToString converts the raw byte arrays we get into a string. This is a bit of a process, as byte strings are normally []uint8
|
||||
func byteListToString(raw []int8) string {
|
||||
byteList := make([]byte, len(raw))
|
||||
|
||||
for pos, singleByte := range raw {
|
||||
byteList[pos] = byte(singleByte)
|
||||
if singleByte == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return string(bytes.Trim(byteList, "\x00"))
|
||||
}
|
||||
|
||||
func chop(buf []byte) []byte {
|
||||
return buf[0 : len(buf)-1]
|
||||
}
|
||||
|
||||
// convertBytesToString trims null bytes and returns a string
|
||||
func convertBytesToString(arr []byte) string {
|
||||
return string(bytes.Trim(arr, "\x00"))
|
||||
}
|
|
@ -0,0 +1,401 @@
|
|||
// Copyright (c) 2012 VMware, Inc.
|
||||
|
||||
package gosigar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/elastic/gosigar/sys/windows"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// version is Windows version of the host OS.
|
||||
version = windows.GetWindowsVersion()
|
||||
|
||||
// processQueryLimitedInfoAccess is set to PROCESS_QUERY_INFORMATION for Windows
|
||||
// 2003 and XP where PROCESS_QUERY_LIMITED_INFORMATION is unknown. For all newer
|
||||
// OS versions it is set to PROCESS_QUERY_LIMITED_INFORMATION.
|
||||
processQueryLimitedInfoAccess = windows.PROCESS_QUERY_LIMITED_INFORMATION
|
||||
)
|
||||
|
||||
func init() {
|
||||
if !version.IsWindowsVistaOrGreater() {
|
||||
// PROCESS_QUERY_LIMITED_INFORMATION cannot be used on 2003 or XP.
|
||||
processQueryLimitedInfoAccess = syscall.PROCESS_QUERY_INFORMATION
|
||||
}
|
||||
}
|
||||
|
||||
func (self *LoadAverage) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *FDUsage) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *ProcEnv) Get(pid int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *ProcExe) Get(pid int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *ProcFDUsage) Get(pid int) error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *Uptime) Get() error {
|
||||
// Minimum supported OS is Windows Vista.
|
||||
if !version.IsWindowsVistaOrGreater() {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
uptimeMs, err := windows.GetTickCount64()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get boot time using GetTickCount64 api")
|
||||
}
|
||||
self.Length = float64(time.Duration(uptimeMs)*time.Millisecond) / float64(time.Second)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Mem) Get() error {
|
||||
memoryStatusEx, err := windows.GlobalMemoryStatusEx()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GlobalMemoryStatusEx failed")
|
||||
}
|
||||
|
||||
self.Total = memoryStatusEx.TotalPhys
|
||||
self.Free = memoryStatusEx.AvailPhys
|
||||
self.Used = self.Total - self.Free
|
||||
self.ActualFree = self.Free
|
||||
self.ActualUsed = self.Used
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Swap) Get() error {
|
||||
memoryStatusEx, err := windows.GlobalMemoryStatusEx()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GlobalMemoryStatusEx failed")
|
||||
}
|
||||
|
||||
self.Total = memoryStatusEx.TotalPageFile
|
||||
self.Free = memoryStatusEx.AvailPageFile
|
||||
self.Used = self.Total - self.Free
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *HugeTLBPages) Get() error {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
func (self *Cpu) Get() error {
|
||||
idle, kernel, user, err := windows.GetSystemTimes()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetSystemTimes failed")
|
||||
}
|
||||
|
||||
// CPU times are reported in milliseconds by gosigar.
|
||||
self.Idle = uint64(idle / time.Millisecond)
|
||||
self.Sys = uint64(kernel / time.Millisecond)
|
||||
self.User = uint64(user / time.Millisecond)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *CpuList) Get() error {
|
||||
cpus, err := windows.NtQuerySystemProcessorPerformanceInformation()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "NtQuerySystemProcessorPerformanceInformation failed")
|
||||
}
|
||||
|
||||
self.List = make([]Cpu, 0, len(cpus))
|
||||
for _, cpu := range cpus {
|
||||
self.List = append(self.List, Cpu{
|
||||
Idle: uint64(cpu.IdleTime / time.Millisecond),
|
||||
Sys: uint64(cpu.KernelTime / time.Millisecond),
|
||||
User: uint64(cpu.UserTime / time.Millisecond),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *FileSystemList) Get() error {
|
||||
drives, err := windows.GetAccessPaths()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetAccessPaths failed")
|
||||
}
|
||||
|
||||
for _, drive := range drives {
|
||||
dt, err := windows.GetDriveType(drive)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetDriveType failed")
|
||||
}
|
||||
fsType, err := windows.GetFilesystemType(drive)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetFilesystemType failed")
|
||||
}
|
||||
if fsType != "" {
|
||||
self.List = append(self.List, FileSystem{
|
||||
DirName: drive,
|
||||
DevName: drive,
|
||||
TypeName: dt.String(),
|
||||
SysTypeName: fsType,
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get retrieves a list of all process identifiers (PIDs) in the system.
|
||||
func (self *ProcList) Get() error {
|
||||
pids, err := windows.EnumProcesses()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "EnumProcesses failed")
|
||||
}
|
||||
|
||||
// Convert uint32 PIDs to int.
|
||||
self.List = make([]int, 0, len(pids))
|
||||
for _, pid := range pids {
|
||||
self.List = append(self.List, int(pid))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcState) Get(pid int) error {
|
||||
var errs []error
|
||||
|
||||
var err error
|
||||
self.Name, err = getProcName(pid)
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrap(err, "getProcName failed"))
|
||||
}
|
||||
|
||||
self.State, err = getProcStatus(pid)
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrap(err, "getProcStatus failed"))
|
||||
}
|
||||
|
||||
self.Ppid, err = getParentPid(pid)
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrap(err, "getParentPid failed"))
|
||||
}
|
||||
|
||||
// getProcCredName will often fail when run as a non-admin user. This is
|
||||
// caused by strict ACL of the process token belonging to other users.
|
||||
// Instead of failing completely, ignore this error and still return most
|
||||
// data with an empty Username.
|
||||
self.Username, _ = getProcCredName(pid)
|
||||
|
||||
if len(errs) > 0 {
|
||||
errStrs := make([]string, 0, len(errs))
|
||||
for _, e := range errs {
|
||||
errStrs = append(errStrs, e.Error())
|
||||
}
|
||||
return errors.New(strings.Join(errStrs, "; "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getProcName returns the process name associated with the PID.
|
||||
func getProcName(pid int) (string, error) {
|
||||
handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess, false, uint32(pid))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
|
||||
}
|
||||
defer syscall.CloseHandle(handle)
|
||||
|
||||
filename, err := windows.GetProcessImageFileName(handle)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "GetProcessImageFileName failed for pid=%v", pid)
|
||||
}
|
||||
|
||||
return filepath.Base(filename), nil
|
||||
}
|
||||
|
||||
// getProcStatus returns the status of a process.
|
||||
func getProcStatus(pid int) (RunState, error) {
|
||||
handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess, false, uint32(pid))
|
||||
if err != nil {
|
||||
return RunStateUnknown, errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
|
||||
}
|
||||
defer syscall.CloseHandle(handle)
|
||||
|
||||
var exitCode uint32
|
||||
err = syscall.GetExitCodeProcess(handle, &exitCode)
|
||||
if err != nil {
|
||||
return RunStateUnknown, errors.Wrapf(err, "GetExitCodeProcess failed for pid=%v", pid)
|
||||
}
|
||||
|
||||
if exitCode == 259 { //still active
|
||||
return RunStateRun, nil
|
||||
}
|
||||
return RunStateSleep, nil
|
||||
}
|
||||
|
||||
// getParentPid returns the parent process ID of a process.
|
||||
func getParentPid(pid int) (int, error) {
|
||||
handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess, false, uint32(pid))
|
||||
if err != nil {
|
||||
return RunStateUnknown, errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
|
||||
}
|
||||
defer syscall.CloseHandle(handle)
|
||||
|
||||
procInfo, err := windows.NtQueryProcessBasicInformation(handle)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "NtQueryProcessBasicInformation failed for pid=%v", pid)
|
||||
}
|
||||
|
||||
return int(procInfo.InheritedFromUniqueProcessID), nil
|
||||
}
|
||||
|
||||
func getProcCredName(pid int) (string, error) {
|
||||
handle, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
|
||||
}
|
||||
defer syscall.CloseHandle(handle)
|
||||
|
||||
// Find process token via win32.
|
||||
var token syscall.Token
|
||||
err = syscall.OpenProcessToken(handle, syscall.TOKEN_QUERY, &token)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "OpenProcessToken failed for pid=%v", pid)
|
||||
}
|
||||
// Close token to prevent handle leaks.
|
||||
defer token.Close()
|
||||
|
||||
// Find the token user.
|
||||
tokenUser, err := token.GetTokenUser()
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "GetTokenInformation failed for pid=%v", pid)
|
||||
}
|
||||
|
||||
// Look up domain account by SID.
|
||||
account, domain, _, err := tokenUser.User.Sid.LookupAccount("")
|
||||
if err != nil {
|
||||
sid, sidErr := tokenUser.User.Sid.String()
|
||||
if sidErr != nil {
|
||||
return "", errors.Wrapf(err, "failed while looking up account name for pid=%v", pid)
|
||||
}
|
||||
return "", errors.Wrapf(err, "failed while looking up account name for SID=%v of pid=%v", sid, pid)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`%s\%s`, domain, account), nil
|
||||
}
|
||||
|
||||
func (self *ProcMem) Get(pid int) error {
|
||||
handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess|windows.PROCESS_VM_READ, false, uint32(pid))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
|
||||
}
|
||||
defer syscall.CloseHandle(handle)
|
||||
|
||||
counters, err := windows.GetProcessMemoryInfo(handle)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetProcessMemoryInfo failed for pid=%v", pid)
|
||||
}
|
||||
|
||||
self.Resident = uint64(counters.WorkingSetSize)
|
||||
self.Size = uint64(counters.PrivateUsage)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProcTime) Get(pid int) error {
|
||||
cpu, err := getProcTimes(pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Windows epoch times are expressed as time elapsed since midnight on
|
||||
// January 1, 1601 at Greenwich, England. This converts the Filetime to
|
||||
// unix epoch in milliseconds.
|
||||
self.StartTime = uint64(cpu.CreationTime.Nanoseconds() / 1e6)
|
||||
|
||||
// Convert to millis.
|
||||
self.User = uint64(windows.FiletimeToDuration(&cpu.UserTime).Nanoseconds() / 1e6)
|
||||
self.Sys = uint64(windows.FiletimeToDuration(&cpu.KernelTime).Nanoseconds() / 1e6)
|
||||
self.Total = self.User + self.Sys
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getProcTimes(pid int) (*syscall.Rusage, error) {
|
||||
handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess, false, uint32(pid))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
|
||||
}
|
||||
defer syscall.CloseHandle(handle)
|
||||
|
||||
var cpu syscall.Rusage
|
||||
if err := syscall.GetProcessTimes(handle, &cpu.CreationTime, &cpu.ExitTime, &cpu.KernelTime, &cpu.UserTime); err != nil {
|
||||
return nil, errors.Wrapf(err, "GetProcessTimes failed for pid=%v", pid)
|
||||
}
|
||||
|
||||
return &cpu, nil
|
||||
}
|
||||
|
||||
func (self *ProcArgs) Get(pid int) error {
|
||||
// The minimum supported client for Win32_Process is Windows Vista.
|
||||
if !version.IsWindowsVistaOrGreater() {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
handle, err := syscall.OpenProcess(processQueryLimitedInfoAccess|windows.PROCESS_VM_READ, false, uint32(pid))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "OpenProcess failed for pid=%v", pid)
|
||||
}
|
||||
defer syscall.CloseHandle(handle)
|
||||
pbi, err := windows.NtQueryProcessBasicInformation(handle)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "NtQueryProcessBasicInformation failed for pid=%v", pid)
|
||||
}
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
userProcParams, err := windows.GetUserProcessParams(handle, pbi)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if argsW, err := windows.ReadProcessUnicodeString(handle, &userProcParams.CommandLine); err == nil {
|
||||
self.List, err = windows.ByteSliceToStringSlice(argsW)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *FileSystemUsage) Get(path string) error {
|
||||
freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes, err := windows.GetDiskFreeSpaceEx(path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetDiskFreeSpaceEx failed")
|
||||
}
|
||||
|
||||
self.Total = totalNumberOfBytes
|
||||
self.Free = totalNumberOfFreeBytes
|
||||
self.Used = self.Total - self.Free
|
||||
self.Avail = freeBytesAvailable
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Rusage) Get(who int) error {
|
||||
if who != 0 {
|
||||
return ErrNotImplemented{runtime.GOOS}
|
||||
}
|
||||
|
||||
pid := os.Getpid()
|
||||
cpu, err := getProcTimes(pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.Utime = windows.FiletimeToDuration(&cpu.UserTime)
|
||||
self.Stime = windows.FiletimeToDuration(&cpu.KernelTime)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// Package windows contains various Windows system call.
|
||||
package windows
|
||||
|
||||
// Use "go generate -v -x ." to generate the source.
|
||||
|
||||
// Add -trace to enable debug prints around syscalls.
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -systemdll=true -output zsyscall_windows.go syscall_windows.go
|
||||
//go:generate go run fix_generated.go -input zsyscall_windows.go
|
|
@ -0,0 +1,132 @@
|
|||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// On both 32-bit and 64-bit systems NtQuerySystemInformation expects the
|
||||
// size of SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION to be 48.
|
||||
const sizeofSystemProcessorPerformanceInformation = 48
|
||||
|
||||
// ProcessBasicInformation is an equivalent representation of
|
||||
// PROCESS_BASIC_INFORMATION in the Windows API.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684280(v=vs.85).aspx
|
||||
type ProcessBasicInformation struct {
|
||||
ExitStatus uint
|
||||
PebBaseAddress uintptr
|
||||
AffinityMask uint
|
||||
BasePriority uint
|
||||
UniqueProcessID uint
|
||||
InheritedFromUniqueProcessID uint
|
||||
}
|
||||
|
||||
// NtQueryProcessBasicInformation queries basic information about the process
|
||||
// associated with the given handle (provided by OpenProcess). It uses the
|
||||
// NtQueryInformationProcess function to collect the data.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684280(v=vs.85).aspx
|
||||
func NtQueryProcessBasicInformation(handle syscall.Handle) (ProcessBasicInformation, error) {
|
||||
var processBasicInfo ProcessBasicInformation
|
||||
processBasicInfoPtr := (*byte)(unsafe.Pointer(&processBasicInfo))
|
||||
size := uint32(unsafe.Sizeof(processBasicInfo))
|
||||
ntStatus, _ := _NtQueryInformationProcess(handle, 0, processBasicInfoPtr, size, nil)
|
||||
if ntStatus != 0 {
|
||||
return ProcessBasicInformation{}, errors.Errorf("NtQueryInformationProcess failed, NTSTATUS=0x%X", ntStatus)
|
||||
}
|
||||
|
||||
return processBasicInfo, nil
|
||||
}
|
||||
|
||||
// SystemProcessorPerformanceInformation contains CPU performance information
|
||||
// for a single CPU.
|
||||
type SystemProcessorPerformanceInformation struct {
|
||||
IdleTime time.Duration // Amount of time spent idle.
|
||||
KernelTime time.Duration // Kernel time does NOT include time spent in idle.
|
||||
UserTime time.Duration // Amount of time spent executing in user mode.
|
||||
}
|
||||
|
||||
// _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION is an equivalent representation of
|
||||
// SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION in the Windows API. This struct is
|
||||
// used internally with NtQuerySystemInformation call and is not exported. The
|
||||
// exported equivalent is SystemProcessorPerformanceInformation.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724509(v=vs.85).aspx
|
||||
type _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION struct {
|
||||
IdleTime int64
|
||||
KernelTime int64
|
||||
UserTime int64
|
||||
Reserved1 [2]int64
|
||||
Reserved2 uint32
|
||||
}
|
||||
|
||||
// NtQuerySystemProcessorPerformanceInformation queries CPU performance
|
||||
// information for each CPU. It uses the NtQuerySystemInformation function to
|
||||
// collect the SystemProcessorPerformanceInformation.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724509(v=vs.85).aspx
|
||||
func NtQuerySystemProcessorPerformanceInformation() ([]SystemProcessorPerformanceInformation, error) {
|
||||
// NTSTATUS code for success.
|
||||
// https://msdn.microsoft.com/en-us/library/cc704588.aspx
|
||||
const STATUS_SUCCESS = 0
|
||||
|
||||
// From the _SYSTEM_INFORMATION_CLASS enum.
|
||||
// http://processhacker.sourceforge.net/doc/ntexapi_8h.html#ad5d815b48e8f4da1ef2eb7a2f18a54e0
|
||||
const systemProcessorPerformanceInformation = 8
|
||||
|
||||
// Create a buffer large enough to hold an entry for each processor.
|
||||
b := make([]byte, runtime.NumCPU()*sizeofSystemProcessorPerformanceInformation)
|
||||
|
||||
// Query the performance information. Note that this function uses 0 to
|
||||
// indicate success. Most other Windows functions use non-zero for success.
|
||||
var returnLength uint32
|
||||
ntStatus, _ := _NtQuerySystemInformation(systemProcessorPerformanceInformation, &b[0], uint32(len(b)), &returnLength)
|
||||
if ntStatus != STATUS_SUCCESS {
|
||||
return nil, errors.Errorf("NtQuerySystemInformation failed, NTSTATUS=0x%X, bufLength=%v, returnLength=%v", ntStatus, len(b), returnLength)
|
||||
}
|
||||
|
||||
return readSystemProcessorPerformanceInformationBuffer(b)
|
||||
}
|
||||
|
||||
// readSystemProcessorPerformanceInformationBuffer reads from a buffer
|
||||
// containing SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION data. The buffer should
|
||||
// contain one entry for each CPU.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724509(v=vs.85).aspx
|
||||
func readSystemProcessorPerformanceInformationBuffer(b []byte) ([]SystemProcessorPerformanceInformation, error) {
|
||||
n := len(b) / sizeofSystemProcessorPerformanceInformation
|
||||
r := bytes.NewReader(b)
|
||||
|
||||
rtn := make([]SystemProcessorPerformanceInformation, 0, n)
|
||||
for i := 0; i < n; i++ {
|
||||
_, err := r.Seek(int64(i*sizeofSystemProcessorPerformanceInformation), io.SeekStart)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to seek to cpuN=%v in buffer", i)
|
||||
}
|
||||
|
||||
times := make([]uint64, 3)
|
||||
for j := range times {
|
||||
err := binary.Read(r, binary.LittleEndian, ×[j])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed reading cpu times for cpuN=%v", i)
|
||||
}
|
||||
}
|
||||
|
||||
idleTime := time.Duration(times[0] * 100)
|
||||
kernelTime := time.Duration(times[1] * 100)
|
||||
userTime := time.Duration(times[2] * 100)
|
||||
|
||||
rtn = append(rtn, SystemProcessorPerformanceInformation{
|
||||
IdleTime: idleTime,
|
||||
KernelTime: kernelTime - idleTime, // Subtract out idle time from kernel time.
|
||||
UserTime: userTime,
|
||||
})
|
||||
}
|
||||
|
||||
return rtn, nil
|
||||
}
|
|
@ -0,0 +1,272 @@
|
|||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// Cache of privilege names to LUIDs.
|
||||
var (
|
||||
privNames = make(map[string]int64)
|
||||
privNameMutex sync.Mutex
|
||||
)
|
||||
|
||||
const (
|
||||
// SeDebugPrivilege is the name of the privilege used to debug programs.
|
||||
SeDebugPrivilege = "SeDebugPrivilege"
|
||||
)
|
||||
|
||||
// Errors returned by AdjustTokenPrivileges.
|
||||
const (
|
||||
ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300
|
||||
)
|
||||
|
||||
// Attribute bits for privileges.
|
||||
const (
|
||||
_SE_PRIVILEGE_ENABLED_BY_DEFAULT uint32 = 0x00000001
|
||||
_SE_PRIVILEGE_ENABLED uint32 = 0x00000002
|
||||
_SE_PRIVILEGE_REMOVED uint32 = 0x00000004
|
||||
_SE_PRIVILEGE_USED_FOR_ACCESS uint32 = 0x80000000
|
||||
)
|
||||
|
||||
// Privilege contains information about a single privilege associated with a
|
||||
// Token.
|
||||
type Privilege struct {
|
||||
LUID int64 `json:"-"` // Locally unique identifier (guaranteed only until the system is restarted).
|
||||
Name string `json:"-"`
|
||||
EnabledByDefault bool `json:"enabled_by_default,omitempty"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Removed bool `json:"removed,omitempty"`
|
||||
Used bool `json:"used,omitempty"`
|
||||
}
|
||||
|
||||
func (p Privilege) String() string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(p.Name)
|
||||
buf.WriteString("=(")
|
||||
|
||||
opts := make([]string, 0, 4)
|
||||
if p.EnabledByDefault {
|
||||
opts = append(opts, "Default")
|
||||
}
|
||||
if p.Enabled {
|
||||
opts = append(opts, "Enabled")
|
||||
}
|
||||
if !p.EnabledByDefault && !p.Enabled {
|
||||
opts = append(opts, "Disabled")
|
||||
}
|
||||
if p.Removed {
|
||||
opts = append(opts, "Removed")
|
||||
}
|
||||
if p.Used {
|
||||
opts = append(opts, "Used")
|
||||
}
|
||||
|
||||
buf.WriteString(strings.Join(opts, ", "))
|
||||
buf.WriteString(")")
|
||||
|
||||
// Example: SeDebugPrivilege=(Default, Enabled)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// User represent the information about a Windows account.
|
||||
type User struct {
|
||||
SID string
|
||||
Account string
|
||||
Domain string
|
||||
Type uint32
|
||||
}
|
||||
|
||||
func (u User) String() string {
|
||||
return fmt.Sprintf(`User:%v\%v, SID:%v, Type:%v`, u.Domain, u.Account, u.SID, u.Type)
|
||||
}
|
||||
|
||||
// DebugInfo contains general debug info about the current process.
|
||||
type DebugInfo struct {
|
||||
OSVersion Version // OS version info.
|
||||
Arch string // Architecture of the machine.
|
||||
NumCPU int // Number of CPUs.
|
||||
User User // User that this process is running as.
|
||||
ProcessPrivs map[string]Privilege // Privileges held by the process.
|
||||
}
|
||||
|
||||
func (d DebugInfo) String() string {
|
||||
bytes, _ := json.Marshal(d)
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
// LookupPrivilegeName looks up a privilege name given a LUID value.
|
||||
func LookupPrivilegeName(systemName string, luid int64) (string, error) {
|
||||
buf := make([]uint16, 256)
|
||||
bufSize := uint32(len(buf))
|
||||
err := _LookupPrivilegeName(systemName, &luid, &buf[0], &bufSize)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "LookupPrivilegeName failed for luid=%v", luid)
|
||||
}
|
||||
|
||||
return syscall.UTF16ToString(buf), nil
|
||||
}
|
||||
|
||||
// mapPrivileges maps privilege names to LUID values.
|
||||
func mapPrivileges(names []string) ([]int64, error) {
|
||||
var privileges []int64
|
||||
privNameMutex.Lock()
|
||||
defer privNameMutex.Unlock()
|
||||
for _, name := range names {
|
||||
p, ok := privNames[name]
|
||||
if !ok {
|
||||
err := _LookupPrivilegeValue("", name, &p)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "LookupPrivilegeValue failed on '%v'", name)
|
||||
}
|
||||
privNames[name] = p
|
||||
}
|
||||
privileges = append(privileges, p)
|
||||
}
|
||||
return privileges, nil
|
||||
}
|
||||
|
||||
// EnableTokenPrivileges enables the specified privileges in the given
|
||||
// Token. The token must have TOKEN_ADJUST_PRIVILEGES access. If the token
|
||||
// does not already contain the privilege it cannot be enabled.
|
||||
func EnableTokenPrivileges(token syscall.Token, privileges ...string) error {
|
||||
privValues, err := mapPrivileges(privileges)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
binary.Write(&b, binary.LittleEndian, uint32(len(privValues)))
|
||||
for _, p := range privValues {
|
||||
binary.Write(&b, binary.LittleEndian, p)
|
||||
binary.Write(&b, binary.LittleEndian, uint32(_SE_PRIVILEGE_ENABLED))
|
||||
}
|
||||
|
||||
success, err := _AdjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(b.Len()), nil, nil)
|
||||
if !success {
|
||||
return err
|
||||
}
|
||||
if err == ERROR_NOT_ALL_ASSIGNED {
|
||||
return errors.Wrap(err, "error not all privileges were assigned")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTokenPrivileges returns a list of privileges associated with a token.
|
||||
// The provided token must have at a minimum TOKEN_QUERY access. This is a
|
||||
// wrapper around the GetTokenInformation function.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa446671(v=vs.85).aspx
|
||||
func GetTokenPrivileges(token syscall.Token) (map[string]Privilege, error) {
|
||||
// Determine the required buffer size.
|
||||
var size uint32
|
||||
syscall.GetTokenInformation(token, syscall.TokenPrivileges, nil, 0, &size)
|
||||
|
||||
// This buffer will receive a TOKEN_PRIVILEGE structure.
|
||||
b := bytes.NewBuffer(make([]byte, size))
|
||||
err := syscall.GetTokenInformation(token, syscall.TokenPrivileges, &b.Bytes()[0], uint32(b.Len()), &size)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetTokenInformation failed")
|
||||
}
|
||||
|
||||
var privilegeCount uint32
|
||||
err = binary.Read(b, binary.LittleEndian, &privilegeCount)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to read PrivilegeCount")
|
||||
}
|
||||
|
||||
rtn := make(map[string]Privilege, privilegeCount)
|
||||
for i := 0; i < int(privilegeCount); i++ {
|
||||
var luid int64
|
||||
err = binary.Read(b, binary.LittleEndian, &luid)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to read LUID value")
|
||||
}
|
||||
|
||||
var attributes uint32
|
||||
err = binary.Read(b, binary.LittleEndian, &attributes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to read attributes")
|
||||
}
|
||||
|
||||
name, err := LookupPrivilegeName("", luid)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "LookupPrivilegeName failed for LUID=%v", luid)
|
||||
}
|
||||
|
||||
rtn[name] = Privilege{
|
||||
LUID: luid,
|
||||
Name: name,
|
||||
EnabledByDefault: (attributes & _SE_PRIVILEGE_ENABLED_BY_DEFAULT) > 0,
|
||||
Enabled: (attributes & _SE_PRIVILEGE_ENABLED) > 0,
|
||||
Removed: (attributes & _SE_PRIVILEGE_REMOVED) > 0,
|
||||
Used: (attributes & _SE_PRIVILEGE_USED_FOR_ACCESS) > 0,
|
||||
}
|
||||
}
|
||||
|
||||
return rtn, nil
|
||||
}
|
||||
|
||||
// GetTokenUser returns the User associated with the given Token.
|
||||
func GetTokenUser(token syscall.Token) (User, error) {
|
||||
tokenUser, err := token.GetTokenUser()
|
||||
if err != nil {
|
||||
return User{}, errors.Wrap(err, "GetTokenUser failed")
|
||||
}
|
||||
|
||||
var user User
|
||||
user.SID, err = tokenUser.User.Sid.String()
|
||||
if err != nil {
|
||||
return user, errors.Wrap(err, "ConvertSidToStringSid failed")
|
||||
}
|
||||
|
||||
user.Account, user.Domain, user.Type, err = tokenUser.User.Sid.LookupAccount("")
|
||||
if err != nil {
|
||||
return user, errors.Wrap(err, "LookupAccountSid failed")
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// GetDebugInfo returns general debug info about the current process.
|
||||
func GetDebugInfo() (*DebugInfo, error) {
|
||||
h, err := windows.GetCurrentProcess()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var token syscall.Token
|
||||
err = syscall.OpenProcessToken(syscall.Handle(h), syscall.TOKEN_QUERY, &token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
privs, err := GetTokenPrivileges(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user, err := GetTokenUser(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &DebugInfo{
|
||||
User: user,
|
||||
ProcessPrivs: privs,
|
||||
OSVersion: GetWindowsVersion(),
|
||||
Arch: runtime.GOARCH,
|
||||
NumCPU: runtime.NumCPU(),
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,634 @@
|
|||
package windows
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
sizeofUint32 = 4
|
||||
sizeofProcessEntry32 = uint32(unsafe.Sizeof(ProcessEntry32{}))
|
||||
sizeofProcessMemoryCountersEx = uint32(unsafe.Sizeof(ProcessMemoryCountersEx{}))
|
||||
sizeofMemoryStatusEx = uint32(unsafe.Sizeof(MemoryStatusEx{}))
|
||||
)
|
||||
|
||||
// Process-specific access rights. Others are declared in the syscall package.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684880(v=vs.85).aspx
|
||||
const (
|
||||
PROCESS_QUERY_LIMITED_INFORMATION uint32 = 0x1000
|
||||
PROCESS_VM_READ uint32 = 0x0010
|
||||
)
|
||||
|
||||
// error codes for GetVolumeInformation function
|
||||
const (
|
||||
ERROR_INVALID_FUNCTION syscall.Errno = 1
|
||||
ERROR_NOT_READY syscall.Errno = 21
|
||||
)
|
||||
|
||||
// SizeOfRtlUserProcessParameters gives the size
|
||||
// of the RtlUserProcessParameters struct.
|
||||
const SizeOfRtlUserProcessParameters = unsafe.Sizeof(RtlUserProcessParameters{})
|
||||
|
||||
// MAX_PATH is the maximum length for a path in Windows.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
|
||||
const MAX_PATH = 260
|
||||
|
||||
// DriveType represents a type of drive (removable, fixed, CD-ROM, RAM disk, or
|
||||
// network drive).
|
||||
type DriveType uint32
|
||||
|
||||
// Drive types as returned by GetDriveType.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364939(v=vs.85).aspx
|
||||
const (
|
||||
DRIVE_UNKNOWN DriveType = iota
|
||||
DRIVE_NO_ROOT_DIR
|
||||
DRIVE_REMOVABLE
|
||||
DRIVE_FIXED
|
||||
DRIVE_REMOTE
|
||||
DRIVE_CDROM
|
||||
DRIVE_RAMDISK
|
||||
)
|
||||
|
||||
// UnicodeString is Go's equivalent for the _UNICODE_STRING struct.
|
||||
type UnicodeString struct {
|
||||
Size uint16
|
||||
MaximumLength uint16
|
||||
Buffer uintptr
|
||||
}
|
||||
|
||||
// RtlUserProcessParameters is Go's equivalent for the
|
||||
// _RTL_USER_PROCESS_PARAMETERS struct.
|
||||
// A few undocumented fields are exposed.
|
||||
type RtlUserProcessParameters struct {
|
||||
Reserved1 [16]byte
|
||||
Reserved2 [5]uintptr
|
||||
CurrentDirectoryPath UnicodeString
|
||||
CurrentDirectoryHandle uintptr
|
||||
DllPath UnicodeString
|
||||
ImagePathName UnicodeString
|
||||
CommandLine UnicodeString
|
||||
}
|
||||
|
||||
func (dt DriveType) String() string {
|
||||
names := map[DriveType]string{
|
||||
DRIVE_UNKNOWN: "unknown",
|
||||
DRIVE_NO_ROOT_DIR: "invalid",
|
||||
DRIVE_REMOVABLE: "removable",
|
||||
DRIVE_FIXED: "fixed",
|
||||
DRIVE_REMOTE: "remote",
|
||||
DRIVE_CDROM: "cdrom",
|
||||
DRIVE_RAMDISK: "ramdisk",
|
||||
}
|
||||
|
||||
name, found := names[dt]
|
||||
if !found {
|
||||
return "unknown DriveType value"
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// Flags that can be used with CreateToolhelp32Snapshot.
|
||||
const (
|
||||
TH32CS_INHERIT uint32 = 0x80000000 // Indicates that the snapshot handle is to be inheritable.
|
||||
TH32CS_SNAPHEAPLIST uint32 = 0x00000001 // Includes all heaps of the process specified in th32ProcessID in the snapshot.
|
||||
TH32CS_SNAPMODULE uint32 = 0x00000008 // Includes all modules of the process specified in th32ProcessID in the snapshot.
|
||||
TH32CS_SNAPMODULE32 uint32 = 0x00000010 // Includes all 32-bit modules of the process specified in th32ProcessID in the snapshot when called from a 64-bit process.
|
||||
TH32CS_SNAPPROCESS uint32 = 0x00000002 // Includes all processes in the system in the snapshot.
|
||||
TH32CS_SNAPTHREAD uint32 = 0x00000004 // Includes all threads in the system in the snapshot.
|
||||
)
|
||||
|
||||
// ProcessEntry32 is an equivalent representation of PROCESSENTRY32 in the
|
||||
// Windows API. It contains a process's information. Do not modify or reorder.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684839(v=vs.85).aspx
|
||||
type ProcessEntry32 struct {
|
||||
size uint32
|
||||
CntUsage uint32
|
||||
ProcessID uint32
|
||||
DefaultHeapID uintptr
|
||||
ModuleID uint32
|
||||
CntThreads uint32
|
||||
ParentProcessID uint32
|
||||
PriorityClassBase int32
|
||||
Flags uint32
|
||||
exeFile [MAX_PATH]uint16
|
||||
}
|
||||
|
||||
// ExeFile returns the name of the executable file for the process. It does
|
||||
// not contain the full path.
|
||||
func (p ProcessEntry32) ExeFile() string {
|
||||
return syscall.UTF16ToString(p.exeFile[:])
|
||||
}
|
||||
|
||||
func (p ProcessEntry32) String() string {
|
||||
return fmt.Sprintf("{CntUsage:%v ProcessID:%v DefaultHeapID:%v ModuleID:%v "+
|
||||
"CntThreads:%v ParentProcessID:%v PriorityClassBase:%v Flags:%v ExeFile:%v",
|
||||
p.CntUsage, p.ProcessID, p.DefaultHeapID, p.ModuleID, p.CntThreads,
|
||||
p.ParentProcessID, p.PriorityClassBase, p.Flags, p.ExeFile())
|
||||
}
|
||||
|
||||
// MemoryStatusEx is an equivalent representation of MEMORYSTATUSEX in the
|
||||
// Windows API. It contains information about the current state of both physical
|
||||
// and virtual memory, including extended memory.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366770
|
||||
type MemoryStatusEx struct {
|
||||
length uint32
|
||||
MemoryLoad uint32
|
||||
TotalPhys uint64
|
||||
AvailPhys uint64
|
||||
TotalPageFile uint64
|
||||
AvailPageFile uint64
|
||||
TotalVirtual uint64
|
||||
AvailVirtual uint64
|
||||
AvailExtendedVirtual uint64
|
||||
}
|
||||
|
||||
// ProcessMemoryCountersEx is an equivalent representation of
|
||||
// PROCESS_MEMORY_COUNTERS_EX in the Windows API.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684874(v=vs.85).aspx
|
||||
type ProcessMemoryCountersEx struct {
|
||||
cb uint32
|
||||
PageFaultCount uint32
|
||||
PeakWorkingSetSize uintptr
|
||||
WorkingSetSize uintptr
|
||||
QuotaPeakPagedPoolUsage uintptr
|
||||
QuotaPagedPoolUsage uintptr
|
||||
QuotaPeakNonPagedPoolUsage uintptr
|
||||
QuotaNonPagedPoolUsage uintptr
|
||||
PagefileUsage uintptr
|
||||
PeakPagefileUsage uintptr
|
||||
PrivateUsage uintptr
|
||||
}
|
||||
|
||||
// GetLogicalDriveStrings returns a list of drives in the system.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364975(v=vs.85).aspx
|
||||
func GetLogicalDriveStrings() ([]string, error) {
|
||||
// Determine the size of the buffer required to receive all drives.
|
||||
bufferLength, err := _GetLogicalDriveStringsW(0, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetLogicalDriveStringsW failed to get buffer length")
|
||||
}
|
||||
if bufferLength < 0 {
|
||||
return nil, errors.New("GetLogicalDriveStringsW returned an invalid buffer length")
|
||||
}
|
||||
|
||||
buffer := make([]uint16, bufferLength)
|
||||
_, err = _GetLogicalDriveStringsW(uint32(len(buffer)), &buffer[0])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetLogicalDriveStringsW failed")
|
||||
}
|
||||
|
||||
return UTF16SliceToStringSlice(buffer), nil
|
||||
}
|
||||
|
||||
// GetAccessPaths returns the list of access paths for volumes in the system.
|
||||
func GetAccessPaths() ([]string, error) {
|
||||
volumes, err := GetVolumes()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetVolumes failed")
|
||||
}
|
||||
|
||||
var paths []string
|
||||
for _, volumeName := range volumes {
|
||||
volumePaths, err := GetVolumePathsForVolume(volumeName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get list of access paths for volume '%s'", volumeName)
|
||||
}
|
||||
if len(volumePaths) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get only the first path
|
||||
paths = append(paths, volumePaths[0])
|
||||
}
|
||||
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
// GetVolumes returs the list of volumes in the system.
|
||||
// https://docs.microsoft.com/es-es/windows/desktop/api/fileapi/nf-fileapi-findfirstvolumew
|
||||
func GetVolumes() ([]string, error) {
|
||||
buffer := make([]uint16, MAX_PATH+1)
|
||||
|
||||
var volumes []string
|
||||
|
||||
h, err := _FindFirstVolume(&buffer[0], uint32(len(buffer)))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "FindFirstVolumeW failed")
|
||||
}
|
||||
defer _FindVolumeClose(h)
|
||||
|
||||
for {
|
||||
volumes = append(volumes, syscall.UTF16ToString(buffer))
|
||||
|
||||
err = _FindNextVolume(h, &buffer[0], uint32(len(buffer)))
|
||||
if err != nil {
|
||||
if errors.Cause(err) == syscall.ERROR_NO_MORE_FILES {
|
||||
break
|
||||
}
|
||||
return nil, errors.Wrap(err, "FindNextVolumeW failed")
|
||||
}
|
||||
}
|
||||
|
||||
return volumes, nil
|
||||
}
|
||||
|
||||
// GetVolumePathsForVolume returns the list of volume paths for a volume.
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/FileAPI/nf-fileapi-getvolumepathnamesforvolumenamew
|
||||
func GetVolumePathsForVolume(volumeName string) ([]string, error) {
|
||||
var length uint32
|
||||
err := _GetVolumePathNamesForVolumeName(volumeName, nil, 0, &length)
|
||||
if errors.Cause(err) != syscall.ERROR_MORE_DATA {
|
||||
return nil, errors.Wrap(err, "GetVolumePathNamesForVolumeNameW failed to get needed buffer length")
|
||||
}
|
||||
if length == 0 {
|
||||
// Not mounted, no paths, that's ok
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
buffer := make([]uint16, length*(MAX_PATH+1))
|
||||
err = _GetVolumePathNamesForVolumeName(volumeName, &buffer[0], length, &length)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetVolumePathNamesForVolumeNameW failed")
|
||||
}
|
||||
|
||||
return UTF16SliceToStringSlice(buffer), nil
|
||||
}
|
||||
|
||||
// GlobalMemoryStatusEx retrieves information about the system's current usage
|
||||
// of both physical and virtual memory.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366589(v=vs.85).aspx
|
||||
func GlobalMemoryStatusEx() (MemoryStatusEx, error) {
|
||||
memoryStatusEx := MemoryStatusEx{length: sizeofMemoryStatusEx}
|
||||
err := _GlobalMemoryStatusEx(&memoryStatusEx)
|
||||
if err != nil {
|
||||
return MemoryStatusEx{}, errors.Wrap(err, "GlobalMemoryStatusEx failed")
|
||||
}
|
||||
|
||||
return memoryStatusEx, nil
|
||||
}
|
||||
|
||||
// GetProcessMemoryInfo retrieves information about the memory usage of the
|
||||
// specified process.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683219(v=vs.85).aspx
|
||||
func GetProcessMemoryInfo(handle syscall.Handle) (ProcessMemoryCountersEx, error) {
|
||||
processMemoryCountersEx := ProcessMemoryCountersEx{cb: sizeofProcessMemoryCountersEx}
|
||||
err := _GetProcessMemoryInfo(handle, &processMemoryCountersEx, processMemoryCountersEx.cb)
|
||||
if err != nil {
|
||||
return ProcessMemoryCountersEx{}, errors.Wrap(err, "GetProcessMemoryInfo failed")
|
||||
}
|
||||
|
||||
return processMemoryCountersEx, nil
|
||||
}
|
||||
|
||||
// GetProcessImageFileName Retrieves the name of the executable file for the
|
||||
// specified process.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683217(v=vs.85).aspx
|
||||
func GetProcessImageFileName(handle syscall.Handle) (string, error) {
|
||||
buffer := make([]uint16, MAX_PATH)
|
||||
_, err := _GetProcessImageFileName(handle, &buffer[0], uint32(len(buffer)))
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "GetProcessImageFileName failed")
|
||||
}
|
||||
|
||||
return syscall.UTF16ToString(buffer), nil
|
||||
}
|
||||
|
||||
// GetSystemTimes retrieves system timing information. On a multiprocessor
|
||||
// system, the values returned are the sum of the designated times across all
|
||||
// processors. The returned kernel time does not include the system idle time.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724400(v=vs.85).aspx
|
||||
func GetSystemTimes() (idle, kernel, user time.Duration, err error) {
|
||||
var idleTime, kernelTime, userTime syscall.Filetime
|
||||
err = _GetSystemTimes(&idleTime, &kernelTime, &userTime)
|
||||
if err != nil {
|
||||
return 0, 0, 0, errors.Wrap(err, "GetSystemTimes failed")
|
||||
}
|
||||
|
||||
idle = FiletimeToDuration(&idleTime)
|
||||
kernel = FiletimeToDuration(&kernelTime) // Kernel time includes idle time so we subtract it out.
|
||||
user = FiletimeToDuration(&userTime)
|
||||
|
||||
return idle, kernel - idle, user, nil
|
||||
}
|
||||
|
||||
// FiletimeToDuration converts a Filetime to a time.Duration. Do not use this
|
||||
// method to convert a Filetime to an actual clock time, for that use
|
||||
// Filetime.Nanosecond().
|
||||
func FiletimeToDuration(ft *syscall.Filetime) time.Duration {
|
||||
n := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime) // in 100-nanosecond intervals
|
||||
return time.Duration(n * 100)
|
||||
}
|
||||
|
||||
// GetDriveType Determines whether a disk drive is a removable, fixed, CD-ROM,
|
||||
// RAM disk, or network drive. A trailing backslash is required on the
|
||||
// rootPathName.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364939
|
||||
func GetDriveType(rootPathName string) (DriveType, error) {
|
||||
rootPathNamePtr, err := syscall.UTF16PtrFromString(rootPathName)
|
||||
if err != nil {
|
||||
return DRIVE_UNKNOWN, errors.Wrapf(err, "UTF16PtrFromString failed for rootPathName=%v", rootPathName)
|
||||
}
|
||||
|
||||
dt, err := _GetDriveType(rootPathNamePtr)
|
||||
if err != nil {
|
||||
return DRIVE_UNKNOWN, errors.Wrapf(err, "GetDriveType failed for rootPathName=%v", rootPathName)
|
||||
}
|
||||
|
||||
return dt, nil
|
||||
}
|
||||
|
||||
// GetFilesystemType returns file system type information at the given root path.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationw
|
||||
func GetFilesystemType(rootPathName string) (string, error) {
|
||||
rootPathNamePtr, err := syscall.UTF16PtrFromString(rootPathName)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "UTF16PtrFromString failed for rootPathName=%v", rootPathName)
|
||||
}
|
||||
|
||||
buffer := make([]uint16, MAX_PATH+1)
|
||||
success, err := _GetVolumeInformation(rootPathNamePtr, nil, 0, nil, nil, nil, &buffer[0], MAX_PATH)
|
||||
// check if CD-ROM or other type that is not supported in GetVolumeInformation function
|
||||
if err == ERROR_NOT_READY || err == ERROR_INVALID_FUNCTION {
|
||||
return "", nil
|
||||
}
|
||||
if !success {
|
||||
return "", errors.Wrap(err, "GetVolumeInformationW failed")
|
||||
}
|
||||
|
||||
return strings.ToLower(syscall.UTF16ToString(buffer)), nil
|
||||
}
|
||||
|
||||
// EnumProcesses retrieves the process identifier for each process object in the
|
||||
// system. This function can return a max of 65536 PIDs. If there are more
|
||||
// processes than that then this will not return them all.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682629(v=vs.85).aspx
|
||||
func EnumProcesses() ([]uint32, error) {
|
||||
enumProcesses := func(size int) ([]uint32, error) {
|
||||
var (
|
||||
pids = make([]uint32, size)
|
||||
sizeBytes = len(pids) * sizeofUint32
|
||||
bytesWritten uint32
|
||||
)
|
||||
|
||||
err := _EnumProcesses(&pids[0], uint32(sizeBytes), &bytesWritten)
|
||||
|
||||
pidsWritten := int(bytesWritten) / sizeofUint32
|
||||
if int(bytesWritten)%sizeofUint32 != 0 || pidsWritten > len(pids) {
|
||||
return nil, errors.Errorf("EnumProcesses returned an invalid bytesWritten value of %v", bytesWritten)
|
||||
}
|
||||
pids = pids[:pidsWritten]
|
||||
|
||||
return pids, err
|
||||
}
|
||||
|
||||
// Retry the EnumProcesses call with larger arrays if needed.
|
||||
size := 2048
|
||||
var pids []uint32
|
||||
for tries := 0; tries < 5; tries++ {
|
||||
var err error
|
||||
pids, err = enumProcesses(size)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "EnumProcesses failed")
|
||||
}
|
||||
|
||||
if len(pids) < size {
|
||||
break
|
||||
}
|
||||
|
||||
// Increase the size the pids array and retry the enumProcesses call
|
||||
// because the array wasn't large enough to hold all of the processes.
|
||||
size *= 2
|
||||
}
|
||||
|
||||
return pids, nil
|
||||
}
|
||||
|
||||
// GetDiskFreeSpaceEx retrieves information about the amount of space that is
|
||||
// available on a disk volume, which is the total amount of space, the total
|
||||
// amount of free space, and the total amount of free space available to the
|
||||
// user that is associated with the calling thread.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364937(v=vs.85).aspx
|
||||
func GetDiskFreeSpaceEx(directoryName string) (freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes uint64, err error) {
|
||||
directoryNamePtr, err := syscall.UTF16PtrFromString(directoryName)
|
||||
if err != nil {
|
||||
return 0, 0, 0, errors.Wrapf(err, "UTF16PtrFromString failed for directoryName=%v", directoryName)
|
||||
}
|
||||
|
||||
err = _GetDiskFreeSpaceEx(directoryNamePtr, &freeBytesAvailable, &totalNumberOfBytes, &totalNumberOfFreeBytes)
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
return freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes, nil
|
||||
}
|
||||
|
||||
// CreateToolhelp32Snapshot takes a snapshot of the specified processes, as well
|
||||
// as the heaps, modules, and threads used by these processes.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682489(v=vs.85).aspx
|
||||
func CreateToolhelp32Snapshot(flags, pid uint32) (syscall.Handle, error) {
|
||||
h, err := _CreateToolhelp32Snapshot(flags, pid)
|
||||
if err != nil {
|
||||
return syscall.InvalidHandle, err
|
||||
}
|
||||
if h == syscall.InvalidHandle {
|
||||
return syscall.InvalidHandle, syscall.GetLastError()
|
||||
}
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// Process32First retrieves information about the first process encountered in a
|
||||
// system snapshot.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684834
|
||||
func Process32First(handle syscall.Handle) (ProcessEntry32, error) {
|
||||
processEntry32 := ProcessEntry32{size: sizeofProcessEntry32}
|
||||
err := _Process32First(handle, &processEntry32)
|
||||
if err != nil {
|
||||
return ProcessEntry32{}, errors.Wrap(err, "Process32First failed")
|
||||
}
|
||||
|
||||
return processEntry32, nil
|
||||
}
|
||||
|
||||
// Process32Next retrieves information about the next process recorded in a
|
||||
// system snapshot. When there are no more processes to iterate then
|
||||
// syscall.ERROR_NO_MORE_FILES is returned (use errors.Cause() to unwrap).
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684836
|
||||
func Process32Next(handle syscall.Handle) (ProcessEntry32, error) {
|
||||
processEntry32 := ProcessEntry32{size: sizeofProcessEntry32}
|
||||
err := _Process32Next(handle, &processEntry32)
|
||||
if err != nil {
|
||||
return ProcessEntry32{}, errors.Wrap(err, "Process32Next failed")
|
||||
}
|
||||
|
||||
return processEntry32, nil
|
||||
}
|
||||
|
||||
// UTF16SliceToStringSlice converts slice of uint16 containing a list of UTF16
|
||||
// strings to a slice of strings.
|
||||
func UTF16SliceToStringSlice(buffer []uint16) []string {
|
||||
// Split the uint16 slice at null-terminators.
|
||||
var startIdx int
|
||||
var stringsUTF16 [][]uint16
|
||||
for i, value := range buffer {
|
||||
if value == 0 {
|
||||
stringsUTF16 = append(stringsUTF16, buffer[startIdx:i])
|
||||
startIdx = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the utf16 slices to strings.
|
||||
result := make([]string, 0, len(stringsUTF16))
|
||||
for _, stringUTF16 := range stringsUTF16 {
|
||||
if len(stringUTF16) > 0 {
|
||||
result = append(result, syscall.UTF16ToString(stringUTF16))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func GetUserProcessParams(handle syscall.Handle, pbi ProcessBasicInformation) (params RtlUserProcessParameters, err error) {
|
||||
const is32bitProc = unsafe.Sizeof(uintptr(0)) == 4
|
||||
|
||||
// Offset of params field within PEB structure.
|
||||
// This structure is different in 32 and 64 bit.
|
||||
paramsOffset := 0x20
|
||||
if is32bitProc {
|
||||
paramsOffset = 0x10
|
||||
}
|
||||
|
||||
// Read the PEB from the target process memory
|
||||
pebSize := paramsOffset + 8
|
||||
peb := make([]byte, pebSize)
|
||||
nRead, err := ReadProcessMemory(handle, pbi.PebBaseAddress, peb)
|
||||
if err != nil {
|
||||
return params, err
|
||||
}
|
||||
if nRead != uintptr(pebSize) {
|
||||
return params, errors.Errorf("PEB: short read (%d/%d)", nRead, pebSize)
|
||||
}
|
||||
|
||||
// Get the RTL_USER_PROCESS_PARAMETERS struct pointer from the PEB
|
||||
paramsAddr := *(*uintptr)(unsafe.Pointer(&peb[paramsOffset]))
|
||||
|
||||
// Read the RTL_USER_PROCESS_PARAMETERS from the target process memory
|
||||
paramsBuf := make([]byte, SizeOfRtlUserProcessParameters)
|
||||
nRead, err = ReadProcessMemory(handle, paramsAddr, paramsBuf)
|
||||
if err != nil {
|
||||
return params, err
|
||||
}
|
||||
if nRead != uintptr(SizeOfRtlUserProcessParameters) {
|
||||
return params, errors.Errorf("RTL_USER_PROCESS_PARAMETERS: short read (%d/%d)", nRead, SizeOfRtlUserProcessParameters)
|
||||
}
|
||||
|
||||
params = *(*RtlUserProcessParameters)(unsafe.Pointer(¶msBuf[0]))
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// ReadProcessUnicodeString returns a zero-terminated UTF-16 string from another
|
||||
// process's memory.
|
||||
func ReadProcessUnicodeString(handle syscall.Handle, s *UnicodeString) ([]byte, error) {
|
||||
// Allocate an extra UTF-16 null character at the end in case the read string
|
||||
// is not terminated.
|
||||
extra := 2
|
||||
if s.Size&1 != 0 {
|
||||
extra = 3 // If size is odd, need 3 nulls to terminate.
|
||||
}
|
||||
buf := make([]byte, int(s.Size)+extra)
|
||||
nRead, err := ReadProcessMemory(handle, s.Buffer, buf[:s.Size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if nRead != uintptr(s.Size) {
|
||||
return nil, errors.Errorf("unicode string: short read: (%d/%d)", nRead, s.Size)
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// ByteSliceToStringSlice uses CommandLineToArgv API to split an UTF-16 command
|
||||
// line string into a list of parameters.
|
||||
func ByteSliceToStringSlice(utf16 []byte) ([]string, error) {
|
||||
n := len(utf16)
|
||||
// Discard odd byte
|
||||
if n&1 != 0 {
|
||||
n--
|
||||
utf16 = utf16[:n]
|
||||
}
|
||||
if n == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
terminated := false
|
||||
for i := 0; i < n && !terminated; i += 2 {
|
||||
terminated = utf16[i] == 0 && utf16[i+1] == 0
|
||||
}
|
||||
if !terminated {
|
||||
// Append a null uint16 at the end if terminator is missing
|
||||
utf16 = append(utf16, 0, 0)
|
||||
}
|
||||
var numArgs int32
|
||||
argsWide, err := syscall.CommandLineToArgv((*uint16)(unsafe.Pointer(&utf16[0])), &numArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Free memory allocated for CommandLineToArgvW arguments.
|
||||
defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(argsWide)))
|
||||
|
||||
args := make([]string, numArgs)
|
||||
for idx := range args {
|
||||
args[idx] = syscall.UTF16ToString(argsWide[idx][:])
|
||||
}
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// ReadProcessMemory reads from another process memory. The Handle needs to have
|
||||
// the PROCESS_VM_READ right.
|
||||
// A zero-byte read is a no-op, no error is returned.
|
||||
func ReadProcessMemory(handle syscall.Handle, baseAddress uintptr, dest []byte) (numRead uintptr, err error) {
|
||||
n := len(dest)
|
||||
if n == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if err = _ReadProcessMemory(handle, baseAddress, uintptr(unsafe.Pointer(&dest[0])), uintptr(n), &numRead); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return numRead, nil
|
||||
}
|
||||
|
||||
func GetTickCount64() (uptime uint64, err error) {
|
||||
if uptime, err = _GetTickCount64(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uptime, nil
|
||||
}
|
||||
|
||||
// Windows API calls
|
||||
//sys _GlobalMemoryStatusEx(buffer *MemoryStatusEx) (err error) = kernel32.GlobalMemoryStatusEx
|
||||
//sys _GetLogicalDriveStringsW(bufferLength uint32, buffer *uint16) (length uint32, err error) = kernel32.GetLogicalDriveStringsW
|
||||
//sys _GetProcessMemoryInfo(handle syscall.Handle, psmemCounters *ProcessMemoryCountersEx, cb uint32) (err error) = psapi.GetProcessMemoryInfo
|
||||
//sys _GetProcessImageFileName(handle syscall.Handle, outImageFileName *uint16, size uint32) (length uint32, err error) = psapi.GetProcessImageFileNameW
|
||||
//sys _GetSystemTimes(idleTime *syscall.Filetime, kernelTime *syscall.Filetime, userTime *syscall.Filetime) (err error) = kernel32.GetSystemTimes
|
||||
//sys _GetDriveType(rootPathName *uint16) (dt DriveType, err error) = kernel32.GetDriveTypeW
|
||||
//sys _GetVolumeInformation(rootPathName *uint16, volumeName *uint16, volumeNameSize uint32, volumeSerialNumber *uint32, maximumComponentLength *uint32, fileSystemFlags *uint32, fileSystemName *uint16, fileSystemNameSize uint32) (success bool, err error) [true] = kernel32.GetVolumeInformationW
|
||||
//sys _EnumProcesses(processIds *uint32, sizeBytes uint32, bytesReturned *uint32) (err error) = psapi.EnumProcesses
|
||||
//sys _GetDiskFreeSpaceEx(directoryName *uint16, freeBytesAvailable *uint64, totalNumberOfBytes *uint64, totalNumberOfFreeBytes *uint64) (err error) = kernel32.GetDiskFreeSpaceExW
|
||||
//sys _Process32First(handle syscall.Handle, processEntry32 *ProcessEntry32) (err error) = kernel32.Process32FirstW
|
||||
//sys _Process32Next(handle syscall.Handle, processEntry32 *ProcessEntry32) (err error) = kernel32.Process32NextW
|
||||
//sys _CreateToolhelp32Snapshot(flags uint32, processID uint32) (handle syscall.Handle, err error) = kernel32.CreateToolhelp32Snapshot
|
||||
//sys _NtQuerySystemInformation(systemInformationClass uint32, systemInformation *byte, systemInformationLength uint32, returnLength *uint32) (ntstatus uint32, err error) = ntdll.NtQuerySystemInformation
|
||||
//sys _NtQueryInformationProcess(processHandle syscall.Handle, processInformationClass uint32, processInformation *byte, processInformationLength uint32, returnLength *uint32) (ntstatus uint32, err error) = ntdll.NtQueryInformationProcess
|
||||
//sys _LookupPrivilegeName(systemName string, luid *int64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
||||
//sys _LookupPrivilegeValue(systemName string, name string, luid *int64) (err error) = advapi32.LookupPrivilegeValueW
|
||||
//sys _AdjustTokenPrivileges(token syscall.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
||||
//sys _FindFirstVolume(volumeName *uint16, size uint32) (handle syscall.Handle, err error) = kernel32.FindFirstVolumeW
|
||||
//sys _FindNextVolume(handle syscall.Handle, volumeName *uint16, size uint32) (err error) = kernel32.FindNextVolumeW
|
||||
//sys _FindVolumeClose(handle syscall.Handle) (err error) = kernel32.FindVolumeClose
|
||||
//sys _GetVolumePathNamesForVolumeName(volumeName string, buffer *uint16, bufferSize uint32, length *uint32) (err error) = kernel32.GetVolumePathNamesForVolumeNameW
|
||||
//sys _ReadProcessMemory(handle syscall.Handle, baseAddress uintptr, buffer uintptr, size uintptr, numRead *uintptr) (err error) = kernel32.ReadProcessMemory
|
||||
//sys _GetTickCount64() (uptime uint64, err error) = kernel32.GetTickCount64
|
|
@ -0,0 +1,43 @@
|
|||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Version identifies a Windows version by major, minor, and build number.
|
||||
type Version struct {
|
||||
Major int
|
||||
Minor int
|
||||
Build int
|
||||
}
|
||||
|
||||
// GetWindowsVersion returns the Windows version information. Applications not
|
||||
// manifested for Windows 8.1 or Windows 10 will return the Windows 8 OS version
|
||||
// value (6.2).
|
||||
//
|
||||
// For a table of version numbers see:
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724833(v=vs.85).aspx
|
||||
func GetWindowsVersion() Version {
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx
|
||||
ver, err := syscall.GetVersion()
|
||||
if err != nil {
|
||||
// GetVersion should never return an error.
|
||||
panic(fmt.Errorf("GetVersion failed: %v", err))
|
||||
}
|
||||
|
||||
return Version{
|
||||
Major: int(ver & 0xFF),
|
||||
Minor: int(ver >> 8 & 0xFF),
|
||||
Build: int(ver >> 16),
|
||||
}
|
||||
}
|
||||
|
||||
// IsWindowsVistaOrGreater returns true if the Windows version is Vista or
|
||||
// greater.
|
||||
func (v Version) IsWindowsVistaOrGreater() bool {
|
||||
// Vista is 6.0.
|
||||
return v.Major >= 6 && v.Minor >= 0
|
||||
}
|
299
vendor/github.com/elastic/gosigar/sys/windows/zsyscall_windows.go
generated
vendored
Normal file
299
vendor/github.com/elastic/gosigar/sys/windows/zsyscall_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,299 @@
|
|||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
errERROR_EINVAL error = syscall.EINVAL
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return errERROR_EINVAL
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
modntdll = windows.NewLazySystemDLL("ntdll.dll")
|
||||
modpsapi = windows.NewLazySystemDLL("psapi.dll")
|
||||
|
||||
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
||||
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
|
||||
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
|
||||
procCreateToolhelp32Snapshot = modkernel32.NewProc("CreateToolhelp32Snapshot")
|
||||
procFindFirstVolumeW = modkernel32.NewProc("FindFirstVolumeW")
|
||||
procFindNextVolumeW = modkernel32.NewProc("FindNextVolumeW")
|
||||
procFindVolumeClose = modkernel32.NewProc("FindVolumeClose")
|
||||
procGetDiskFreeSpaceExW = modkernel32.NewProc("GetDiskFreeSpaceExW")
|
||||
procGetDriveTypeW = modkernel32.NewProc("GetDriveTypeW")
|
||||
procGetLogicalDriveStringsW = modkernel32.NewProc("GetLogicalDriveStringsW")
|
||||
procGetSystemTimes = modkernel32.NewProc("GetSystemTimes")
|
||||
procGetTickCount64 = modkernel32.NewProc("GetTickCount64")
|
||||
procGetVolumeInformationW = modkernel32.NewProc("GetVolumeInformationW")
|
||||
procGetVolumePathNamesForVolumeNameW = modkernel32.NewProc("GetVolumePathNamesForVolumeNameW")
|
||||
procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx")
|
||||
procProcess32FirstW = modkernel32.NewProc("Process32FirstW")
|
||||
procProcess32NextW = modkernel32.NewProc("Process32NextW")
|
||||
procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory")
|
||||
procNtQueryInformationProcess = modntdll.NewProc("NtQueryInformationProcess")
|
||||
procNtQuerySystemInformation = modntdll.NewProc("NtQuerySystemInformation")
|
||||
procEnumProcesses = modpsapi.NewProc("EnumProcesses")
|
||||
procGetProcessImageFileNameW = modpsapi.NewProc("GetProcessImageFileNameW")
|
||||
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo")
|
||||
)
|
||||
|
||||
func _AdjustTokenPrivileges(token syscall.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
|
||||
var _p0 uint32
|
||||
if releaseAll {
|
||||
_p0 = 1
|
||||
}
|
||||
r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
|
||||
success = r0 != 0
|
||||
if true {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _LookupPrivilegeName(systemName string, luid *int64, buffer *uint16, size *uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return __LookupPrivilegeName(_p0, luid, buffer, size)
|
||||
}
|
||||
|
||||
func __LookupPrivilegeName(systemName *uint16, luid *int64, buffer *uint16, size *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _LookupPrivilegeValue(systemName string, name string, luid *int64) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *uint16
|
||||
_p1, err = syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return __LookupPrivilegeValue(_p0, _p1, luid)
|
||||
}
|
||||
|
||||
func __LookupPrivilegeValue(systemName *uint16, name *uint16, luid *int64) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _CreateToolhelp32Snapshot(flags uint32, processID uint32) (handle syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procCreateToolhelp32Snapshot.Addr(), 2, uintptr(flags), uintptr(processID), 0)
|
||||
handle = syscall.Handle(r0)
|
||||
if handle == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _FindFirstVolume(volumeName *uint16, size uint32) (handle syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procFindFirstVolumeW.Addr(), 2, uintptr(unsafe.Pointer(volumeName)), uintptr(size), 0)
|
||||
handle = syscall.Handle(r0)
|
||||
if handle == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _FindNextVolume(handle syscall.Handle, volumeName *uint16, size uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procFindNextVolumeW.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(volumeName)), uintptr(size))
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _FindVolumeClose(handle syscall.Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procFindVolumeClose.Addr(), 1, uintptr(handle), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _GetDiskFreeSpaceEx(directoryName *uint16, freeBytesAvailable *uint64, totalNumberOfBytes *uint64, totalNumberOfFreeBytes *uint64) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procGetDiskFreeSpaceExW.Addr(), 4, uintptr(unsafe.Pointer(directoryName)), uintptr(unsafe.Pointer(freeBytesAvailable)), uintptr(unsafe.Pointer(totalNumberOfBytes)), uintptr(unsafe.Pointer(totalNumberOfFreeBytes)), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _GetDriveType(rootPathName *uint16) (dt DriveType, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procGetDriveTypeW.Addr(), 1, uintptr(unsafe.Pointer(rootPathName)), 0, 0)
|
||||
dt = DriveType(r0)
|
||||
if dt == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _GetLogicalDriveStringsW(bufferLength uint32, buffer *uint16) (length uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procGetLogicalDriveStringsW.Addr(), 2, uintptr(bufferLength), uintptr(unsafe.Pointer(buffer)), 0)
|
||||
length = uint32(r0)
|
||||
if length == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _GetSystemTimes(idleTime *syscall.Filetime, kernelTime *syscall.Filetime, userTime *syscall.Filetime) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetSystemTimes.Addr(), 3, uintptr(unsafe.Pointer(idleTime)), uintptr(unsafe.Pointer(kernelTime)), uintptr(unsafe.Pointer(userTime)))
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _GetTickCount64() (uptime uint64, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procGetTickCount64.Addr(), 0, 0, 0, 0)
|
||||
uptime = uint64(r0)
|
||||
if uptime == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _GetVolumeInformation(rootPathName *uint16, volumeName *uint16, volumeNameSize uint32, volumeSerialNumber *uint32, maximumComponentLength *uint32, fileSystemFlags *uint32, fileSystemName *uint16, fileSystemNameSize uint32) (success bool, err error) {
|
||||
r0, _, e1 := syscall.Syscall9(procGetVolumeInformationW.Addr(), 8, uintptr(unsafe.Pointer(rootPathName)), uintptr(unsafe.Pointer(volumeName)), uintptr(volumeNameSize), uintptr(unsafe.Pointer(volumeSerialNumber)), uintptr(unsafe.Pointer(maximumComponentLength)), uintptr(unsafe.Pointer(fileSystemFlags)), uintptr(unsafe.Pointer(fileSystemName)), uintptr(fileSystemNameSize), 0)
|
||||
success = r0 != 0
|
||||
if true {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _GetVolumePathNamesForVolumeName(volumeName string, buffer *uint16, bufferSize uint32, length *uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(volumeName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return __GetVolumePathNamesForVolumeName(_p0, buffer, bufferSize, length)
|
||||
}
|
||||
|
||||
func __GetVolumePathNamesForVolumeName(volumeName *uint16, buffer *uint16, bufferSize uint32, length *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procGetVolumePathNamesForVolumeNameW.Addr(), 4, uintptr(unsafe.Pointer(volumeName)), uintptr(unsafe.Pointer(buffer)), uintptr(bufferSize), uintptr(unsafe.Pointer(length)), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _GlobalMemoryStatusEx(buffer *MemoryStatusEx) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGlobalMemoryStatusEx.Addr(), 1, uintptr(unsafe.Pointer(buffer)), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _Process32First(handle syscall.Handle, processEntry32 *ProcessEntry32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procProcess32FirstW.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(processEntry32)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _Process32Next(handle syscall.Handle, processEntry32 *ProcessEntry32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procProcess32NextW.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(processEntry32)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _ReadProcessMemory(handle syscall.Handle, baseAddress uintptr, buffer uintptr, size uintptr, numRead *uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procReadProcessMemory.Addr(), 5, uintptr(handle), uintptr(baseAddress), uintptr(buffer), uintptr(size), uintptr(unsafe.Pointer(numRead)), 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _NtQueryInformationProcess(processHandle syscall.Handle, processInformationClass uint32, processInformation *byte, processInformationLength uint32, returnLength *uint32) (ntstatus uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procNtQueryInformationProcess.Addr(), 5, uintptr(processHandle), uintptr(processInformationClass), uintptr(unsafe.Pointer(processInformation)), uintptr(processInformationLength), uintptr(unsafe.Pointer(returnLength)), 0)
|
||||
ntstatus = uint32(r0)
|
||||
if ntstatus == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _NtQuerySystemInformation(systemInformationClass uint32, systemInformation *byte, systemInformationLength uint32, returnLength *uint32) (ntstatus uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procNtQuerySystemInformation.Addr(), 4, uintptr(systemInformationClass), uintptr(unsafe.Pointer(systemInformation)), uintptr(systemInformationLength), uintptr(unsafe.Pointer(returnLength)), 0, 0)
|
||||
ntstatus = uint32(r0)
|
||||
if ntstatus == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _EnumProcesses(processIds *uint32, sizeBytes uint32, bytesReturned *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procEnumProcesses.Addr(), 3, uintptr(unsafe.Pointer(processIds)), uintptr(sizeBytes), uintptr(unsafe.Pointer(bytesReturned)))
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _GetProcessImageFileName(handle syscall.Handle, outImageFileName *uint16, size uint32) (length uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procGetProcessImageFileNameW.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(outImageFileName)), uintptr(size))
|
||||
length = uint32(r0)
|
||||
if length == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _GetProcessMemoryInfo(handle syscall.Handle, psmemCounters *ProcessMemoryCountersEx, cb uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(psmemCounters)), uintptr(cb))
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/google/uuid"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -46,6 +47,13 @@ type Key struct {
|
|||
// we only store privkey as pubkey/address can be derived from it
|
||||
// privkey in this struct is always in plaintext
|
||||
PrivateKey *ecdsa.PrivateKey
|
||||
// ExtendedKey is the extended key of the PrivateKey itself, and it's used
|
||||
// to derive child keys.
|
||||
ExtendedKey *extkeys.ExtendedKey
|
||||
// SubAccountIndex is DEPRECATED
|
||||
// It was use in Status to keep track of the number of sub-account created
|
||||
// before having multi-account support.
|
||||
SubAccountIndex uint32
|
||||
}
|
||||
|
||||
type keyStore interface {
|
||||
|
@ -65,10 +73,12 @@ type plainKeyJSON struct {
|
|||
}
|
||||
|
||||
type encryptedKeyJSONV3 struct {
|
||||
Address string `json:"address"`
|
||||
Crypto CryptoJSON `json:"crypto"`
|
||||
Id string `json:"id"`
|
||||
Version int `json:"version"`
|
||||
Address string `json:"address"`
|
||||
Crypto CryptoJSON `json:"crypto"`
|
||||
Id string `json:"id"`
|
||||
Version int `json:"version"`
|
||||
ExtendedKey CryptoJSON `json:"extendedkey"`
|
||||
SubAccountIndex uint32 `json:"subaccountindex"`
|
||||
}
|
||||
|
||||
type encryptedKeyJSONV1 struct {
|
||||
|
@ -143,6 +153,43 @@ func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
|
|||
return key
|
||||
}
|
||||
|
||||
func newKeyForPurposeFromExtendedKey(keyPurpose extkeys.KeyPurpose, extKey *extkeys.ExtendedKey) (*Key, error) {
|
||||
var (
|
||||
extChild1, extChild2 *extkeys.ExtendedKey
|
||||
err error
|
||||
)
|
||||
|
||||
if extKey.Depth == 0 { // we are dealing with master key
|
||||
// CKD#1 - main account
|
||||
extChild1, err = extKey.ChildForPurpose(keyPurpose, 0)
|
||||
if err != nil {
|
||||
return &Key{}, err
|
||||
}
|
||||
|
||||
// CKD#2 - sub-accounts root
|
||||
extChild2, err = extKey.ChildForPurpose(keyPurpose, 1)
|
||||
if err != nil {
|
||||
return &Key{}, err
|
||||
}
|
||||
} else { // we are dealing with non-master key, so it is safe to persist and extend from it
|
||||
extChild1 = extKey
|
||||
extChild2 = extKey
|
||||
}
|
||||
|
||||
privateKeyECDSA := extChild1.ToECDSA()
|
||||
id, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := &Key{
|
||||
Id: id,
|
||||
Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
|
||||
PrivateKey: privateKeyECDSA,
|
||||
ExtendedKey: extChild2,
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// NewKeyForDirectICAP generates a key whose address fits into < 155 bits so it can fit
|
||||
// into the Direct ICAP spec. for simplicity and easier compatibility with other libs, we
|
||||
// retry until the first byte is 0.
|
||||
|
|
|
@ -37,6 +37,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/google/uuid"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -234,6 +236,11 @@ func (ks *KeyStore) Accounts() []accounts.Account {
|
|||
return ks.cache.accounts()
|
||||
}
|
||||
|
||||
// AccountDecryptedKey returns decrypted key for account (provided that password is correct).
|
||||
func (ks *KeyStore) AccountDecryptedKey(a accounts.Account, auth string) (accounts.Account, *Key, error) {
|
||||
return ks.getDecryptedKey(a, auth)
|
||||
}
|
||||
|
||||
// Delete deletes the key matched by account if the passphrase is correct.
|
||||
// If the account contains no filename, the address must match a unique key.
|
||||
func (ks *KeyStore) Delete(a accounts.Account, passphrase string) error {
|
||||
|
@ -460,13 +467,71 @@ func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (acco
|
|||
|
||||
key := newKeyFromECDSA(priv)
|
||||
if ks.cache.hasAddress(key.Address) {
|
||||
return accounts.Account{
|
||||
Address: key.Address,
|
||||
}, ErrAccountAlreadyExists
|
||||
return accounts.Account{}, ErrAccountAlreadyExists
|
||||
}
|
||||
return ks.importKey(key, passphrase)
|
||||
}
|
||||
|
||||
// ImportSingleExtendedKey imports an extended key setting it in both the PrivateKey and ExtendedKey fields
|
||||
// of the Key struct.
|
||||
// ImportExtendedKey is used in older version of Status where PrivateKey is set to be the BIP44 key at index 0,
|
||||
// and ExtendedKey is the extended key of the BIP44 key at index 1.
|
||||
func (ks *KeyStore) ImportSingleExtendedKey(extKey *extkeys.ExtendedKey, passphrase string) (accounts.Account, error) {
|
||||
privateKeyECDSA := extKey.ToECDSA()
|
||||
id, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return accounts.Account{}, err
|
||||
}
|
||||
key := &Key{
|
||||
Id: id,
|
||||
Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
|
||||
PrivateKey: privateKeyECDSA,
|
||||
ExtendedKey: extKey,
|
||||
}
|
||||
|
||||
if ks.cache.hasAddress(key.Address) {
|
||||
return accounts.Account{}, ErrAccountAlreadyExists
|
||||
}
|
||||
|
||||
return ks.importKey(key, passphrase)
|
||||
}
|
||||
|
||||
// ImportExtendedKey stores ECDSA key (obtained from extended key) along with CKD#2 (root for sub-accounts)
|
||||
// If key file is not found, it is created. Key is encrypted with the given passphrase.
|
||||
// Deprecated: status-go is now using ImportSingleExtendedKey
|
||||
func (ks *KeyStore) ImportExtendedKey(extKey *extkeys.ExtendedKey, passphrase string) (accounts.Account, error) {
|
||||
return ks.ImportExtendedKeyForPurpose(extkeys.KeyPurposeWallet, extKey, passphrase)
|
||||
}
|
||||
|
||||
// ImportExtendedKeyForPurpose stores ECDSA key (obtained from extended key) along with CKD#2 (root for sub-accounts)
|
||||
// If key file is not found, it is created. Key is encrypted with the given passphrase.
|
||||
// Deprecated: status-go is now using ImportSingleExtendedKey
|
||||
func (ks *KeyStore) ImportExtendedKeyForPurpose(keyPurpose extkeys.KeyPurpose, extKey *extkeys.ExtendedKey, passphrase string) (accounts.Account, error) {
|
||||
key, err := newKeyForPurposeFromExtendedKey(keyPurpose, extKey)
|
||||
if err != nil {
|
||||
zeroKey(key.PrivateKey)
|
||||
return accounts.Account{}, err
|
||||
}
|
||||
|
||||
// if account is already imported, return cached version
|
||||
if ks.cache.hasAddress(key.Address) {
|
||||
a := accounts.Account{
|
||||
Address: key.Address,
|
||||
}
|
||||
ks.cache.maybeReload()
|
||||
ks.cache.mu.Lock()
|
||||
a, err := ks.cache.find(a)
|
||||
ks.cache.mu.Unlock()
|
||||
if err != nil {
|
||||
zeroKey(key.PrivateKey)
|
||||
return a, err
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
return ks.importKey(key, passphrase)
|
||||
}
|
||||
|
||||
func (ks *KeyStore) importKey(key *Key, passphrase string) (accounts.Account, error) {
|
||||
a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.storage.JoinPath(keyFileName(key.Address))}}
|
||||
if err := ks.storage.StoreKey(a.URL.Path, key, passphrase); err != nil {
|
||||
|
@ -477,6 +542,15 @@ func (ks *KeyStore) importKey(key *Key, passphrase string) (accounts.Account, er
|
|||
return a, nil
|
||||
}
|
||||
|
||||
func (ks *KeyStore) IncSubAccountIndex(a accounts.Account, passphrase string) error {
|
||||
a, key, err := ks.getDecryptedKey(a, passphrase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key.SubAccountIndex++
|
||||
return ks.storage.StoreKey(a.URL.Path, key, passphrase)
|
||||
}
|
||||
|
||||
// Update changes the passphrase of an existing account.
|
||||
func (ks *KeyStore) Update(a accounts.Account, passphrase, newPassphrase string) error {
|
||||
a, key, err := ks.getDecryptedKey(a, passphrase)
|
||||
|
@ -500,6 +574,10 @@ func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (account
|
|||
|
||||
// zeroKey zeroes a private key in memory.
|
||||
func zeroKey(k *ecdsa.PrivateKey) {
|
||||
if k == nil {
|
||||
return
|
||||
}
|
||||
|
||||
b := k.D.Bits()
|
||||
for i := range b {
|
||||
b[i] = 0
|
||||
|
|
|
@ -43,6 +43,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/google/uuid"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
@ -189,15 +190,68 @@ func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encryptedExtendedKey, err := EncryptExtendedKey(key.ExtendedKey, auth, scryptN, scryptP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encryptedKeyJSONV3 := encryptedKeyJSONV3{
|
||||
hex.EncodeToString(key.Address[:]),
|
||||
cryptoStruct,
|
||||
key.Id.String(),
|
||||
version,
|
||||
encryptedExtendedKey,
|
||||
key.SubAccountIndex,
|
||||
}
|
||||
return json.Marshal(encryptedKeyJSONV3)
|
||||
}
|
||||
|
||||
func EncryptExtendedKey(extKey *extkeys.ExtendedKey, auth string, scryptN, scryptP int) (CryptoJSON, error) {
|
||||
if extKey == nil {
|
||||
return CryptoJSON{}, nil
|
||||
}
|
||||
authArray := []byte(auth)
|
||||
salt := make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
|
||||
panic("reading from crypto/rand failed: " + err.Error())
|
||||
}
|
||||
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen)
|
||||
if err != nil {
|
||||
return CryptoJSON{}, err
|
||||
}
|
||||
encryptKey := derivedKey[:16]
|
||||
keyBytes := []byte(extKey.String())
|
||||
|
||||
iv := make([]byte, aes.BlockSize) // 16
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic("reading from crypto/rand failed: " + err.Error())
|
||||
}
|
||||
cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv)
|
||||
if err != nil {
|
||||
return CryptoJSON{}, err
|
||||
}
|
||||
mac := crypto.Keccak256(derivedKey[16:32], cipherText)
|
||||
|
||||
scryptParamsJSON := make(map[string]interface{}, 5)
|
||||
scryptParamsJSON["n"] = scryptN
|
||||
scryptParamsJSON["r"] = scryptR
|
||||
scryptParamsJSON["p"] = scryptP
|
||||
scryptParamsJSON["dklen"] = scryptDKLen
|
||||
scryptParamsJSON["salt"] = hex.EncodeToString(salt)
|
||||
|
||||
cipherParamsJSON := cipherparamsJSON{
|
||||
IV: hex.EncodeToString(iv),
|
||||
}
|
||||
|
||||
return CryptoJSON{
|
||||
Cipher: "aes-128-ctr",
|
||||
CipherText: hex.EncodeToString(cipherText),
|
||||
CipherParams: cipherParamsJSON,
|
||||
KDF: "scrypt",
|
||||
KDFParams: scryptParamsJSON,
|
||||
MAC: hex.EncodeToString(mac),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DecryptKey decrypts a key from a json blob, returning the private key itself.
|
||||
func DecryptKey(keyjson []byte, auth string) (*Key, error) {
|
||||
// Parse the json into a simple map to fetch the key version
|
||||
|
@ -209,19 +263,41 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
|
|||
var (
|
||||
keyBytes, keyId []byte
|
||||
err error
|
||||
extKeyBytes []byte
|
||||
extKey *extkeys.ExtendedKey
|
||||
)
|
||||
|
||||
subAccountIndex, ok := m["subaccountindex"].(float64)
|
||||
if !ok {
|
||||
subAccountIndex = 0
|
||||
}
|
||||
|
||||
if version, ok := m["version"].(string); ok && version == "1" {
|
||||
k := new(encryptedKeyJSONV1)
|
||||
if err := json.Unmarshal(keyjson, k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyBytes, keyId, err = decryptKeyV1(k, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extKey, err = extkeys.NewKeyFromString(extkeys.EmptyExtendedKeyString)
|
||||
} else {
|
||||
k := new(encryptedKeyJSONV3)
|
||||
if err := json.Unmarshal(keyjson, k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyBytes, keyId, err = decryptKeyV3(k, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extKeyBytes, err = decryptExtendedKey(k, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extKey, err = extkeys.NewKeyFromString(string(extKeyBytes))
|
||||
}
|
||||
// Handle any decryption errors and return the key
|
||||
if err != nil {
|
||||
|
@ -233,9 +309,11 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
|
|||
return nil, err
|
||||
}
|
||||
return &Key{
|
||||
Id: id,
|
||||
Address: crypto.PubkeyToAddress(key.PublicKey),
|
||||
PrivateKey: key,
|
||||
Id: id,
|
||||
Address: crypto.PubkeyToAddress(key.PublicKey),
|
||||
PrivateKey: key,
|
||||
ExtendedKey: extKey,
|
||||
SubAccountIndex: uint32(subAccountIndex),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -329,6 +407,51 @@ func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byt
|
|||
return plainText, keyId, err
|
||||
}
|
||||
|
||||
func decryptExtendedKey(keyProtected *encryptedKeyJSONV3, auth string) (plainText []byte, err error) {
|
||||
if len(keyProtected.ExtendedKey.CipherText) == 0 {
|
||||
return []byte(extkeys.EmptyExtendedKeyString), nil
|
||||
}
|
||||
|
||||
if keyProtected.Version != version {
|
||||
return nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
|
||||
}
|
||||
|
||||
if keyProtected.ExtendedKey.Cipher != "aes-128-ctr" {
|
||||
return nil, fmt.Errorf("Cipher not supported: %v", keyProtected.ExtendedKey.Cipher)
|
||||
}
|
||||
|
||||
mac, err := hex.DecodeString(keyProtected.ExtendedKey.MAC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iv, err := hex.DecodeString(keyProtected.ExtendedKey.CipherParams.IV)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cipherText, err := hex.DecodeString(keyProtected.ExtendedKey.CipherText)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
derivedKey, err := getKDFKey(keyProtected.ExtendedKey, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
|
||||
if !bytes.Equal(calculatedMAC, mac) {
|
||||
return nil, ErrDecrypt
|
||||
}
|
||||
|
||||
plainText, err = aesCTRXOR(derivedKey[:16], cipherText, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plainText, err
|
||||
}
|
||||
|
||||
func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
|
||||
authArray := []byte(auth)
|
||||
salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
|
||||
|
|
|
@ -143,6 +143,8 @@ type Downloader struct {
|
|||
quitCh chan struct{} // Quit channel to signal termination
|
||||
quitLock sync.Mutex // Lock to prevent double closes
|
||||
|
||||
downloads sync.WaitGroup // Keeps track of the currently active downloads
|
||||
|
||||
// Testing hooks
|
||||
syncInitHook func(uint64, uint64) // Method to call upon initiating a new sync run
|
||||
bodyFetchHook func([]*types.Header) // Method to call upon starting a block body fetch
|
||||
|
@ -439,7 +441,9 @@ func (d *Downloader) getMode() SyncMode {
|
|||
// specified peer and head hash.
|
||||
func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.Int) (err error) {
|
||||
d.mux.Post(StartEvent{})
|
||||
d.downloads.Add(1)
|
||||
defer func() {
|
||||
d.downloads.Done()
|
||||
// reset on error
|
||||
if err != nil {
|
||||
d.mux.Post(FailedEvent{err})
|
||||
|
@ -560,14 +564,22 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I
|
|||
} else if mode == FullSync {
|
||||
fetchers = append(fetchers, d.processFullSyncContent)
|
||||
}
|
||||
return d.spawnSync(fetchers)
|
||||
return d.spawnSync(errCanceled, fetchers)
|
||||
}
|
||||
|
||||
// spawnSync runs d.process and all given fetcher functions to completion in
|
||||
// separate goroutines, returning the first error that appears.
|
||||
func (d *Downloader) spawnSync(fetchers []func() error) error {
|
||||
func (d *Downloader) spawnSync(errCancel error, fetchers []func() error) error {
|
||||
d.cancelLock.Lock()
|
||||
select {
|
||||
case <-d.cancelCh:
|
||||
d.cancelLock.Unlock()
|
||||
return errCancel
|
||||
default:
|
||||
}
|
||||
errc := make(chan error, len(fetchers))
|
||||
d.cancelWg.Add(len(fetchers))
|
||||
d.cancelLock.Unlock()
|
||||
for _, fn := range fetchers {
|
||||
fn := fn
|
||||
go func() { defer d.cancelWg.Done(); errc <- fn() }()
|
||||
|
@ -632,6 +644,10 @@ func (d *Downloader) Terminate() {
|
|||
|
||||
// Cancel any pending download requests
|
||||
d.Cancel()
|
||||
|
||||
// Wait, so external dependencies aren't destroyed
|
||||
// until the download processing is done.
|
||||
d.downloads.Wait()
|
||||
}
|
||||
|
||||
// fetchHead retrieves the head header and prior pivot block (if available) from
|
||||
|
|
|
@ -22,6 +22,7 @@ require (
|
|||
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf
|
||||
github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498
|
||||
github.com/edsrzf/mmap-go v1.0.0
|
||||
github.com/elastic/gosigar v0.14.1
|
||||
github.com/fatih/color v1.7.0
|
||||
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff
|
||||
|
@ -55,6 +56,7 @@ require (
|
|||
github.com/rs/cors v1.7.0
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible
|
||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4
|
||||
github.com/status-im/status-go/extkeys v1.1.2
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954
|
||||
github.com/tklauser/go-sysconf v0.3.5 // indirect
|
||||
|
|
|
@ -80,6 +80,7 @@ github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx2
|
|||
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||
|
@ -98,10 +99,8 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
|
|||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/cloudflare-go v0.14.0 h1:gFqGlGl/5f9UGXAaKapCGUfaTCgRKKnzu2VvzMZlOFA=
|
||||
github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304=
|
||||
github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ=
|
||||
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f h1:C43yEtQ6NIf4ftFXD/V55gnGFgPbMQobd//YlnLjUJ8=
|
||||
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
|
||||
|
@ -111,28 +110,28 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea h1:j4317fAZh7X6GqbFowYdYdI0L9bwxL07jyPZIdepyZ0=
|
||||
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
|
||||
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M=
|
||||
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bacAU/eSnV3dAxVpepaghAdhGoQ=
|
||||
github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
|
||||
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
|
||||
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/elastic/gosigar v0.14.1 h1:T0aQ7n/n2ZA9W7DmAnj60v+qzqKERdBgJBO1CG2W6rc=
|
||||
github.com/elastic/gosigar v0.14.1/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ethereum/go-ethereum v1.9.5/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c=
|
||||
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
|
||||
|
@ -140,10 +139,8 @@ github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod
|
|||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
|
||||
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||
|
@ -186,9 +183,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64=
|
||||
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
|
@ -240,7 +235,6 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
|
|||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=
|
||||
|
@ -254,16 +248,11 @@ github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM52
|
|||
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
|
||||
github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
|
||||
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
|
@ -289,7 +278,6 @@ github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hz
|
|||
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
||||
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0=
|
||||
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
|
@ -297,11 +285,9 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6
|
|||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
|
@ -318,7 +304,6 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
|
@ -354,13 +339,14 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3
|
|||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg=
|
||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
|
||||
github.com/status-im/status-go/extkeys v1.1.2 h1:FSjARgDathJ3rIapJt851LsIXP9Oyuu2M2jPJKuzloU=
|
||||
github.com/status-im/status-go/extkeys v1.1.2/go.mod h1:hCmFzb2jiiVF2voZKYbzuhOQiHHCmyLJsZJXrFFg7BY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
|
||||
|
@ -388,6 +374,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
|
@ -456,6 +443,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -485,7 +473,6 @@ golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988 h1:EjgCl+fVlIaPJSori0ikSz3uV0DOHKWOJFpv1sAAhBM=
|
||||
golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -527,7 +514,6 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
|||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
|
||||
gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
|
||||
|
@ -575,7 +561,6 @@ google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyz
|
|||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
|
@ -583,7 +568,6 @@ gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7
|
|||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 h1:a6cXbcDDUkSBlpnkWV1bJ+vv3mOgQEltEJ2rPxroVu0=
|
||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
|
||||
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
|
||||
|
@ -592,11 +576,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
|
@ -219,6 +219,14 @@ func NewPrivateAccountAPI(b Backend, nonceLock *AddrLocker) *PrivateAccountAPI {
|
|||
}
|
||||
}
|
||||
|
||||
func NewSubsetOfPrivateAccountAPI(am *accounts.Manager) *PrivateAccountAPI {
|
||||
return &PrivateAccountAPI{
|
||||
am: am,
|
||||
nonceLock: nil,
|
||||
b: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// listAccounts will return a list of addresses for accounts this node manages.
|
||||
func (s *PrivateAccountAPI) ListAccounts() []common.Address {
|
||||
return s.am.Accounts()
|
||||
|
@ -449,7 +457,7 @@ func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr c
|
|||
// Look up the wallet containing the requested signer
|
||||
account := accounts.Account{Address: addr}
|
||||
|
||||
wallet, err := s.b.AccountManager().Find(account)
|
||||
wallet, err := s.am.Find(account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
@ -45,13 +44,6 @@ var locationEnabled uint32
|
|||
// padded to to aid in alignment.
|
||||
var locationLength uint32
|
||||
|
||||
// fieldPadding is a global map with maximum field value lengths seen until now
|
||||
// to allow padding log contexts in a bit smarter way.
|
||||
var fieldPadding = make(map[string]int)
|
||||
|
||||
// fieldPaddingLock is a global mutex protecting the field padding map.
|
||||
var fieldPaddingLock sync.RWMutex
|
||||
|
||||
type Format interface {
|
||||
Format(r *Record) []byte
|
||||
}
|
||||
|
@ -171,19 +163,6 @@ func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) {
|
|||
k, v = errorKey, formatLogfmtValue(k, term)
|
||||
}
|
||||
|
||||
// XXX: we should probably check that all of your key bytes aren't invalid
|
||||
fieldPaddingLock.RLock()
|
||||
padding := fieldPadding[k]
|
||||
fieldPaddingLock.RUnlock()
|
||||
|
||||
length := utf8.RuneCountInString(v)
|
||||
if padding < length && length <= termCtxMaxPadding {
|
||||
padding = length
|
||||
|
||||
fieldPaddingLock.Lock()
|
||||
fieldPadding[k] = padding
|
||||
fieldPaddingLock.Unlock()
|
||||
}
|
||||
if color > 0 {
|
||||
fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=", color, k)
|
||||
} else {
|
||||
|
@ -191,9 +170,6 @@ func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) {
|
|||
buf.WriteByte('=')
|
||||
}
|
||||
buf.WriteString(v)
|
||||
if i < len(ctx)-2 && padding > length {
|
||||
buf.Write(bytes.Repeat([]byte{' '}, padding-length))
|
||||
}
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
// +build !ios,!android
|
||||
|
||||
package metrics
|
||||
|
||||
import "github.com/elastic/gosigar"
|
||||
|
||||
func readCPUStats(stats *CPUStats) {
|
||||
global := gosigar.Cpu{}
|
||||
global.Get()
|
||||
|
||||
stats.GlobalTime = int64(global.User + global.Nice + global.Sys)
|
||||
stats.GlobalWait = int64(global.Wait)
|
||||
stats.LocalTime = getProcessCPUTime()
|
||||
}
|
5
vendor/github.com/ethereum/go-ethereum/metrics/cpu_stats_android.go
generated
vendored
Normal file
5
vendor/github.com/ethereum/go-ethereum/metrics/cpu_stats_android.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
// +build android
|
||||
|
||||
package metrics
|
||||
|
||||
func readCPUStats(stats *CPUStats) {}
|
|
@ -0,0 +1,5 @@
|
|||
// +build ios
|
||||
|
||||
package metrics
|
||||
|
||||
func readCPUStats(stats *CPUStats) {}
|
|
@ -8,6 +8,7 @@ package metrics
|
|||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -26,6 +27,10 @@ var Enabled = false
|
|||
// for health monitoring and debug metrics that might impact runtime performance.
|
||||
var EnabledExpensive = false
|
||||
|
||||
// EnabledStr has the same function as Enabled but
|
||||
// it can be set during compilation (linking) time.
|
||||
var EnabledStr = "false"
|
||||
|
||||
// enablerFlags is the CLI flag names to use to enable metrics collections.
|
||||
var enablerFlags = []string{"metrics"}
|
||||
|
||||
|
@ -52,6 +57,12 @@ func init() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if v, err := strconv.ParseBool(EnabledStr); err == nil && v {
|
||||
log.Info("Enabling metrics collection")
|
||||
Enabled = true
|
||||
EnabledExpensive = true
|
||||
}
|
||||
}
|
||||
|
||||
// CollectProcessMetrics periodically collects various metrics about the running
|
||||
|
|
|
@ -30,6 +30,12 @@ import (
|
|||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
// PrivateAdminAPI is the collection of administrative API methods exposed only
|
||||
// over a secure RPC channel.
|
||||
type PrivateAdminAPI struct {
|
||||
node *Node // Node interfaced by this API
|
||||
}
|
||||
|
||||
// apis returns the collection of built-in RPC APIs.
|
||||
func (n *Node) apis() []rpc.API {
|
||||
return []rpc.API{
|
||||
|
@ -94,6 +100,24 @@ func (api *privateAdminAPI) RemovePeer(url string) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// DeletePeer disconnects and deletes forcefully a remote node.
|
||||
func (api *PrivateAdminAPI) DeletePeer(url string) (bool, error) {
|
||||
// Make sure the server is running, fail otherwise
|
||||
server := api.node.Server()
|
||||
if server == nil {
|
||||
return false, ErrNodeStopped
|
||||
}
|
||||
// Try to remove the url as a static peer and return
|
||||
node, err := enode.ParseV4(url)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid enode: %v", err)
|
||||
}
|
||||
if err := server.DeletePeer(node); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// AddTrustedPeer allows a remote node to always connect, even if slots are full
|
||||
func (api *privateAdminAPI) AddTrustedPeer(url string) (bool, error) {
|
||||
// Make sure the server is running, fail otherwise
|
||||
|
|
|
@ -353,6 +353,36 @@ func (srv *Server) RemovePeer(node *enode.Node) {
|
|||
}
|
||||
}
|
||||
|
||||
// DeletePeer deletes the given node forcefully.
|
||||
func (srv *Server) DeletePeer(node *enode.Node) error {
|
||||
peerIDStr := node.ID().String()
|
||||
|
||||
srv.log.Info("DeletePeer called", "peerID", peerIDStr)
|
||||
|
||||
var peer *Peer
|
||||
for _, p := range srv.Peers() {
|
||||
if p.ID() == node.ID() {
|
||||
peer = p
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if peer == nil {
|
||||
err := errors.New("peer not found")
|
||||
srv.log.Info("DeletePeer failed to match a peer", "peerID", peerIDStr, "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
select {
|
||||
case srv.delpeer <- peerDrop{peer, errors.New("forced delete"), true}:
|
||||
case <-srv.quit:
|
||||
}
|
||||
|
||||
srv.log.Info("DeletePeer passed the request to delpeer channel", "peerID", peerIDStr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddTrustedPeer adds the given node to a reserved whitelist which allows the
|
||||
// node to always connect, even if the slot are full.
|
||||
func (srv *Server) AddTrustedPeer(node *enode.Node) {
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
module github.com/status-im/status-go/extkeys
|
||||
|
||||
go 1.13
|
||||
|
||||
replace github.com/ethereum/go-ethereum v1.9.5 => github.com/status-im/go-ethereum v1.9.5-status.7
|
||||
|
||||
require (
|
||||
github.com/btcsuite/btcd v0.20.1-beta
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d
|
||||
github.com/ethereum/go-ethereum v1.9.5
|
||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c
|
||||
golang.org/x/text v0.3.2
|
||||
)
|
|
@ -0,0 +1,102 @@
|
|||
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
||||
github.com/Azure/azure-pipeline-go v0.0.0-20180607212504-7571e8eb0876/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
|
||||
github.com/Azure/azure-storage-blob-go v0.0.0-20180712005634-eaae161d9d5e/go.mod h1:x2mtS6O3mnMEZOJp7d7oldh8IvatBrMfReiyQ+cKgKY=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/allegro/bigcache v0.0.0-20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ=
|
||||
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20170201225849-2268707a8f08/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/docker/docker v0.0.0-20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/elastic/gosigar v0.0.0-20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs=
|
||||
github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gizak/termui v0.0.0-20170117222342-991cd3d38091/go.mod h1:PkJoWUt/zacQKysNfQtcw1RW+eK2SxkieVBtl+4ovLA=
|
||||
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||
github.com/go-stack/stack v1.5.4/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag=
|
||||
github.com/influxdata/influxdb v0.0.0-20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
|
||||
github.com/jackpal/go-nat-pmp v0.0.0-20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/julienschmidt/httprouter v0.0.0-20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/karalabe/hid v0.0.0-20181128192157-d815e0c1a2e2/go.mod h1:YvbcH+3Wo6XPs9nkgTY3u19KXLauXW+J5nB7hEHuX0A=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e/go.mod h1:nty42YY5QByNC5MM7q/nj938VbgPU7avs45z6NClpxI=
|
||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.0-20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/naoina/toml v0.0.0-20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
||||
github.com/nsf/termbox-go v0.0.0-20170211012700-3540b76b9c77/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170128050532-febf2d34b54a/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opentracing/opentracing-go v0.0.0-20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
|
||||
github.com/peterh/liner v0.0.0-20170902204657-a37ad3984311/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||
github.com/pkg/errors v0.0.0-20171216070316-e881fd58d78e/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/prometheus v0.0.0-20170814170113-3101606756c5/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s=
|
||||
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
|
||||
github.com/robertkrimen/otto v0.0.0-20170205013659-6a77b7cbc37d/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY=
|
||||
github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ=
|
||||
github.com/status-im/go-ethereum v1.9.5-status.7 h1:DKH1GiF52LwaZaw6YDBliFEgm/JDsbIT+hn7ph6X94Q=
|
||||
github.com/status-im/go-ethereum v1.9.5-status.7/go.mod h1:YyH5DKB6+z+Vaya7eIm67pnuPZ1oiUMbbsZW41ktN0g=
|
||||
github.com/status-im/status-go/extkeys v1.0.0/go.mod h1:GdqJbrcpkNm5ZsSCpp+PdMxnXx+OcRBdm3PI0rs1FpU=
|
||||
github.com/stretchr/testify v0.0.0-20170809224252-890a5c3458b4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/syndtr/goleveldb v0.0.0-20181128100959-b001fa50d6b2/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
||||
github.com/uber/jaeger-client-go v0.0.0-20180607151842-f7e0d4744fa6/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
|
||||
github.com/uber/jaeger-lib v0.0.0-20180615202729-a51202d6f4a7/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba h1:9bFeDpN3gTqNanMVqNcoR/pJQuP5uroC3t1D7eXozTE=
|
||||
golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4=
|
||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405 h1:829vOVxxusYHC+IqBtkX5mbKtsY9fheQiQn0MZRVLfQ=
|
||||
gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20180302121509-abf0ba0be5d5/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
|
||||
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
@ -0,0 +1,491 @@
|
|||
package extkeys
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcutil/base58"
|
||||
)
|
||||
|
||||
// Implementation of the following BIPs:
|
||||
// - BIP32 (https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
|
||||
// - BIP39 (https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki)
|
||||
// - BIP44 (https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)
|
||||
//
|
||||
// Referencing
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
|
||||
// https://bitcoin.org/en/developer-guide#hardened-keys
|
||||
|
||||
// Reference Implementations
|
||||
// https://github.com/btcsuite/btcutil/tree/master/hdkeychain
|
||||
// https://github.com/WeMeetAgain/go-hdwallet
|
||||
|
||||
// https://github.com/ConsenSys/eth-lightwallet/blob/master/lib/keystore.js
|
||||
// https://github.com/bitpay/bitcore-lib/tree/master/lib
|
||||
|
||||
// MUST CREATE HARDENED CHILDREN OF THE MASTER PRIVATE KEY (M) TO PREVENT
|
||||
// A COMPROMISED CHILD KEY FROM COMPROMISING THE MASTER KEY.
|
||||
// AS THERE ARE NO NORMAL CHILDREN FOR THE MASTER KEYS,
|
||||
// THE MASTER PUBLIC KEY IS NOT USED IN HD WALLETS.
|
||||
// ALL OTHER KEYS CAN HAVE NORMAL CHILDREN,
|
||||
// SO THE CORRESPONDING EXTENDED PUBLIC KEYS MAY BE USED INSTEAD.
|
||||
|
||||
// TODO make sure we're doing this ^^^^ !!!!!!
|
||||
|
||||
type KeyPurpose int
|
||||
|
||||
const (
|
||||
KeyPurposeWallet KeyPurpose = iota + 1
|
||||
KeyPurposeChat
|
||||
)
|
||||
|
||||
const (
|
||||
// HardenedKeyStart defines a starting point for hardened key.
|
||||
// Each extended key has 2^31 normal child keys and 2^31 hardened child keys.
|
||||
// Thus the range for normal child keys is [0, 2^31 - 1] and the range for hardened child keys is [2^31, 2^32 - 1].
|
||||
HardenedKeyStart = 0x80000000 // 2^31
|
||||
|
||||
// MinSeedBytes is the minimum number of bytes allowed for a seed to a master node.
|
||||
MinSeedBytes = 16 // 128 bits
|
||||
|
||||
// MaxSeedBytes is the maximum number of bytes allowed for a seed to a master node.
|
||||
MaxSeedBytes = 64 // 512 bits
|
||||
|
||||
// serializedKeyLen is the length of a serialized public or private
|
||||
// extended key. It consists of 4 bytes version, 1 byte depth, 4 bytes
|
||||
// fingerprint, 4 bytes child number, 32 bytes chain code, and 33 bytes
|
||||
// public/private key data.
|
||||
serializedKeyLen = 4 + 1 + 4 + 4 + 32 + 33 // 78 bytes
|
||||
|
||||
// CoinTypeBTC is BTC coin type
|
||||
CoinTypeBTC = 0 // 0x80000000
|
||||
|
||||
// CoinTypeTestNet is test net coin type
|
||||
CoinTypeTestNet = 1 // 0x80000001
|
||||
|
||||
// CoinTypeETH is ETH coin type
|
||||
CoinTypeETH = 60 // 0x8000003c
|
||||
|
||||
// EmptyExtendedKeyString marker string for zero extended key
|
||||
EmptyExtendedKeyString = "Zeroed extended key"
|
||||
|
||||
// MaxDepth is the maximum depth of an extended key.
|
||||
// Extended keys with depth MaxDepth cannot derive child keys.
|
||||
MaxDepth = 255
|
||||
)
|
||||
|
||||
// errors
|
||||
var (
|
||||
ErrInvalidKey = errors.New("key is invalid")
|
||||
ErrInvalidKeyPurpose = errors.New("key purpose is invalid")
|
||||
ErrInvalidSeed = errors.New("seed is invalid")
|
||||
ErrInvalidSeedLen = fmt.Errorf("the recommended size of seed is %d-%d bits", MinSeedBytes, MaxSeedBytes)
|
||||
ErrDerivingHardenedFromPublic = errors.New("cannot derive a hardened key from public key")
|
||||
ErrBadChecksum = errors.New("bad extended key checksum")
|
||||
ErrInvalidKeyLen = errors.New("serialized extended key length is invalid")
|
||||
ErrDerivingChild = errors.New("error deriving child key")
|
||||
ErrInvalidMasterKey = errors.New("invalid master key supplied")
|
||||
ErrMaxDepthExceeded = errors.New("max depth exceeded")
|
||||
)
|
||||
|
||||
var (
|
||||
// PrivateKeyVersion is version for private key
|
||||
PrivateKeyVersion, _ = hex.DecodeString("0488ADE4")
|
||||
|
||||
// PublicKeyVersion is version for public key
|
||||
PublicKeyVersion, _ = hex.DecodeString("0488B21E")
|
||||
|
||||
// EthBIP44ParentPath is BIP44 keys parent's derivation path
|
||||
EthBIP44ParentPath = []uint32{
|
||||
HardenedKeyStart + 44, // purpose
|
||||
HardenedKeyStart + CoinTypeETH, // cointype set to ETH
|
||||
HardenedKeyStart + 0, // account
|
||||
0, // 0 - public, 1 - private
|
||||
}
|
||||
|
||||
// EIP1581KeyTypeChat is used as chat key_type in the derivation of EIP1581 keys
|
||||
EIP1581KeyTypeChat uint32 = 0x00
|
||||
|
||||
// EthEIP1581ChatParentPath is EIP-1581 chat keys parent's derivation path
|
||||
EthEIP1581ChatParentPath = []uint32{
|
||||
HardenedKeyStart + 43, // purpose
|
||||
HardenedKeyStart + CoinTypeETH, // cointype set to ETH
|
||||
HardenedKeyStart + 1581, // EIP-1581 subpurpose
|
||||
HardenedKeyStart + EIP1581KeyTypeChat, // key_type (chat)
|
||||
}
|
||||
)
|
||||
|
||||
// ExtendedKey represents BIP44-compliant HD key
|
||||
type ExtendedKey struct {
|
||||
Version []byte // 4 bytes, mainnet: 0x0488B21E public, 0x0488ADE4 private; testnet: 0x043587CF public, 0x04358394 private
|
||||
Depth uint8 // 1 byte, depth: 0x00 for master nodes, 0x01 for level-1 derived keys, ....
|
||||
FingerPrint []byte // 4 bytes, fingerprint of the parent's key (0x00000000 if master key)
|
||||
ChildNumber uint32 // 4 bytes, This is ser32(i) for i in xi = xpar/i, with xi the key being serialized. (0x00000000 if master key)
|
||||
KeyData []byte // 33 bytes, the public key or private key data (serP(K) for public keys, 0x00 || ser256(k) for private keys)
|
||||
ChainCode []byte // 32 bytes, the chain code
|
||||
IsPrivate bool // (non-serialized) if false, this chain will only contain a public key and can only create a public key chain.
|
||||
CachedPubKeyData []byte // (non-serialized) used for memoization of public key (calculated from a private key)
|
||||
}
|
||||
|
||||
// nolint: gas
|
||||
const masterSecret = "Bitcoin seed"
|
||||
|
||||
// NewMaster creates new master node, root of HD chain/tree.
|
||||
// Both master and child nodes are of ExtendedKey type, and all the children derive from the root node.
|
||||
func NewMaster(seed []byte) (*ExtendedKey, error) {
|
||||
// Ensure seed is within expected limits
|
||||
lseed := len(seed)
|
||||
if lseed < MinSeedBytes || lseed > MaxSeedBytes {
|
||||
return nil, ErrInvalidSeedLen
|
||||
}
|
||||
|
||||
secretKey, chainCode, err := splitHMAC(seed, []byte(masterSecret))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
master := &ExtendedKey{
|
||||
Version: PrivateKeyVersion,
|
||||
Depth: 0,
|
||||
FingerPrint: []byte{0x00, 0x00, 0x00, 0x00},
|
||||
ChildNumber: 0,
|
||||
KeyData: secretKey,
|
||||
ChainCode: chainCode,
|
||||
IsPrivate: true,
|
||||
}
|
||||
|
||||
return master, nil
|
||||
}
|
||||
|
||||
// Child derives extended key at a given index i.
|
||||
// If parent is private, then derived key is also private. If parent is public, then derived is public.
|
||||
//
|
||||
// If i >= HardenedKeyStart, then hardened key is generated.
|
||||
// You can only generate hardened keys from private parent keys.
|
||||
// If you try generating hardened key form public parent key, ErrDerivingHardenedFromPublic is returned.
|
||||
//
|
||||
// There are four CKD (child key derivation) scenarios:
|
||||
// 1) Private extended key -> Hardened child private extended key
|
||||
// 2) Private extended key -> Non-hardened child private extended key
|
||||
// 3) Public extended key -> Non-hardened child public extended key
|
||||
// 4) Public extended key -> Hardened child public extended key (INVALID!)
|
||||
func (k *ExtendedKey) Child(i uint32) (*ExtendedKey, error) {
|
||||
if k.Depth == MaxDepth {
|
||||
return nil, ErrMaxDepthExceeded
|
||||
}
|
||||
|
||||
// A hardened child may not be created from a public extended key (Case #4).
|
||||
isChildHardened := i >= HardenedKeyStart
|
||||
if !k.IsPrivate && isChildHardened {
|
||||
return nil, ErrDerivingHardenedFromPublic
|
||||
}
|
||||
|
||||
keyLen := 33
|
||||
seed := make([]byte, keyLen+4)
|
||||
if isChildHardened {
|
||||
// Case #1: 0x00 || ser256(parentKey) || ser32(i)
|
||||
copy(seed[1:], k.KeyData) // 0x00 || ser256(parentKey)
|
||||
} else {
|
||||
// Case #2 and #3: serP(parentPubKey) || ser32(i)
|
||||
copy(seed, k.pubKeyBytes())
|
||||
}
|
||||
binary.BigEndian.PutUint32(seed[keyLen:], i)
|
||||
|
||||
secretKey, chainCode, err := splitHMAC(seed, k.ChainCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
child := &ExtendedKey{
|
||||
ChainCode: chainCode,
|
||||
Depth: k.Depth + 1,
|
||||
ChildNumber: i,
|
||||
IsPrivate: k.IsPrivate,
|
||||
// The fingerprint for the derived child is the first 4 bytes of parent's
|
||||
FingerPrint: btcutil.Hash160(k.pubKeyBytes())[:4],
|
||||
}
|
||||
|
||||
if k.IsPrivate {
|
||||
// Case #1 or #2: childKey = parse256(IL) + parentKey
|
||||
parentKeyBigInt := new(big.Int).SetBytes(k.KeyData)
|
||||
keyBigInt := new(big.Int).SetBytes(secretKey)
|
||||
keyBigInt.Add(keyBigInt, parentKeyBigInt)
|
||||
keyBigInt.Mod(keyBigInt, btcec.S256().N)
|
||||
|
||||
// Make sure that child.KeyData is 32 bytes of data even if the value is represented with less bytes.
|
||||
// When we derive a child of this key, we call splitHMAC that does a sha512 of a seed that is:
|
||||
// - 1 byte with 0x00
|
||||
// - 32 bytes for the key data
|
||||
// - 4 bytes for the child key index
|
||||
// If we don't padd the KeyData, it will be shifted to left in that 32 bytes space
|
||||
// generating a different seed and different child key.
|
||||
// This part fixes a bug we had previously and described at:
|
||||
// https://medium.com/@alexberegszaszi/why-do-my-bip32-wallets-disagree-6f3254cc5846#.86inuifuq
|
||||
keyData := keyBigInt.Bytes()
|
||||
if len(keyData) < 32 {
|
||||
extra := make([]byte, 32-len(keyData))
|
||||
keyData = append(extra, keyData...)
|
||||
}
|
||||
|
||||
child.KeyData = keyData
|
||||
child.Version = PrivateKeyVersion
|
||||
} else {
|
||||
// Case #3: childKey = serP(point(parse256(IL)) + parentKey)
|
||||
|
||||
// Calculate the corresponding intermediate public key for intermediate private key.
|
||||
keyx, keyy := btcec.S256().ScalarBaseMult(secretKey)
|
||||
if keyx.Sign() == 0 || keyy.Sign() == 0 {
|
||||
return nil, ErrInvalidKey
|
||||
}
|
||||
|
||||
// Convert the serialized compressed parent public key into X and Y coordinates
|
||||
// so it can be added to the intermediate public key.
|
||||
pubKey, err := btcec.ParsePubKey(k.KeyData, btcec.S256())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// childKey = serP(point(parse256(IL)) + parentKey)
|
||||
childX, childY := btcec.S256().Add(keyx, keyy, pubKey.X, pubKey.Y)
|
||||
pk := btcec.PublicKey{Curve: btcec.S256(), X: childX, Y: childY}
|
||||
child.KeyData = pk.SerializeCompressed()
|
||||
child.Version = PublicKeyVersion
|
||||
}
|
||||
return child, nil
|
||||
}
|
||||
|
||||
// ChildForPurpose derives the child key at index i using a derivation path based on the purpose.
|
||||
func (k *ExtendedKey) ChildForPurpose(p KeyPurpose, i uint32) (*ExtendedKey, error) {
|
||||
switch p {
|
||||
case KeyPurposeWallet:
|
||||
return k.EthBIP44Child(i)
|
||||
case KeyPurposeChat:
|
||||
return k.EthEIP1581ChatChild(i)
|
||||
default:
|
||||
return nil, ErrInvalidKeyPurpose
|
||||
}
|
||||
}
|
||||
|
||||
// BIP44Child returns Status CKD#i (where i is child index).
|
||||
// BIP44 format is used: m / purpose' / coin_type' / account' / change / address_index
|
||||
// BIP44Child is depracated in favour of EthBIP44Child
|
||||
// Param coinType is deprecated; we override it to always use CoinTypeETH.
|
||||
func (k *ExtendedKey) BIP44Child(coinType, i uint32) (*ExtendedKey, error) {
|
||||
return k.EthBIP44Child(i)
|
||||
}
|
||||
|
||||
// BIP44Child returns Status CKD#i (where i is child index).
|
||||
// BIP44 format is used: m / purpose' / coin_type' / account' / change / address_index
|
||||
func (k *ExtendedKey) EthBIP44Child(i uint32) (*ExtendedKey, error) {
|
||||
if !k.IsPrivate {
|
||||
return nil, ErrInvalidMasterKey
|
||||
}
|
||||
|
||||
if k.Depth != 0 {
|
||||
return nil, ErrInvalidMasterKey
|
||||
}
|
||||
|
||||
// m/44'/60'/0'/0/index
|
||||
extKey, err := k.Derive(append(EthBIP44ParentPath, i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return extKey, nil
|
||||
}
|
||||
|
||||
// EthEIP1581ChatChild returns the whisper key #i (where i is child index).
|
||||
// EthEIP1581ChatChild format is used is the one defined in the EIP-1581:
|
||||
// m / 43' / coin_type' / 1581' / key_type / index
|
||||
func (k *ExtendedKey) EthEIP1581ChatChild(i uint32) (*ExtendedKey, error) {
|
||||
if !k.IsPrivate {
|
||||
return nil, ErrInvalidMasterKey
|
||||
}
|
||||
|
||||
if k.Depth != 0 {
|
||||
return nil, ErrInvalidMasterKey
|
||||
}
|
||||
|
||||
// m/43'/60'/1581'/0/index
|
||||
extKey, err := k.Derive(append(EthEIP1581ChatParentPath, i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return extKey, nil
|
||||
}
|
||||
|
||||
// Derive returns a derived child key at a given path
|
||||
func (k *ExtendedKey) Derive(path []uint32) (*ExtendedKey, error) {
|
||||
var err error
|
||||
extKey := k
|
||||
for _, i := range path {
|
||||
extKey, err = extKey.Child(i)
|
||||
if err != nil {
|
||||
return nil, ErrDerivingChild
|
||||
}
|
||||
}
|
||||
|
||||
return extKey, nil
|
||||
}
|
||||
|
||||
// Neuter returns a new extended public key from a give extended private key.
|
||||
// If the input extended key is already public, it will be returned unaltered.
|
||||
func (k *ExtendedKey) Neuter() (*ExtendedKey, error) {
|
||||
// Already an extended public key.
|
||||
if !k.IsPrivate {
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// Get the associated public extended key version bytes.
|
||||
version, err := chaincfg.HDPrivateKeyToPublicKeyID(k.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert it to an extended public key. The key for the new extended
|
||||
// key will simply be the pubkey of the current extended private key.
|
||||
return &ExtendedKey{
|
||||
Version: version,
|
||||
KeyData: k.pubKeyBytes(),
|
||||
ChainCode: k.ChainCode,
|
||||
FingerPrint: k.FingerPrint,
|
||||
Depth: k.Depth,
|
||||
ChildNumber: k.ChildNumber,
|
||||
IsPrivate: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IsZeroed returns true if key is nil or empty
|
||||
func (k *ExtendedKey) IsZeroed() bool {
|
||||
return k == nil || len(k.KeyData) == 0
|
||||
}
|
||||
|
||||
// String returns the extended key as a human-readable base58-encoded string.
|
||||
func (k *ExtendedKey) String() string {
|
||||
if k.IsZeroed() {
|
||||
return EmptyExtendedKeyString
|
||||
}
|
||||
|
||||
var childNumBytes [4]byte
|
||||
binary.BigEndian.PutUint32(childNumBytes[:], k.ChildNumber)
|
||||
|
||||
// The serialized format is:
|
||||
// version (4) || depth (1) || parent fingerprint (4)) ||
|
||||
// child num (4) || chain code (32) || key data (33) || checksum (4)
|
||||
serializedBytes := make([]byte, 0, serializedKeyLen+4)
|
||||
serializedBytes = append(serializedBytes, k.Version...)
|
||||
serializedBytes = append(serializedBytes, k.Depth)
|
||||
serializedBytes = append(serializedBytes, k.FingerPrint...)
|
||||
serializedBytes = append(serializedBytes, childNumBytes[:]...)
|
||||
serializedBytes = append(serializedBytes, k.ChainCode...)
|
||||
if k.IsPrivate {
|
||||
serializedBytes = append(serializedBytes, 0x00)
|
||||
serializedBytes = paddedAppend(32, serializedBytes, k.KeyData)
|
||||
} else {
|
||||
serializedBytes = append(serializedBytes, k.pubKeyBytes()...)
|
||||
}
|
||||
|
||||
checkSum := chainhash.DoubleHashB(serializedBytes)[:4]
|
||||
serializedBytes = append(serializedBytes, checkSum...)
|
||||
return base58.Encode(serializedBytes)
|
||||
}
|
||||
|
||||
// pubKeyBytes returns bytes for the serialized compressed public key associated
|
||||
// with this extended key in an efficient manner including memoization as
|
||||
// necessary.
|
||||
//
|
||||
// When the extended key is already a public key, the key is simply returned as
|
||||
// is since it's already in the correct form. However, when the extended key is
|
||||
// a private key, the public key will be calculated and memoized so future
|
||||
// accesses can simply return the cached result.
|
||||
func (k *ExtendedKey) pubKeyBytes() []byte {
|
||||
// Just return the key if it's already an extended public key.
|
||||
if !k.IsPrivate {
|
||||
return k.KeyData
|
||||
}
|
||||
|
||||
pkx, pky := btcec.S256().ScalarBaseMult(k.KeyData)
|
||||
pubKey := btcec.PublicKey{Curve: btcec.S256(), X: pkx, Y: pky}
|
||||
return pubKey.SerializeCompressed()
|
||||
}
|
||||
|
||||
// ToECDSA returns the key data as ecdsa.PrivateKey
|
||||
func (k *ExtendedKey) ToECDSA() *ecdsa.PrivateKey {
|
||||
privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), k.KeyData)
|
||||
return privKey.ToECDSA()
|
||||
}
|
||||
|
||||
// NewKeyFromString returns a new extended key instance from a base58-encoded
|
||||
// extended key.
|
||||
func NewKeyFromString(key string) (*ExtendedKey, error) {
|
||||
if key == EmptyExtendedKeyString || len(key) == 0 {
|
||||
return &ExtendedKey{}, nil
|
||||
}
|
||||
|
||||
// The base58-decoded extended key must consist of a serialized payload
|
||||
// plus an additional 4 bytes for the checksum.
|
||||
decoded := base58.Decode(key)
|
||||
if len(decoded) != serializedKeyLen+4 {
|
||||
return nil, ErrInvalidKeyLen
|
||||
}
|
||||
|
||||
// The serialized format is:
|
||||
// version (4) || depth (1) || parent fingerprint (4)) ||
|
||||
// child num (4) || chain code (32) || key data (33) || checksum (4)
|
||||
|
||||
// Split the payload and checksum up and ensure the checksum matches.
|
||||
payload := decoded[:len(decoded)-4]
|
||||
checkSum := decoded[len(decoded)-4:]
|
||||
expectedCheckSum := chainhash.DoubleHashB(payload)[:4]
|
||||
if !bytes.Equal(checkSum, expectedCheckSum) {
|
||||
return nil, ErrBadChecksum
|
||||
}
|
||||
|
||||
// Deserialize each of the payload fields.
|
||||
version := payload[:4]
|
||||
depth := payload[4:5][0]
|
||||
fingerPrint := payload[5:9]
|
||||
childNumber := binary.BigEndian.Uint32(payload[9:13])
|
||||
chainCode := payload[13:45]
|
||||
keyData := payload[45:78]
|
||||
|
||||
// The key data is a private key if it starts with 0x00. Serialized
|
||||
// compressed pubkeys either start with 0x02 or 0x03.
|
||||
isPrivate := keyData[0] == 0x00
|
||||
if isPrivate {
|
||||
// Ensure the private key is valid. It must be within the range
|
||||
// of the order of the secp256k1 curve and not be 0.
|
||||
keyData = keyData[1:]
|
||||
keyNum := new(big.Int).SetBytes(keyData)
|
||||
if keyNum.Cmp(btcec.S256().N) >= 0 || keyNum.Sign() == 0 {
|
||||
return nil, ErrInvalidSeed
|
||||
}
|
||||
} else {
|
||||
// Ensure the public key parses correctly and is actually on the
|
||||
// secp256k1 curve.
|
||||
_, err := btcec.ParsePubKey(keyData, btcec.S256())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &ExtendedKey{
|
||||
Version: version,
|
||||
KeyData: keyData,
|
||||
ChainCode: chainCode,
|
||||
FingerPrint: fingerPrint,
|
||||
Depth: depth,
|
||||
ChildNumber: childNumber,
|
||||
IsPrivate: isPrivate,
|
||||
}, nil
|
||||
}
|
File diff suppressed because one or more lines are too long
1364
vendor/github.com/status-im/status-go/extkeys/mnemonic_vectors.json
generated
vendored
Normal file
1364
vendor/github.com/status-im/status-go/extkeys/mnemonic_vectors.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,52 @@
|
|||
package extkeys
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
)
|
||||
|
||||
// errors
|
||||
var (
|
||||
ErrInvalidSecretKey = errors.New("generated secret key cannot be used")
|
||||
)
|
||||
|
||||
func splitHMAC(seed, salt []byte) (secretKey, chainCode []byte, err error) {
|
||||
data := hmac.New(sha512.New, salt)
|
||||
if _, err = data.Write(seed); err != nil {
|
||||
return
|
||||
}
|
||||
I := data.Sum(nil)
|
||||
|
||||
// Split I into two 32-byte sequences, IL and IR.
|
||||
// IL = master secret key
|
||||
// IR = master chain code
|
||||
secretKey = I[:32]
|
||||
chainCode = I[32:]
|
||||
|
||||
// IL (secretKey) is expected to be a 256-bit integer (it is used as parse256(IL)),
|
||||
// and consequently that integer must be within range for SECP256k1 private key.
|
||||
//
|
||||
// There's tiny possibility (<1 in 2^127) this invariant is violated:
|
||||
// error is returned in that case, and simple resolution is to request another child with i incremented.
|
||||
keyBigInt := new(big.Int).SetBytes(secretKey)
|
||||
if keyBigInt.Cmp(btcec.S256().N) >= 0 || keyBigInt.Sign() == 0 {
|
||||
err = ErrInvalidSecretKey
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// paddedAppend appends the src byte slice to dst, returning the new slice.
|
||||
// If the length of the source is smaller than the passed size, leading zero
|
||||
// bytes are appended to the dst slice before appending src.
|
||||
// nolint: unparam
|
||||
func paddedAppend(size uint, dst, src []byte) []byte {
|
||||
for i := 0; i < int(size)-len(src); i++ {
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
return append(dst, src...)
|
||||
}
|
|
@ -35,6 +35,9 @@ github.com/davidlazar/go-crypto/salsa20
|
|||
github.com/deckarep/golang-set
|
||||
# github.com/edsrzf/mmap-go v1.0.0
|
||||
github.com/edsrzf/mmap-go
|
||||
# github.com/elastic/gosigar v0.14.1
|
||||
github.com/elastic/gosigar
|
||||
github.com/elastic/gosigar/sys/windows
|
||||
# github.com/ethereum/go-ethereum v1.10.4 => /home/cammellos/.go/src/github.com/status-im/go-ethereum
|
||||
github.com/ethereum/go-ethereum
|
||||
github.com/ethereum/go-ethereum/accounts
|
||||
|
@ -468,6 +471,8 @@ github.com/status-im/migrate/v4/source/go_bindata
|
|||
github.com/status-im/rendezvous
|
||||
github.com/status-im/rendezvous/protocol
|
||||
github.com/status-im/rendezvous/server
|
||||
# github.com/status-im/status-go/extkeys v1.1.2
|
||||
github.com/status-im/status-go/extkeys
|
||||
# github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501
|
||||
github.com/status-im/tcp-shaker
|
||||
# github.com/stretchr/testify v1.7.0
|
||||
|
|
Loading…
Reference in New Issue